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 }