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 }