001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.expr.*;
005: import net.sf.saxon.instruct.Executable;
006: import net.sf.saxon.instruct.SlotManager;
007: import net.sf.saxon.om.AttributeCollection;
008: import net.sf.saxon.om.Axis;
009: import net.sf.saxon.om.NamespaceException;
010: import net.sf.saxon.om.NamespaceConstant;
011: import net.sf.saxon.pattern.Pattern;
012: import net.sf.saxon.trans.KeyDefinition;
013: import net.sf.saxon.trans.KeyManager;
014: import net.sf.saxon.trans.XPathException;
015: import net.sf.saxon.type.Type;
016: import net.sf.saxon.value.SequenceType;
017:
018: import javax.xml.transform.TransformerConfigurationException;
019: import java.text.Collator;
020: import java.util.Comparator;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023:
024: /**
025: * Handler for xsl:key elements in stylesheet. <br>
026: */
027:
028: public class XSLKey extends StyleElement implements StylesheetProcedure {
029:
030: private Pattern match;
031: private Expression use;
032: private String collationName;
033: SlotManager stackFrameMap;
034:
035: // needed if variables are used
036: /**
037: * Determine whether this type of element is allowed to contain a sequence constructor
038: * @return true: yes, it may contain a sequence constructor
039: */
040:
041: public boolean mayContainSequenceConstructor() {
042: return true;
043: }
044:
045: /**
046: * Get the Procedure object that looks after any local variables declared in the content constructor
047: */
048:
049: public SlotManager getSlotManager() {
050: return stackFrameMap;
051: }
052:
053: public void prepareAttributes() throws XPathException {
054:
055: String nameAtt = null;
056: String matchAtt = null;
057: String useAtt = null;
058:
059: AttributeCollection atts = getAttributeList();
060:
061: for (int a = 0; a < atts.getLength(); a++) {
062: int nc = atts.getNameCode(a);
063: String f = getNamePool().getClarkName(nc);
064: if (f == StandardNames.NAME) {
065: nameAtt = atts.getValue(a).trim();
066: } else if (f == StandardNames.USE) {
067: useAtt = atts.getValue(a);
068: } else if (f == StandardNames.MATCH) {
069: matchAtt = atts.getValue(a);
070: } else if (f == StandardNames.COLLATION) {
071: collationName = atts.getValue(a).trim();
072: } else {
073: checkUnknownAttribute(nc);
074: }
075: }
076:
077: if (nameAtt == null) {
078: reportAbsence("name");
079: return;
080: }
081: try {
082: setObjectNameCode(makeNameCode(nameAtt.trim()));
083: } catch (NamespaceException err) {
084: compileError(err.getMessage(), "XTSE0280");
085: } catch (XPathException err) {
086: compileError(err);
087: }
088:
089: if (matchAtt == null) {
090: reportAbsence("match");
091: matchAtt = "*";
092: }
093: match = makePattern(matchAtt);
094:
095: if (useAtt != null) {
096: use = makeExpression(useAtt);
097: }
098: }
099:
100: public void validate() throws XPathException {
101:
102: stackFrameMap = getConfiguration().makeSlotManager();
103: checkTopLevel(null);
104: if (use != null) {
105: // the value can be supplied as a content constructor in place of a use expression
106: if (hasChildNodes()) {
107: compileError(
108: "An xsl:key element with a @use attribute must be empty",
109: "XTSE1205");
110: }
111: try {
112: RoleLocator role = new RoleLocator(
113: RoleLocator.INSTRUCTION, "xsl:key/use", 0, null);
114: role.setSourceLocator(new ExpressionLocation(this ));
115: use = TypeChecker.staticTypeCheck(use, SequenceType
116: .makeSequenceType(Type.ANY_ATOMIC_TYPE,
117: StaticProperty.ALLOWS_ZERO_OR_MORE),
118: false, role, getStaticContext());
119: } catch (XPathException err) {
120: compileError(err);
121: }
122: } else {
123: if (!hasChildNodes()) {
124: compileError(
125: "An xsl:key element must either have a @use attribute or have content",
126: "XTSE1205");
127: }
128: }
129: use = typeCheck("use", use);
130: match = typeCheck("match", match);
131:
132: // Do a further check that the use expression makes sense in the context of the match pattern
133: if (use != null) {
134: use = use
135: .typeCheck(getStaticContext(), match.getNodeTest());
136: }
137:
138: if (collationName != null) {
139: URI collationURI;
140: try {
141: collationURI = new URI(collationName);
142: if (!collationURI.isAbsolute()) {
143: URI base = new URI(getBaseURI());
144: collationURI = base.resolve(collationURI);
145: collationName = collationURI.toString();
146: }
147: } catch (URISyntaxException err) {
148: compileError("Collation name '" + collationName
149: + "' is not a valid URI");
150: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
151: }
152: }
153: }
154:
155: public Expression compile(Executable exec) throws XPathException {
156:
157: Collator collator = null;
158: if (collationName != null) {
159: Comparator comp = getPrincipalStylesheet().findCollation(
160: collationName);
161: if (comp == null) {
162: compileError("The collation name "
163: + Err.wrap(collationName, Err.URI)
164: + " is not recognized", "XTSE1210");
165: }
166: if (!(comp instanceof Collator)) {
167: compileError(
168: "The collation used for xsl:key must be a java.text.Collator",
169: "XTSE1210");
170: }
171: collator = (Collator) comp;
172: }
173:
174: if (use == null) {
175: Expression body = compileSequenceConstructor(exec,
176: iterateAxis(Axis.CHILD), true);
177: try {
178: use = new Atomizer(body.simplify(getStaticContext()),
179: getStaticContext().getConfiguration());
180: } catch (XPathException e) {
181: compileError(e);
182: }
183:
184: try {
185: RoleLocator role = new RoleLocator(
186: RoleLocator.INSTRUCTION, "xsl:key/use", 0, null);
187: role.setSourceLocator(new ExpressionLocation(this ));
188: use = TypeChecker.staticTypeCheck(use, SequenceType
189: .makeSequenceType(Type.ANY_ATOMIC_TYPE,
190: StaticProperty.ALLOWS_ZERO_OR_MORE),
191: false, role, getStaticContext());
192: // Do a further check that the use expression makes sense in the context of the match pattern
193: use = use.typeCheck(getStaticContext(), match
194: .getNodeTest());
195:
196: } catch (XPathException err) {
197: compileError(err);
198: }
199: }
200:
201: allocateSlots(use);
202:
203: KeyManager km = getPrincipalStylesheet().getKeyManager();
204: KeyDefinition keydef = new KeyDefinition(match, use,
205: collationName, collator);
206: keydef.setStackFrameMap(stackFrameMap);
207: keydef.setLocation(getSystemId(), getLineNumber());
208: keydef.setExecutable(getExecutable());
209: keydef
210: .setBackwardsCompatible(backwardsCompatibleModeIsEnabled());
211: try {
212: km.addKeyDefinition(getObjectFingerprint(), keydef, exec
213: .getConfiguration().getNamePool());
214: } catch (TransformerConfigurationException err) {
215: compileError(err);
216: }
217: return null;
218: }
219:
220: }
221: //
222: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
223: // you may not use this file except in compliance with the License. You may obtain a copy of the
224: // License at http://www.mozilla.org/MPL/
225: //
226: // Software distributed under the License is distributed on an "AS IS" basis,
227: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
228: // See the License for the specific language governing rights and limitations under the License.
229: //
230: // The Original Code is: all this file.
231: //
232: // The Initial Developer of the Original Code is Michael H. Kay.
233: //
234: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
235: //
236: // Contributor(s): none.
237: //
|