1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.processor;
  58. import org.xml.sax.helpers.DefaultHandler;
  59. import javax.xml.transform.TransformerException;
  60. import org.xml.sax.InputSource;
  61. import org.xml.sax.Attributes;
  62. import java.util.Vector;
  63. import java.util.StringTokenizer;
  64. import javax.xml.transform.Source;
  65. import javax.xml.transform.sax.SAXSource;
  66. import javax.xml.transform.URIResolver;
  67. import org.apache.xml.utils.SystemIDResolver;
  68. /**
  69. * Search for the xml-stylesheet processing instructions in an XML document.
  70. * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
  71. */
  72. public class StylesheetPIHandler extends DefaultHandler
  73. {
  74. /** The baseID of the document being processed. */
  75. String m_baseID;
  76. /** The desired media criteria. */
  77. String m_media;
  78. /** The desired title criteria. */
  79. String m_title;
  80. /** The desired character set criteria. */
  81. String m_charset;
  82. /** A list of SAXSource objects that match the criteria. */
  83. Vector m_stylesheets = new Vector();
  84. // Add code to use a URIResolver. Patch from Dmitri Ilyin.
  85. /**
  86. * The object that implements the URIResolver interface,
  87. * or null.
  88. */
  89. URIResolver m_uriResolver;
  90. /**
  91. * Get the object that will be used to resolve URIs in href
  92. * in xml-stylesheet processing instruction.
  93. *
  94. * @param resolver An object that implements the URIResolver interface,
  95. * or null.
  96. */
  97. public void setURIResolver(URIResolver resolver)
  98. {
  99. m_uriResolver = resolver;
  100. }
  101. /**
  102. * Get the object that will be used to resolve URIs in href
  103. * in xml-stylesheet processing instruction.
  104. *
  105. * @return The URIResolver that was set with setURIResolver.
  106. */
  107. public URIResolver getURIResolver()
  108. {
  109. return m_uriResolver;
  110. }
  111. /**
  112. * Construct a StylesheetPIHandler instance that will search
  113. * for xml-stylesheet PIs based on the given criteria.
  114. *
  115. * @param baseID The base ID of the XML document, needed to resolve
  116. * relative IDs.
  117. * @param media The desired media criteria.
  118. * @param title The desired title criteria.
  119. * @param charset The desired character set criteria.
  120. */
  121. public StylesheetPIHandler(String baseID, String media, String title,
  122. String charset)
  123. {
  124. m_baseID = baseID;
  125. m_media = media;
  126. m_title = title;
  127. m_charset = charset;
  128. }
  129. /**
  130. * Return the last stylesheet found that match the constraints.
  131. *
  132. * @return Source object that references the last stylesheet reference
  133. * that matches the constraints.
  134. */
  135. public Source getAssociatedStylesheet()
  136. {
  137. int sz = m_stylesheets.size();
  138. if (sz > 0)
  139. {
  140. Source source = (Source) m_stylesheets.elementAt(sz-1);
  141. return source;
  142. }
  143. else
  144. return null;
  145. }
  146. /**
  147. * Handle the xml-stylesheet processing instruction.
  148. *
  149. * @param target The processing instruction target.
  150. * @param data The processing instruction data, or null if
  151. * none is supplied.
  152. * @throws org.xml.sax.SAXException Any SAX exception, possibly
  153. * wrapping another exception.
  154. * @see org.xml.sax.ContentHandler#processingInstruction
  155. * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
  156. */
  157. public void processingInstruction(String target, String data)
  158. throws org.xml.sax.SAXException
  159. {
  160. if (target.equals("xml-stylesheet"))
  161. {
  162. String href = null; // CDATA #REQUIRED
  163. String type = null; // CDATA #REQUIRED
  164. String title = null; // CDATA #IMPLIED
  165. String media = null; // CDATA #IMPLIED
  166. String charset = null; // CDATA #IMPLIED
  167. boolean alternate = false; // (yes|no) "no"
  168. StringTokenizer tokenizer = new StringTokenizer(data, " \t=\n", true);
  169. boolean lookedAhead = false;
  170. Source source = null;
  171. String token = "";
  172. while (tokenizer.hasMoreTokens())
  173. {
  174. if (!lookedAhead)
  175. token = tokenizer.nextToken();
  176. else
  177. lookedAhead = false;
  178. if (tokenizer.hasMoreTokens() &&
  179. (token.equals(" ") || token.equals("\t") || token.equals("=")))
  180. continue;
  181. String name = token;
  182. if (name.equals("type"))
  183. {
  184. token = tokenizer.nextToken();
  185. while (tokenizer.hasMoreTokens() &&
  186. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  187. token = tokenizer.nextToken();
  188. type = token.substring(1, token.length() - 1);
  189. }
  190. else if (name.equals("href"))
  191. {
  192. token = tokenizer.nextToken();
  193. while (tokenizer.hasMoreTokens() &&
  194. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  195. token = tokenizer.nextToken();
  196. href = token;
  197. if (tokenizer.hasMoreTokens())
  198. {
  199. token = tokenizer.nextToken();
  200. // If the href value has parameters to be passed to a
  201. // servlet(something like "foobar?id=12..."),
  202. // we want to make sure we get them added to
  203. // the href value. Without this check, we would move on
  204. // to try to process another attribute and that would be
  205. // wrong.
  206. // We need to set lookedAhead here to flag that we
  207. // already have the next token.
  208. while ( token.equals("=") && tokenizer.hasMoreTokens())
  209. {
  210. href = href + token + tokenizer.nextToken();
  211. if (tokenizer.hasMoreTokens())
  212. {
  213. token = tokenizer.nextToken();
  214. lookedAhead = true;
  215. }
  216. else
  217. {
  218. break;
  219. }
  220. }
  221. }
  222. href = href.substring(1, href.length() - 1);
  223. try
  224. {
  225. // Add code to use a URIResolver. Patch from Dmitri Ilyin.
  226. if (m_uriResolver != null)
  227. {
  228. source = m_uriResolver.resolve(href, m_baseID);
  229. }
  230. else
  231. {
  232. href = SystemIDResolver.getAbsoluteURI(href, m_baseID);
  233. source = new SAXSource(new InputSource(href));
  234. }
  235. }
  236. catch(TransformerException te)
  237. {
  238. throw new org.xml.sax.SAXException(te);
  239. }
  240. }
  241. else if (name.equals("title"))
  242. {
  243. token = tokenizer.nextToken();
  244. while (tokenizer.hasMoreTokens() &&
  245. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  246. token = tokenizer.nextToken();
  247. title = token.substring(1, token.length() - 1);
  248. }
  249. else if (name.equals("media"))
  250. {
  251. token = tokenizer.nextToken();
  252. while (tokenizer.hasMoreTokens() &&
  253. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  254. token = tokenizer.nextToken();
  255. media = token.substring(1, token.length() - 1);
  256. }
  257. else if (name.equals("charset"))
  258. {
  259. token = tokenizer.nextToken();
  260. while (tokenizer.hasMoreTokens() &&
  261. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  262. token = tokenizer.nextToken();
  263. charset = token.substring(1, token.length() - 1);
  264. }
  265. else if (name.equals("alternate"))
  266. {
  267. token = tokenizer.nextToken();
  268. while (tokenizer.hasMoreTokens() &&
  269. (token.equals(" " ) || token.equals("\t") || token.equals("=")))
  270. token = tokenizer.nextToken();
  271. alternate = token.substring(1, token.length()
  272. - 1).equals("yes");
  273. }
  274. }
  275. if ((null != type)
  276. && (type.equals("text/xsl") || type.equals("text/xml") || type.equals("application/xml+xslt"))
  277. && (null != href))
  278. {
  279. if (null != m_media)
  280. {
  281. if (null != media)
  282. {
  283. if (!media.equals(m_media))
  284. return;
  285. }
  286. else
  287. return;
  288. }
  289. if (null != m_charset)
  290. {
  291. if (null != charset)
  292. {
  293. if (!charset.equals(m_charset))
  294. return;
  295. }
  296. else
  297. return;
  298. }
  299. if (null != m_title)
  300. {
  301. if (null != title)
  302. {
  303. if (!title.equals(m_title))
  304. return;
  305. }
  306. else
  307. return;
  308. }
  309. m_stylesheets.addElement(source);
  310. }
  311. }
  312. }
  313. /**
  314. * The spec notes that "The xml-stylesheet processing instruction is allowed only in the prolog of an XML document.",
  315. * so, at least for right now, I'm going to go ahead an throw a TransformerException
  316. * in order to stop the parse.
  317. *
  318. * @param uri The Namespace URI, or an empty string.
  319. * @param localName The local name (without prefix), or empty string if not namespace processing.
  320. * @param rawName The qualified name (with prefix).
  321. * @param attributes The specified or defaulted attributes.
  322. *
  323. * @throws StopParseException since there can be no valid xml-stylesheet processing
  324. * instructions past the first element.
  325. */
  326. public void startElement(
  327. String namespaceURI, String localName, String qName, Attributes atts)
  328. throws org.xml.sax.SAXException
  329. {
  330. throw new StopParseException();
  331. }
  332. }