package client.net.sf.saxon.ce.style; import client.net.sf.saxon.ce.expr.Expression; import client.net.sf.saxon.ce.expr.RoleLocator; import client.net.sf.saxon.ce.expr.StringLiteral; import client.net.sf.saxon.ce.expr.TypeChecker; import client.net.sf.saxon.ce.expr.instruct.Executable; import client.net.sf.saxon.ce.expr.instruct.NumberInstruction; import client.net.sf.saxon.ce.expr.instruct.ValueOf; import client.net.sf.saxon.ce.expr.number.NumberFormatter; import client.net.sf.saxon.ce.lib.Numberer; import client.net.sf.saxon.ce.om.AttributeCollection; import client.net.sf.saxon.ce.om.StandardNames; import client.net.sf.saxon.ce.pattern.NodeKindTest; import client.net.sf.saxon.ce.pattern.Pattern; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.value.SequenceType; import client.net.sf.saxon.ce.value.StringValue; import client.net.sf.saxon.ce.value.Whitespace; /** * 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; 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 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; for (int a=0; a<atts.getLength(); a++) { int nc = atts.getNameCode(a); String f = getNamePool().getClarkName(nc); 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 { checkUnknownAttribute(nc); } } 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) { if (!StringValue.isValidLanguageCode(language)) { 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); } } public void validate(Declaration decl) throws XPathException { checkEmpty(); select = typeCheck(select); value = typeCheck(value); format = typeCheck(format); groupSize = typeCheck(groupSize); groupSeparator = typeCheck(groupSeparator); letterValue = typeCheck(letterValue); ordinal = typeCheck(ordinal); lang = typeCheck(lang); from = typeCheck("from", from); count = typeCheck("count", count); 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, lang, formatter, numberer, hasVariablesInPatterns, xPath10ModeIsEnabled()); expr.setSourceLocator(this); ValueOf inst = new ValueOf(expr, false); inst.setSourceLocator(this); inst.setIsNumberingInstruction(); return inst; } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.