Home | History | Annotate | Download | only in utils
      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    */
     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 = (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 }
    341