1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xml.internal.utils; 22 23 import java.util.ArrayList; 24 import java.util.List; 25 import java.util.StringTokenizer; 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 import org.xml.sax.Attributes; 31 import org.xml.sax.InputSource; 32 import org.xml.sax.helpers.DefaultHandler; 33 34 /** 35 * Search for the xml-stylesheet processing instructions in an XML document. 36 * @see <a href="http://www.w3.org/TR/xml-stylesheet/"> 37 * Associating Style Sheets with XML documents, Version 1.0</a> 38 * 39 * @LastModified: Oct 2017 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 List<Source> m_stylesheets = new ArrayList<>(); 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 */ 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 */ 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 */ 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 */ 115 public Source getAssociatedStylesheet() 116 { 117 118 int sz = m_stylesheets.size(); 119 120 if (sz > 0) 121 { 122 Source source = m_stylesheets.get(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/"> 139 * Associating Style Sheets with XML documents, Version 1.0</a> 140 */ 141 public void processingInstruction(String target, String data) 142 throws org.xml.sax.SAXException 143 { 144 145 if (target.equals("xml-stylesheet")) 146 { 147 String href = null; // CDATA #REQUIRED 148 String type = null; // CDATA #REQUIRED 149 String title = null; // CDATA #IMPLIED 150 String media = null; // CDATA #IMPLIED 151 String charset = null; // CDATA #IMPLIED 152 boolean alternate = false; // (yes|no) "no" 153 StringTokenizer tokenizer = new StringTokenizer(data, " \t=\n", true); 154 boolean lookedAhead = false; 155 Source source = null; 156 157 String token = ""; 158 while (tokenizer.hasMoreTokens()) 159 { 160 if (!lookedAhead) 161 token = tokenizer.nextToken(); 162 else 163 lookedAhead = false; 164 if (tokenizer.hasMoreTokens() && 165 (token.equals(" ") || token.equals("\t") || token.equals("="))) 166 continue; 167 168 String name = token; 169 if (name.equals("type")) 170 { 171 token = tokenizer.nextToken(); 172 while (tokenizer.hasMoreTokens() && 173 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 174 token = tokenizer.nextToken(); 175 type = token.substring(1, token.length() - 1); 176 177 } 178 else if (name.equals("href")) 179 { 180 token = tokenizer.nextToken(); 181 while (tokenizer.hasMoreTokens() && 182 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 183 token = tokenizer.nextToken(); 184 href = token; 185 if (tokenizer.hasMoreTokens()) 186 { 187 token = tokenizer.nextToken(); 188 // If the href value has parameters to be passed to a 189 // servlet(something like "foobar?id=12..."), 190 // we want to make sure we get them added to 191 // the href value. Without this check, we would move on 192 // to try to process another attribute and that would be 193 // wrong. 194 // We need to set lookedAhead here to flag that we 195 // already have the next token. 196 while ( token.equals("=") && tokenizer.hasMoreTokens()) 197 { 198 href = href + token + tokenizer.nextToken(); 199 if (tokenizer.hasMoreTokens()) 200 { 201 token = tokenizer.nextToken(); 202 lookedAhead = true; 203 } 204 else 205 { 206 break; 207 } 208 } 209 } 210 href = href.substring(1, href.length() - 1); 211 try 212 { 213 // Add code to use a URIResolver. Patch from Dmitri Ilyin. 214 if (m_uriResolver != null) 215 { 216 source = m_uriResolver.resolve(href, m_baseID); 217 } 218 else 219 { 220 href = SystemIDResolver.getAbsoluteURI(href, m_baseID); 221 source = new SAXSource(new InputSource(href)); 222 } 223 } 224 catch(TransformerException te) 225 { 226 throw new org.xml.sax.SAXException(te); 227 } 228 } 229 else if (name.equals("title")) 230 { 231 token = tokenizer.nextToken(); 232 while (tokenizer.hasMoreTokens() && 233 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 234 token = tokenizer.nextToken(); 235 title = token.substring(1, token.length() - 1); 236 } 237 else if (name.equals("media")) 238 { 239 token = tokenizer.nextToken(); 240 while (tokenizer.hasMoreTokens() && 241 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 242 token = tokenizer.nextToken(); 243 media = token.substring(1, token.length() - 1); 244 } 245 else if (name.equals("charset")) 246 { 247 token = tokenizer.nextToken(); 248 while (tokenizer.hasMoreTokens() && 249 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 250 token = tokenizer.nextToken(); 251 charset = token.substring(1, token.length() - 1); 252 } 253 else if (name.equals("alternate")) 254 { 255 token = tokenizer.nextToken(); 256 while (tokenizer.hasMoreTokens() && 257 (token.equals(" " ) || token.equals("\t") || token.equals("="))) 258 token = tokenizer.nextToken(); 259 alternate = token.substring(1, token.length() 260 - 1).equals("yes"); 261 } 262 263 } 264 265 if ((null != type) 266 && (type.equals("text/xsl") || type.equals("text/xml") || type.equals("application/xml+xslt")) 267 && (null != href)) 268 { 269 if (null != m_media) 270 { 271 if (null != media) 272 { 273 if (!media.equals(m_media)) 274 return; 275 } 276 else 277 return; 278 } 279 280 if (null != m_charset) 281 { 282 if (null != charset) 283 { 284 if (!charset.equals(m_charset)) 285 return; 286 } 287 else 288 return; 289 } 290 291 if (null != m_title) 292 { 293 if (null != title) 294 { 295 if (!title.equals(m_title)) 296 return; 297 } 298 else 299 return; 300 } 301 302 m_stylesheets.add(source); 303 } 304 } 305 } 306 307 308 /** 309 * The spec notes that "The xml-stylesheet processing instruction is allowed only in the prolog of an XML document.", 310 * so, at least for right now, I'm going to go ahead an throw a TransformerException 311 * in order to stop the parse. 312 * 313 * @param namespaceURI The Namespace URI, or an empty string. 314 * @param localName The local name (without prefix), or empty string if not namespace processing. 315 * @param qName The qualified name (with prefix). 316 * @param atts The specified or defaulted attributes. 317 * 318 * @throws StopParseException since there can be no valid xml-stylesheet processing 319 * instructions past the first element. 320 */ 321 public void startElement( 322 String namespaceURI, String localName, String qName, Attributes atts) 323 throws org.xml.sax.SAXException 324 { 325 throw new StopParseException(); 326 } 327 328 /** 329 * Added additional getter and setter methods for the Base Id 330 * to fix bugzilla bug 24187 331 * 332 */ 333 public void setBaseId(String baseId) { 334 m_baseID = baseId; 335 336 } 337 public String getBaseId() { 338 return m_baseID ; 339 } 340 341 }