001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.sort.IntHashMap;
005: import net.sf.saxon.expr.Expression;
006: import net.sf.saxon.instruct.Executable;
007: import net.sf.saxon.om.*;
008: import net.sf.saxon.trans.XPathException;
009:
010: import java.util.*;
011:
012: /**
013: * An xsl:character-map declaration in the stylesheet. <br>
014: */
015:
016: public class XSLCharacterMap extends StyleElement {
017:
018: //int fingerprint;
019: // the name of this character map
020:
021: String use;
022: // the value of the use-character-maps attribute, as supplied
023:
024: List characterMapElements = null;
025: // list of XSLCharacterMap objects referenced by this one
026:
027: boolean validated = false;
028: // set to true once validate() has been called
029:
030: boolean redundant = false;
031:
032: // set to true if another character-map overrrides this one
033:
034: /**
035: * Get the fingerprint of the name of this character map
036: * @return the fingerprint value
037: */
038:
039: public int getCharacterMapFingerprint() {
040: return getObjectNameCode() & 0xfffff;
041: }
042:
043: /**
044: * Test whether this character map is redundant (because another with the
045: * same name has higher import precedence). Note that a character map is not
046: * considered redundant simply because it is not referenced in an xsl:output
047: * declaration; we allow character-maps to be selected at run-time using the
048: * setOutputProperty() API.
049: */
050:
051: public boolean isRedundant() {
052: return redundant;
053: }
054:
055: /**
056: * Validate the attributes on this instruction
057: * @throws XPathException
058: */
059:
060: public void prepareAttributes() throws XPathException {
061:
062: String name = null;
063: use = null;
064:
065: AttributeCollection atts = getAttributeList();
066:
067: for (int a = 0; a < atts.getLength(); a++) {
068: int nc = atts.getNameCode(a);
069: String f = getNamePool().getClarkName(nc);
070: if (f == StandardNames.NAME) {
071: name = atts.getValue(a).trim();
072: } else if (f == StandardNames.USE_CHARACTER_MAPS) {
073: use = atts.getValue(a);
074: } else {
075: checkUnknownAttribute(nc);
076: }
077: }
078:
079: if (name == null) {
080: reportAbsence("name");
081: return;
082: }
083:
084: try {
085: setObjectNameCode(makeNameCode(name.trim()));
086: } catch (NamespaceException err) {
087: compileError(err.getMessage(), "XTSE0280");
088: } catch (XPathException err) {
089: compileError(err.getMessage());
090: }
091:
092: }
093:
094: public void validate() throws XPathException {
095:
096: if (validated)
097: return;
098:
099: // check that this is a top-level declaration
100:
101: checkTopLevel(null);
102:
103: // check that the only children are xsl:output-character elements
104:
105: AxisIterator kids = iterateAxis(Axis.CHILD);
106: while (true) {
107: Item child = kids.next();
108: if (child == null) {
109: break;
110: }
111: if (!(child instanceof XSLOutputCharacter)) {
112: compileError(
113: "Only xsl:output-character is allowed within xsl:character-map",
114: "XTSE0010");
115: }
116: }
117:
118: // check that there isn't another character-map with the same name and import
119: // precedence
120:
121: XSLStylesheet principal = getPrincipalStylesheet();
122: XSLCharacterMap other = principal
123: .getCharacterMap(getObjectFingerprint());
124: if (other != this ) {
125: if (this .getPrecedence() == other.getPrecedence()) {
126: compileError(
127: "There are two character-maps with the same name and import precedence",
128: "XTSE1580");
129: } else if (this .getPrecedence() < other.getPrecedence()) {
130: redundant = true;
131: }
132: }
133:
134: // validate the use-character-maps attribute
135:
136: if (use != null) {
137:
138: // identify any character maps that this one refers to
139:
140: characterMapElements = new ArrayList(5);
141: StringTokenizer st = new StringTokenizer(use);
142:
143: while (st.hasMoreTokens()) {
144: String displayname = st.nextToken();
145: try {
146: String[] parts = getConfiguration()
147: .getNameChecker()
148: .getQNameParts(displayname);
149: String uri = getURIForPrefix(parts[0], false);
150: if (uri == null) {
151: compileError("Undeclared namespace prefix "
152: + Err.wrap(parts[0])
153: + " in character map name", "XTSE0280");
154: }
155: int nameCode = getTargetNamePool().allocate(
156: parts[0], uri, parts[1]);
157: XSLCharacterMap ref = principal
158: .getCharacterMap(nameCode & 0xfffff);
159: if (ref == null) {
160: compileError("No character-map named '"
161: + displayname + "' has been defined",
162: "XTSE1590");
163: } else {
164: characterMapElements.add(ref);
165: }
166: } catch (QNameException err) {
167: compileError("Invalid character-map name. "
168: + err.getMessage(), "XTSE1590");
169: }
170: }
171:
172: // check for circularity
173:
174: for (Iterator it = characterMapElements.iterator(); it
175: .hasNext();) {
176: ((XSLCharacterMap) it.next()).checkCircularity(this );
177: }
178: }
179:
180: validated = true;
181: }
182:
183: /**
184: * Check for circularity: specifically, check that this attribute set does not contain
185: * a direct or indirect reference to the one supplied as a parameter
186: */
187:
188: private void checkCircularity(XSLCharacterMap origin)
189: throws XPathException {
190: if (this == origin) {
191: compileError(
192: "The definition of the character map is circular",
193: "XTSE1600");
194: characterMapElements = null; // for error recovery
195: } else {
196: if (!validated) {
197: // if this attribute set isn't validated yet, we don't check it.
198: // The circularity will be detected when the last attribute set in the cycle
199: // gets validated
200: return;
201: }
202: if (characterMapElements != null) {
203: for (Iterator it = characterMapElements.iterator(); it
204: .hasNext();) {
205: ((XSLCharacterMap) it.next())
206: .checkCircularity(origin);
207: }
208: }
209: }
210: }
211:
212: /**
213: * Assemble all the mappings defined by this character map, adding them to a
214: * HashMap that maps integer codepoints to strings
215: */
216:
217: public void assemble(IntHashMap map) {
218: if (characterMapElements != null) {
219: for (int i = 0; i < characterMapElements.size(); i++) {
220: XSLCharacterMap charmap = (XSLCharacterMap) characterMapElements
221: .get(i);
222: charmap.assemble(map);
223: }
224: }
225: AxisIterator kids = iterateAxis(Axis.CHILD);
226: while (true) {
227: Item child = kids.next();
228: if (child == null) {
229: return;
230: }
231: XSLOutputCharacter oc = (XSLOutputCharacter) child;
232: map.put(oc.getCodePoint(), oc.getReplacementString());
233: }
234: }
235:
236: public Expression compile(Executable exec) throws XPathException {
237: return null;
238: }
239:
240: }
241:
242: //
243: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
244: // you may not use this file except in compliance with the License. You may obtain a copy of the
245: // License at http://www.mozilla.org/MPL/
246: //
247: // Software distributed under the License is distributed on an "AS IS" basis,
248: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
249: // See the License for the specific language governing rights and limitations under the License.
250: //
251: // The Original Code is: all this file.
252: //
253: // The Initial Developer of the Original Code is Michael H. Kay
254: //
255: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
256: //
257: // Contributor(s): none.
258: //
|