1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 4 */ 5 /* 6 * Licensed to the Apache Software Foundation (ASF) under one or more 7 * contributor license agreements. See the NOTICE file distributed with 8 * this work for additional information regarding copyright ownership. 9 * The ASF licenses this file to You under the Apache License, Version 2.0 10 * (the "License"); you may not use this file except in compliance with 11 * the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 */ 21 22 package com.sun.org.apache.xml.internal.utils; 23 24 import java.util.ArrayList; 25 import java.util.List; 26 import java.util.StringTokenizer; 27 import javax.xml.transform.Source; 28 import javax.xml.transform.TransformerException; 29 import javax.xml.transform.URIResolver; 30 import javax.xml.transform.sax.SAXSource; 31 import org.xml.sax.Attributes; 32 import org.xml.sax.InputSource; 33 import org.xml.sax.helpers.DefaultHandler; 34 35 /** 36 * Search for the xml-stylesheet processing instructions in an XML document. 37 * @see <a href="http://www.w3.org/TR/xml-stylesheet/"> 38 * Associating Style Sheets with XML documents, Version 1.0</a> 39 */ 40 public class StylesheetPIHandler extends DefaultHandler 41 { 42 /** The baseID of the document being processed. */ 43 String m_baseID; 44 45 /** The desired media criteria. */ 46 String m_media; 47 48 /** The desired title criteria. */ 49 String m_title; 50 51 /** The desired character set criteria. */ 52 String m_charset; 53 54 /** A list of SAXSource objects that match the criteria. */ 55 List<Source> m_stylesheets = new ArrayList<>(); 56 57 // Add code to use a URIResolver. Patch from Dmitri Ilyin. 58 59 /** 60 * The object that implements the URIResolver interface, 61 * or null. 62 */ 63 URIResolver m_uriResolver; 64 65 /** 66 * Get the object that will be used to resolve URIs in href 67 * in xml-stylesheet processing instruction. 68 * 69 * @param resolver An object that implements the URIResolver interface, 70 * or null. 71 */ 72 public void setURIResolver(URIResolver resolver) 73 { 74 m_uriResolver = resolver; 75 } 76 77 /** 78 * Get the object that will be used to resolve URIs in href 79 * in xml-stylesheet processing instruction. 80 * 81 * @return The URIResolver that was set with setURIResolver. 82 */ 83 public URIResolver getURIResolver() 84 { 85 return m_uriResolver; 86 } 87 88 /** 89 * Construct a StylesheetPIHandler instance that will search 90 * for xml-stylesheet PIs based on the given criteria. 91 * 92 * @param baseID The base ID of the XML document, needed to resolve 93 * relative IDs. 94 * @param media The desired media criteria. 95 * @param title The desired title criteria. 96 * @param charset The desired character set criteria. 97 */ 98 public StylesheetPIHandler(String baseID, String media, String title, 99 String charset) 100 { 101 102 m_baseID = baseID; 103 m_media = media; 104 m_title = title; 105 m_charset = charset; 106 } 107 108 /** 109 * Return the last stylesheet found that match the constraints. 110 * 111 * @return Source object that references the last stylesheet reference 112 * that matches the constraints. 113 */ 114 public Source getAssociatedStylesheet() 115 { 116 117 int sz = m_stylesheets.size(); 118 119 if (sz > 0) 120 { 121 Source source = m_stylesheets.get(sz-1); 122 return source; 123 } 124 else 125 return null; 126 } 127 128 /** 129 * Handle the xml-stylesheet processing instruction. 130 * 131 * @param target The processing instruction target. 132 * @param data The processing instruction data, or null if 133 * none is supplied. 134 * @throws org.xml.sax.SAXException Any SAX exception, possibly 135 * wrapping another exception. 136 * @see org.xml.sax.ContentHandler#processingInstruction 137 * @see <a href="http://www.w3.org/TR/xml-stylesheet/"> 138 * 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.add(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 }