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