package client.net.sf.saxon.ce.style; import client.net.sf.saxon.ce.Configuration; import client.net.sf.saxon.ce.trans.Err; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.expr.instruct.Executable; import client.net.sf.saxon.ce.expr.instruct.SlotManager; import client.net.sf.saxon.ce.om.*; import client.net.sf.saxon.ce.pattern.Pattern; import client.net.sf.saxon.ce.expr.sort.CodepointCollator; import client.net.sf.saxon.ce.lib.StringCollator; import client.net.sf.saxon.ce.trans.KeyDefinition; import client.net.sf.saxon.ce.trans.KeyManager; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.URI; import client.net.sf.saxon.ce.type.BuiltInAtomicType; import client.net.sf.saxon.ce.type.TypeHierarchy; import client.net.sf.saxon.ce.value.SequenceType; import client.net.sf.saxon.ce.value.Whitespace; /** * 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; // 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++) { int nc = atts.getNameCode(a); String f = getNamePool().getClarkName(nc); if (f.equals(StandardNames.NAME)) { nameAtt = Whitespace.trim(atts.getValue(a)) ; } else if (f.equals(StandardNames.USE)) { useAtt = atts.getValue(a); } else if (f.equals(StandardNames.MATCH)) { matchAtt = atts.getValue(a); } else if (f.equals(StandardNames.COLLATION)) { collationName = Whitespace.trim(atts.getValue(a)) ; } else { checkUnknownAttribute(nc); } } 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 (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 = new SlotManager(); checkTopLevel(null); 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); 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, match.getNodeTest()); } if (collationName != null) { URI collationURI; try { collationURI = new URI(collationName, true); if (!collationURI.isAbsolute()) { URI base = new URI(getBaseURI()); collationURI = base.resolve(collationURI.toString()); collationName = collationURI.toString(); } } catch (URI.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().getExecutable().getKeyManager().preRegisterKeyDefinition(keyName); } } public Expression compile(Executable exec, Declaration decl) throws XPathException { StaticContext env = getStaticContext(); Configuration config = env.getConfiguration(); StringCollator collator = null; if (collationName != null) { collator = getConfiguration().getNamedCollation(collationName); 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 { compileError("The collation used for xsl:key must be capable of generating collation keys", "XTSE1210"); } } if (use==null) { Expression body = compileSequenceConstructor(exec, decl, iterateAxis(Axis.CHILD)); try { ExpressionVisitor visitor = makeExpressionVisitor(); use = new Atomizer(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 use = makeExpressionVisitor().typeCheck(use, match.getNodeTest()); } catch (XPathException err) { compileError(err); } } final TypeHierarchy th = config.getTypeHierarchy(); BuiltInAtomicType useType = (BuiltInAtomicType)use.getItemType(th).getPrimitiveItemType(); if (xPath10ModeIsEnabled()) { if (!useType.equals(BuiltInAtomicType.STRING) && !useType.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) { use = new AtomicSequenceConverter(use, BuiltInAtomicType.STRING); useType = BuiltInAtomicType.STRING; } } allocateSlots(use); int slots = match.allocateSlots(getStaticContext(), stackFrameMap, 0); allocatePatternSlots(slots); //allocateSlots(new PatternSponsor(match)); KeyManager km = getExecutable().getKeyManager(); KeyDefinition keydef = new KeyDefinition(match, use, collationName, collator); keydef.setIndexedItemType(useType); keydef.setStackFrameMap(stackFrameMap); keydef.setSourceLocator(this); keydef.setExecutable(getExecutable()); keydef.setBackwardsCompatible(xPath10ModeIsEnabled()); try { km.addKeyDefinition(keyName, keydef, exec.getConfiguration()); } catch (XPathException err) { compileError(err); } return null; } /** * Optimize the stylesheet construct * @param declaration */ public void optimize(Declaration declaration) throws XPathException { // already done earlier } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.