1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: StylesheetPIHandler.java 468655 2006-10-28 07:12:06Z minchau $
20  */
21 package org.apache.xml.utils;
22 
23 import java.util.StringTokenizer;
24 import java.util.Vector;
25 
26 import javax.xml.transform.Source;
27 import javax.xml.transform.TransformerException;
28 import javax.xml.transform.URIResolver;
29 import javax.xml.transform.sax.SAXSource;
30 
31 import org.apache.xml.utils.SystemIDResolver;
32 
33 import org.xml.sax.Attributes;
34 import org.xml.sax.InputSource;
35 import org.xml.sax.helpers.DefaultHandler;
36 
37 /**
38  * Search for the xml-stylesheet processing instructions in an XML document.
39  * @see <a href="http://www.w3.org/TR/xml-stylesheet/">Associating Style Sheets with XML documents, Version 1.0</a>
40  */
41 public class StylesheetPIHandler extends DefaultHandler
42 {
43   /** The baseID of the document being processed.  */
44   String m_baseID;
45 
46   /** The desired media criteria. */
47   String m_media;
48 
49   /** The desired title criteria.  */
50   String m_title;
51 
52   /** The desired character set criteria.   */
53   String m_charset;
54 
55   /** A list of SAXSource objects that match the criteria.  */
56   Vector m_stylesheets = new Vector();
57 
58   // Add code to use a URIResolver. Patch from Dmitri Ilyin.
59 
60   /**
61    * The object that implements the URIResolver interface,
62    * or null.
63    */
64   URIResolver m_uriResolver;
65 
66   /**
67    * Get the object that will be used to resolve URIs in href
68    * in xml-stylesheet processing instruction.
69    *
70    * @param resolver An object that implements the URIResolver interface,
71    * or null.
72    */
setURIResolver(URIResolver resolver)73   public void setURIResolver(URIResolver resolver)
74   {
75     m_uriResolver = resolver;
76   }
77 
78   /**
79    * Get the object that will be used to resolve URIs in href
80    * in xml-stylesheet processing instruction.
81    *
82    * @return The URIResolver that was set with setURIResolver.
83    */
getURIResolver()84   public URIResolver getURIResolver()
85   {
86     return m_uriResolver;
87   }
88 
89   /**
90    * Construct a StylesheetPIHandler instance that will search
91    * for xml-stylesheet PIs based on the given criteria.
92    *
93    * @param baseID The base ID of the XML document, needed to resolve
94    *               relative IDs.
95    * @param media The desired media criteria.
96    * @param title The desired title criteria.
97    * @param charset The desired character set criteria.
98    */
StylesheetPIHandler(String baseID, String media, String title, String charset)99   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    */
getAssociatedStylesheet()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    */
processingInstruction(String target, String data)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    */
startElement( String namespaceURI, String localName, String qName, Attributes atts)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     */
setBaseId(String baseId)332    public void setBaseId(String baseId) {
333        m_baseID = baseId;
334 
335    }
getBaseId()336    public String  getBaseId() {
337        return m_baseID ;
338    }
339 
340 }
341