001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: StylesheetPIHandler.java,v 1.3 2005/01/23 01:02:10 mcnamara Exp $
018: */
019: package org.apache.xml.utils;
020:
021: import java.util.StringTokenizer;
022: import java.util.Vector;
023:
024: import javax.xml.transform.Source;
025: import javax.xml.transform.TransformerException;
026: import javax.xml.transform.URIResolver;
027: import javax.xml.transform.sax.SAXSource;
028:
029: import org.apache.xml.utils.SystemIDResolver;
030:
031: import org.xml.sax.Attributes;
032: import org.xml.sax.InputSource;
033: import org.xml.sax.helpers.DefaultHandler;
034:
035: /**
036: * Search for the xml-stylesheet processing instructions in an XML document.
037: * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
038: */
039: public class StylesheetPIHandler extends DefaultHandler {
040: /** The baseID of the document being processed. */
041: String m_baseID;
042:
043: /** The desired media criteria. */
044: String m_media;
045:
046: /** The desired title criteria. */
047: String m_title;
048:
049: /** The desired character set criteria. */
050: String m_charset;
051:
052: /** A list of SAXSource objects that match the criteria. */
053: Vector m_stylesheets = new Vector();
054:
055: // Add code to use a URIResolver. Patch from Dmitri Ilyin.
056:
057: /**
058: * The object that implements the URIResolver interface,
059: * or null.
060: */
061: URIResolver m_uriResolver;
062:
063: /**
064: * Get the object that will be used to resolve URIs in href
065: * in xml-stylesheet processing instruction.
066: *
067: * @param resolver An object that implements the URIResolver interface,
068: * or null.
069: */
070: public void setURIResolver(URIResolver resolver) {
071: m_uriResolver = resolver;
072: }
073:
074: /**
075: * Get the object that will be used to resolve URIs in href
076: * in xml-stylesheet processing instruction.
077: *
078: * @return The URIResolver that was set with setURIResolver.
079: */
080: public URIResolver getURIResolver() {
081: return m_uriResolver;
082: }
083:
084: /**
085: * Construct a StylesheetPIHandler instance that will search
086: * for xml-stylesheet PIs based on the given criteria.
087: *
088: * @param baseID The base ID of the XML document, needed to resolve
089: * relative IDs.
090: * @param media The desired media criteria.
091: * @param title The desired title criteria.
092: * @param charset The desired character set criteria.
093: */
094: public StylesheetPIHandler(String baseID, String media,
095: String title, String charset) {
096:
097: m_baseID = baseID;
098: m_media = media;
099: m_title = title;
100: m_charset = charset;
101: }
102:
103: /**
104: * Return the last stylesheet found that match the constraints.
105: *
106: * @return Source object that references the last stylesheet reference
107: * that matches the constraints.
108: */
109: public Source getAssociatedStylesheet() {
110:
111: int sz = m_stylesheets.size();
112:
113: if (sz > 0) {
114: Source source = (Source) m_stylesheets.elementAt(sz - 1);
115: return source;
116: } else
117: return null;
118: }
119:
120: /**
121: * Handle the xml-stylesheet processing instruction.
122: *
123: * @param target The processing instruction target.
124: * @param data The processing instruction data, or null if
125: * none is supplied.
126: * @throws org.xml.sax.SAXException Any SAX exception, possibly
127: * wrapping another exception.
128: * @see org.xml.sax.ContentHandler#processingInstruction
129: * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
130: */
131: public void processingInstruction(String target, String data)
132: throws org.xml.sax.SAXException {
133:
134: if (target.equals("xml-stylesheet")) {
135: String href = null; // CDATA #REQUIRED
136: String type = null; // CDATA #REQUIRED
137: String title = null; // CDATA #IMPLIED
138: String media = null; // CDATA #IMPLIED
139: String charset = null; // CDATA #IMPLIED
140: boolean alternate = false; // (yes|no) "no"
141: StringTokenizer tokenizer = new StringTokenizer(data,
142: " \t=\n", true);
143: boolean lookedAhead = false;
144: Source source = null;
145:
146: String token = "";
147: while (tokenizer.hasMoreTokens()) {
148: if (!lookedAhead)
149: token = tokenizer.nextToken();
150: else
151: lookedAhead = false;
152: if (tokenizer.hasMoreTokens()
153: && (token.equals(" ") || token.equals("\t") || token
154: .equals("=")))
155: continue;
156:
157: String name = token;
158: if (name.equals("type")) {
159: token = tokenizer.nextToken();
160: while (tokenizer.hasMoreTokens()
161: && (token.equals(" ") || token.equals("\t") || token
162: .equals("=")))
163: token = tokenizer.nextToken();
164: type = token.substring(1, token.length() - 1);
165:
166: } else if (name.equals("href")) {
167: token = tokenizer.nextToken();
168: while (tokenizer.hasMoreTokens()
169: && (token.equals(" ") || token.equals("\t") || token
170: .equals("=")))
171: token = tokenizer.nextToken();
172: href = token;
173: if (tokenizer.hasMoreTokens()) {
174: token = tokenizer.nextToken();
175: // If the href value has parameters to be passed to a
176: // servlet(something like "foobar?id=12..."),
177: // we want to make sure we get them added to
178: // the href value. Without this check, we would move on
179: // to try to process another attribute and that would be
180: // wrong.
181: // We need to set lookedAhead here to flag that we
182: // already have the next token.
183: while (token.equals("=")
184: && tokenizer.hasMoreTokens()) {
185: href = href + token + tokenizer.nextToken();
186: if (tokenizer.hasMoreTokens()) {
187: token = tokenizer.nextToken();
188: lookedAhead = true;
189: } else {
190: break;
191: }
192: }
193: }
194: href = href.substring(1, href.length() - 1);
195: try {
196: // Add code to use a URIResolver. Patch from Dmitri Ilyin.
197: if (m_uriResolver != null) {
198: source = m_uriResolver.resolve(href,
199: m_baseID);
200: } else {
201: href = SystemIDResolver.getAbsoluteURI(
202: href, m_baseID);
203: source = new SAXSource(
204: new InputSource(href));
205: }
206: } catch (TransformerException te) {
207: throw new org.xml.sax.SAXException(te);
208: }
209: } else if (name.equals("title")) {
210: token = tokenizer.nextToken();
211: while (tokenizer.hasMoreTokens()
212: && (token.equals(" ") || token.equals("\t") || token
213: .equals("=")))
214: token = tokenizer.nextToken();
215: title = token.substring(1, token.length() - 1);
216: } else if (name.equals("media")) {
217: token = tokenizer.nextToken();
218: while (tokenizer.hasMoreTokens()
219: && (token.equals(" ") || token.equals("\t") || token
220: .equals("=")))
221: token = tokenizer.nextToken();
222: media = token.substring(1, token.length() - 1);
223: } else if (name.equals("charset")) {
224: token = tokenizer.nextToken();
225: while (tokenizer.hasMoreTokens()
226: && (token.equals(" ") || token.equals("\t") || token
227: .equals("=")))
228: token = tokenizer.nextToken();
229: charset = token.substring(1, token.length() - 1);
230: } else if (name.equals("alternate")) {
231: token = tokenizer.nextToken();
232: while (tokenizer.hasMoreTokens()
233: && (token.equals(" ") || token.equals("\t") || token
234: .equals("=")))
235: token = tokenizer.nextToken();
236: alternate = token.substring(1, token.length() - 1)
237: .equals("yes");
238: }
239:
240: }
241:
242: if ((null != type)
243: && (type.equals("text/xsl")
244: || type.equals("text/xml") || type
245: .equals("application/xml+xslt"))
246: && (null != href)) {
247: if (null != m_media) {
248: if (null != media) {
249: if (!media.equals(m_media))
250: return;
251: } else
252: return;
253: }
254:
255: if (null != m_charset) {
256: if (null != charset) {
257: if (!charset.equals(m_charset))
258: return;
259: } else
260: return;
261: }
262:
263: if (null != m_title) {
264: if (null != title) {
265: if (!title.equals(m_title))
266: return;
267: } else
268: return;
269: }
270:
271: m_stylesheets.addElement(source);
272: }
273: }
274: }
275:
276: /**
277: * The spec notes that "The xml-stylesheet processing instruction is allowed only in the prolog of an XML document.",
278: * so, at least for right now, I'm going to go ahead an throw a TransformerException
279: * in order to stop the parse.
280: *
281: * @param namespaceURI The Namespace URI, or an empty string.
282: * @param localName The local name (without prefix), or empty string if not namespace processing.
283: * @param qName The qualified name (with prefix).
284: * @param atts The specified or defaulted attributes.
285: *
286: * @throws StopParseException since there can be no valid xml-stylesheet processing
287: * instructions past the first element.
288: */
289: public void startElement(String namespaceURI, String localName,
290: String qName, Attributes atts)
291: throws org.xml.sax.SAXException {
292: throw new StopParseException();
293: }
294:
295: /**
296: * Added additional getter and setter methods for the Base Id
297: * to fix bugzilla bug 24187
298: *
299: */
300: public void setBaseId(String baseId) {
301: m_baseID = baseId;
302:
303: }
304:
305: public String getBaseId() {
306: return m_baseID;
307: }
308:
309: }
|