001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.Err;
005: import net.sf.saxon.expr.Expression;
006: import net.sf.saxon.expr.ExpressionTool;
007: import net.sf.saxon.instruct.Attribute;
008: import net.sf.saxon.instruct.Executable;
009: import net.sf.saxon.instruct.FixedAttribute;
010: import net.sf.saxon.om.*;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.type.SchemaType;
013: import net.sf.saxon.type.SimpleType;
014: import net.sf.saxon.value.StringValue;
015:
016: /**
017: * xsl:attribute element in stylesheet. <br>
018: */
019:
020: public final class XSLAttribute extends XSLStringConstructor {
021:
022: private Expression attributeName;
023: private Expression separator;
024: private Expression namespace = null;
025: private int validationAction = Validation.PRESERVE;
026: private SimpleType schemaType;
027:
028: public void prepareAttributes() throws XPathException {
029:
030: AttributeCollection atts = getAttributeList();
031:
032: String nameAtt = null;
033: String namespaceAtt = null;
034: String selectAtt = null;
035: String separatorAtt = null;
036: String validationAtt = null;
037: String typeAtt = null;
038:
039: for (int a = 0; a < atts.getLength(); a++) {
040: int nc = atts.getNameCode(a);
041: String f = getNamePool().getClarkName(nc);
042: if (f == StandardNames.NAME) {
043: nameAtt = atts.getValue(a).trim();
044: } else if (f == StandardNames.NAMESPACE) {
045: namespaceAtt = atts.getValue(a).trim();
046: } else if (f == StandardNames.SELECT) {
047: selectAtt = atts.getValue(a);
048: } else if (f == StandardNames.SEPARATOR) {
049: separatorAtt = atts.getValue(a);
050: } else if (f == StandardNames.VALIDATION) {
051: validationAtt = atts.getValue(a).trim();
052: } else if (f == StandardNames.TYPE) {
053: typeAtt = atts.getValue(a).trim();
054: } else {
055: checkUnknownAttribute(nc);
056: }
057: }
058:
059: if (nameAtt == null) {
060: reportAbsence("name");
061: return;
062: }
063: attributeName = makeAttributeValueTemplate(nameAtt);
064: if (attributeName instanceof StringValue) {
065: if (!getConfiguration().getNameChecker().isQName(
066: ((StringValue) attributeName).getStringValue())) {
067: invalidAttributeName("Attribute name "
068: + Err.wrap(nameAtt) + " is not a valid QName");
069: }
070: if (nameAtt.equals("xmlns")) {
071: if (namespace == null) {
072: invalidAttributeName("Invalid attribute name: xmlns");
073: }
074: }
075: if (nameAtt.startsWith("xmlns:")) {
076: if (namespaceAtt == null) {
077: invalidAttributeName("Invalid attribute name: "
078: + Err.wrap(nameAtt));
079: } else {
080: // ignore the prefix "xmlns"
081: nameAtt = nameAtt.substring(6);
082: attributeName = new StringValue(nameAtt);
083: }
084: }
085: }
086:
087: if (namespaceAtt != null) {
088: namespace = makeAttributeValueTemplate(namespaceAtt);
089: }
090:
091: if (selectAtt != null) {
092: select = makeExpression(selectAtt);
093: }
094:
095: if (separatorAtt == null) {
096: if (selectAtt == null) {
097: separator = StringValue.EMPTY_STRING;
098: } else {
099: separator = StringValue.SINGLE_SPACE;
100: }
101: } else {
102: separator = makeAttributeValueTemplate(separatorAtt);
103: }
104:
105: if (validationAtt != null) {
106: validationAction = Validation.getCode(validationAtt);
107: if (validationAction != Validation.STRIP
108: && !getConfiguration().isSchemaAware(
109: Configuration.XSLT)) {
110: compileError(
111: "To perform validation, a schema-aware XSLT processor is needed",
112: "XTSE1660");
113: validationAction = getContainingStylesheet()
114: .getDefaultValidation();
115: }
116: if (validationAction == Validation.INVALID) {
117: compileError("Invalid value of validation attribute",
118: "XTSE0020");
119: validationAction = getContainingStylesheet()
120: .getDefaultValidation();
121: }
122: } else {
123: validationAction = getContainingStylesheet()
124: .getDefaultValidation();
125: }
126:
127: if (typeAtt != null) {
128: if (!getConfiguration().isSchemaAware(Configuration.XSLT)) {
129: compileError(
130: "The @type attribute is available only with a schema-aware XSLT processor",
131: "XTSE1660");
132: } else {
133: SchemaType type = getSchemaType(typeAtt);
134: if (type == null) {
135: compileError("Unknown attribute type " + typeAtt,
136: "XTSE1520");
137: } else {
138: if (type.isSimpleType()) {
139: schemaType = (SimpleType) type;
140: } else {
141: compileError(
142: "Type annotation for attributes must be a simple type",
143: "XTSE1530");
144: type = null;
145: }
146: }
147: }
148: }
149:
150: if (typeAtt != null && validationAtt != null) {
151: compileError(
152: "The validation and type attributes are mutually exclusive",
153: "XTSE1505");
154: validationAction = getContainingStylesheet()
155: .getDefaultValidation();
156: schemaType = null;
157: }
158: }
159:
160: private void invalidAttributeName(String message)
161: throws XPathException {
162: // if (forwardsCompatibleModeIsEnabled()) {
163: // DynamicError err = new DynamicError(message);
164: // err.setErrorCode("XTDE0850");
165: // err.setLocator(this);
166: // attributeName = new ErrorExpression(err);
167: // } else {
168: compileError(message, "XTDE0850");
169: // prevent a duplicate error message...
170: attributeName = new StringValue("saxon-error-attribute");
171: // }
172: }
173:
174: public void validate() throws XPathException {
175: if (!(getParent() instanceof XSLAttributeSet)) {
176: checkWithinTemplate();
177: }
178: if (schemaType != null) {
179: if (schemaType.isNamespaceSensitive()) {
180: compileError(
181: "Validation at attribute level must not specify a "
182: + "namespace-sensitive type (xs:QName or xs:NOTATION)",
183: "XTSE1545");
184: }
185: }
186: attributeName = typeCheck("name", attributeName);
187: namespace = typeCheck("namespace", namespace);
188: select = typeCheck("select", select);
189: separator = typeCheck("separator", separator);
190: super .validate();
191: }
192:
193: public Expression compile(Executable exec) throws XPathException {
194: NamespaceResolver nsContext = null;
195:
196: int annotation = getTypeAnnotation(schemaType);
197:
198: // deal specially with the case where the attribute name is known statically
199:
200: if (attributeName instanceof StringValue) {
201: String qName = ((StringValue) attributeName)
202: .getStringValue().trim();
203: String[] parts;
204: try {
205: parts = getConfiguration().getNameChecker()
206: .getQNameParts(qName);
207: } catch (QNameException e) {
208: // This can't happen, because of previous checks
209: return null;
210: }
211:
212: if (namespace == null) {
213: String nsuri = "";
214: if (!parts[0].equals("")) {
215: nsuri = getURIForPrefix(parts[0], false);
216: if (nsuri == null) {
217: undeclaredNamespaceError(parts[0], "XTSE0280");
218: return null;
219: }
220: }
221: int nameCode = getTargetNamePool().allocate(parts[0],
222: nsuri, parts[1]);
223: FixedAttribute inst = new FixedAttribute(nameCode,
224: validationAction, schemaType, annotation);
225: inst.setParentExpression(this ); // temporarily
226: compileContent(exec, inst, separator);
227: //inst.setSeparator(separator);
228: ExpressionTool.makeParentReferences(inst);
229: return inst;
230: } else if (namespace instanceof StringValue) {
231: String nsuri = ((StringValue) namespace)
232: .getStringValue();
233: if (nsuri.equals("")) {
234: parts[0] = "";
235: } else if (parts[0].equals("")) {
236: // Need to choose an arbitrary prefix
237: // First see if the requested namespace is declared in the stylesheet
238: AxisIterator iter = iterateAxis(Axis.NAMESPACE);
239: while (true) {
240: NodeInfo ns = (NodeInfo) iter.next();
241: if (ns == null) {
242: break;
243: }
244: if (ns.getStringValue().equals(nsuri)) {
245: parts[0] = ns.getLocalPart();
246: break;
247: }
248: }
249: // Otherwise see the URI is known to the namepool
250: if (parts[0].equals("")) {
251: String p = getTargetNamePool()
252: .suggestPrefixForURI(
253: ((StringValue) namespace)
254: .getStringValue());
255: if (p != null) {
256: parts[0] = p;
257: }
258: }
259: // Otherwise choose something arbitrary. This will get changed
260: // if it clashes with another attribute
261: if (parts[0].equals("")) {
262: parts[0] = "ns0";
263: }
264: }
265: int nameCode = getTargetNamePool().allocate(parts[0],
266: nsuri, parts[1]);
267: FixedAttribute inst = new FixedAttribute(nameCode,
268: validationAction, schemaType, annotation);
269: compileContent(exec, inst, separator);
270: //inst.setSeparator(separator);
271: ExpressionTool.makeParentReferences(inst);
272: return inst;
273: }
274: } else {
275: // if the namespace URI must be deduced at run-time from the attribute name
276: // prefix, we need to save the namespace context of the instruction
277:
278: if (namespace == null) {
279: nsContext = makeNamespaceContext();
280: }
281: }
282:
283: Attribute inst = new Attribute(attributeName, namespace,
284: nsContext, validationAction, schemaType, annotation,
285: false);
286: compileContent(exec, inst, separator);
287: return inst;
288: }
289:
290: }
291:
292: //
293: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
294: // you may not use this file except in compliance with the License. You may obtain a copy of the
295: // License at http://www.mozilla.org/MPL/
296: //
297: // Software distributed under the License is distributed on an "AS IS" basis,
298: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
299: // See the License for the specific language governing rights and limitations under the License.
300: //
301: // The Original Code is: all this file.
302: //
303: // The Initial Developer of the Original Code is Michael H. Kay.
304: //
305: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
306: //
307: // Contributor(s): none.
308: //
|