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