[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">&#xa;</xsl:if>
+//	    </xsl:if>
+//    </xsl:for-each-group>
+//    <xsl:text>};&#xa;&#xa;</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">&#xa;</xsl:if>
+//	    </xsl:if>
+//    </xsl:for-each-group>
+//    <xsl:text>};&#xa;&#xa;</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"), ");&#xa;")
+     *          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">,&#xa;</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("&#xA;");
+                    } else if (c=='\t') {
+                        writer.write("&#x9;");
+                    } else if (c=='\r') {
+                        writer.write("&#xD;");
+                    }
+                } 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("&#xD;");
+                    }
+                }
+
+            } 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("&#xA;");
+                } else if (c=='\r') {
+                    writer.write("&#xD;");
+                } else if (c=='\t') {
+                    writer.write("&#x9;");
+                } 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("&#xA;");
+            } else if (c=='\r') {
+                sb.append("&#xD;");
+            } else if (c=='\t') {
+                sb.append("&#x9;");
+            } 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 = "&#xA;";
+                } else if (c=='\r') {
+                    e = "&#xD;";
+                } else if (c=='\t') {
+                    e = "&#x9;";
+                }
+                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