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.xpath.internal;
  22 
  23 import com.sun.org.apache.xml.internal.dtm.DTM;
  24 import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
  25 import java.io.IOException;
  26 import java.util.ArrayList;
  27 import java.util.List;
  28 import javax.xml.transform.Source;
  29 import javax.xml.transform.SourceLocator;
  30 import javax.xml.transform.TransformerException;
  31 import javax.xml.transform.URIResolver;
  32 import javax.xml.transform.sax.SAXSource;
  33 import javax.xml.transform.stream.StreamSource;
  34 import org.xml.sax.XMLReader;
  35 import org.xml.sax.helpers.XMLReaderFactory;
  36 
  37 /**
  38  * This class bottlenecks all management of source trees.  The methods
  39  * in this class should allow easy garbage collection of source
  40  * trees (not yet!), and should centralize parsing for those source trees.
  41  *
  42  * @LastModified: Oct 2017
  43  */
  44 @SuppressWarnings("deprecation")
  45 public class SourceTreeManager
  46 {
  47 
  48   /** List of SourceTree objects that this manager manages. */
  49   private List<SourceTree> m_sourceTree = new ArrayList<>();
  50 
  51   /**
  52    * Reset the list of SourceTree objects that this manager manages.
  53    *
  54    */
  55   public void reset()
  56   {
  57     m_sourceTree = new ArrayList<>();
  58   }
  59 
  60   /** The TrAX URI resolver used to obtain source trees. */
  61   URIResolver m_uriResolver;
  62 
  63   /**
  64    * Set an object that will be used to resolve URIs used in
  65    * document(), etc.
  66    * @param resolver An object that implements the URIResolver interface,
  67    * or null.
  68    */
  69   public void setURIResolver(URIResolver resolver)
  70   {
  71     m_uriResolver = resolver;
  72   }
  73 
  74   /**
  75    * Get the object that will be used to resolve URIs used in
  76    * document(), etc.
  77    * @return An object that implements the URIResolver interface,
  78    * or null.
  79    */
  80   public URIResolver getURIResolver()
  81   {
  82     return m_uriResolver;
  83   }
  84 
  85   /**
  86    * Given a document, find the URL associated with that document.
  87    * @param owner Document that was previously processed by this liaison.
  88    *
  89    * @return The base URI of the owner argument.
  90    */
  91   public String findURIFromDoc(int owner)
  92   {
  93     int n = m_sourceTree.size();
  94 
  95     for (int i = 0; i < n; i++)
  96     {
  97       SourceTree sTree = m_sourceTree.get(i);
  98 
  99       if (owner == sTree.m_root)
 100         return sTree.m_url;
 101     }
 102 
 103     return null;
 104   }
 105 
 106   /**
 107    * This will be called by the processor when it encounters
 108    * an xsl:include, xsl:import, or document() function.
 109    *
 110    * @param base The base URI that should be used.
 111    * @param urlString Value from an xsl:import or xsl:include's href attribute,
 112    * or a URI specified in the document() function.
 113    *
 114    * @return a Source that can be used to process the resource.
 115    *
 116    * @throws IOException
 117    * @throws TransformerException
 118    */
 119   public Source resolveURI(
 120           String base, String urlString, SourceLocator locator)
 121             throws TransformerException, IOException
 122   {
 123 
 124     Source source = null;
 125 
 126     if (null != m_uriResolver)
 127     {
 128       source = m_uriResolver.resolve(urlString, base);
 129     }
 130 
 131     if (null == source)
 132     {
 133       String uri = SystemIDResolver.getAbsoluteURI(urlString, base);
 134 
 135       source = new StreamSource(uri);
 136     }
 137 
 138     return source;
 139   }
 140 
 141   /** JJK: Support  <?xalan:doc_cache_off?> kluge in ElemForEach.
 142    * TODO: This function is highly dangerous. Cache management must be improved.
 143    *
 144    * @param n The node to remove.
 145    */
 146   public void removeDocumentFromCache(int n)
 147   {
 148     if(DTM.NULL ==n)
 149       return;
 150     for(int i=m_sourceTree.size()-1;i>=0;--i)
 151     {
 152       SourceTree st= m_sourceTree.get(i);
 153       if(st!=null && st.m_root==n)
 154       {
 155         m_sourceTree.remove(i);
 156         return;
 157       }
 158     }
 159   }
 160 
 161 
 162 
 163   /**
 164    * Put the source tree root node in the document cache.
 165    * TODO: This function needs to be a LOT more sophisticated.
 166    *
 167    * @param n The node to cache.
 168    * @param source The Source object to cache.
 169    */
 170   public void putDocumentInCache(int n, Source source)
 171   {
 172 
 173     int cachedNode = getNode(source);
 174 
 175     if (DTM.NULL != cachedNode)
 176     {
 177       if (!(cachedNode == n))
 178         throw new RuntimeException(
 179           "Programmer's Error!  "
 180           + "putDocumentInCache found reparse of doc: "
 181           + source.getSystemId());
 182       return;
 183     }
 184     if (null != source.getSystemId())
 185     {
 186       m_sourceTree.add(new SourceTree(n, source.getSystemId()));
 187     }
 188   }
 189 
 190   /**
 191    * Given a Source object, find the node associated with it.
 192    *
 193    * @param source The Source object to act as the key.
 194    *
 195    * @return The node that is associated with the Source, or null if not found.
 196    */
 197   public int getNode(Source source)
 198   {
 199 
 200 //    if (source instanceof DOMSource)
 201 //      return ((DOMSource) source).getNode();
 202 
 203     // TODO: Not sure if the BaseID is really the same thing as the ID.
 204     String url = source.getSystemId();
 205 
 206     if (null == url)
 207       return DTM.NULL;
 208 
 209     int n = m_sourceTree.size();
 210 
 211     // System.out.println("getNode: "+n);
 212     for (int i = 0; i < n; i++)
 213     {
 214       SourceTree sTree = m_sourceTree.get(i);
 215 
 216       // System.out.println("getNode -         url: "+url);
 217       // System.out.println("getNode - sTree.m_url: "+sTree.m_url);
 218       if (url.equals(sTree.m_url))
 219         return sTree.m_root;
 220     }
 221 
 222     // System.out.println("getNode - returning: "+node);
 223     return DTM.NULL;
 224   }
 225 
 226   /**
 227    * Get the source tree from the a base URL and a URL string.
 228    *
 229    * @param base The base URI to use if the urlString is relative.
 230    * @param urlString An absolute or relative URL string.
 231    * @param locator The location of the caller, for diagnostic purposes.
 232    *
 233    * @return should be a non-null reference to the node identified by the
 234    * base and urlString.
 235    *
 236    * @throws TransformerException If the URL can not resolve to a node.
 237    */
 238   public int getSourceTree(
 239           String base, String urlString, SourceLocator locator, XPathContext xctxt)
 240             throws TransformerException
 241   {
 242 
 243     // System.out.println("getSourceTree");
 244     try
 245     {
 246       Source source = this.resolveURI(base, urlString, locator);
 247 
 248       // System.out.println("getSourceTree - base: "+base+", urlString: "+urlString+", source: "+source.getSystemId());
 249       return getSourceTree(source, locator, xctxt);
 250     }
 251     catch (IOException ioe)
 252     {
 253       throw new TransformerException(ioe.getMessage(), locator, ioe);
 254     }
 255 
 256     /* catch (TransformerException te)
 257      {
 258        throw new TransformerException(te.getMessage(), locator, te);
 259      }*/
 260   }
 261 
 262   /**
 263    * Get the source tree from the input source.
 264    *
 265    * @param source The Source object that should identify the desired node.
 266    * @param locator The location of the caller, for diagnostic purposes.
 267    *
 268    * @return non-null reference to a node.
 269    *
 270    * @throws TransformerException if the Source argument can't be resolved to
 271    *         a node.
 272    */
 273   public int getSourceTree(Source source, SourceLocator locator, XPathContext xctxt)
 274           throws TransformerException
 275   {
 276 
 277     int n = getNode(source);
 278 
 279     if (DTM.NULL != n)
 280       return n;
 281 
 282     n = parseToNode(source, locator, xctxt);
 283 
 284     if (DTM.NULL != n)
 285       putDocumentInCache(n, source);
 286 
 287     return n;
 288   }
 289 
 290   /**
 291    * Try to create a DOM source tree from the input source.
 292    *
 293    * @param source The Source object that identifies the source node.
 294    * @param locator The location of the caller, for diagnostic purposes.
 295    *
 296    * @return non-null reference to node identified by the source argument.
 297    *
 298    * @throws TransformerException if the source argument can not be resolved
 299    *         to a source node.
 300    */
 301   public int parseToNode(Source source, SourceLocator locator, XPathContext xctxt)
 302           throws TransformerException
 303   {
 304 
 305     try
 306     {
 307       Object xowner = xctxt.getOwnerObject();
 308       DTM dtm;
 309       if(null != xowner && xowner instanceof com.sun.org.apache.xml.internal.dtm.DTMWSFilter)
 310       {
 311         dtm = xctxt.getDTM(source, false,
 312                           (com.sun.org.apache.xml.internal.dtm.DTMWSFilter)xowner, false, true);
 313       }
 314       else
 315       {
 316         dtm = xctxt.getDTM(source, false, null, false, true);
 317       }
 318       return dtm.getDocument();
 319     }
 320     catch (Exception e)
 321     {
 322       //e.printStackTrace();
 323       throw new TransformerException(e.getMessage(), locator, e);
 324     }
 325 
 326   }
 327 
 328   /**
 329    * This method returns the SAX2 parser to use with the InputSource
 330    * obtained from this URI.
 331    * It may return null if any SAX2-conformant XML parser can be used,
 332    * or if getInputSource() will also return null. The parser must
 333    * be free for use (i.e.
 334    * not currently in use for another parse().
 335    *
 336    * @param inputSource The value returned from the URIResolver.
 337    * @return a SAX2 XMLReader to use to resolve the inputSource argument.
 338    * @param locator The location of the original caller, for diagnostic purposes.
 339    *
 340    * @throws TransformerException if the reader can not be created.
 341    */
 342   public static XMLReader getXMLReader(Source inputSource, SourceLocator locator)
 343           throws TransformerException
 344   {
 345 
 346     try
 347     {
 348       XMLReader reader = (inputSource instanceof SAXSource)
 349                          ? ((SAXSource) inputSource).getXMLReader() : null;
 350 
 351       if (null == reader)
 352       {
 353         try {
 354           javax.xml.parsers.SAXParserFactory factory=
 355               javax.xml.parsers.SAXParserFactory.newInstance();
 356           factory.setNamespaceAware( true );
 357           javax.xml.parsers.SAXParser jaxpParser=
 358               factory.newSAXParser();
 359           reader=jaxpParser.getXMLReader();
 360 
 361         } catch( javax.xml.parsers.ParserConfigurationException ex ) {
 362           throw new org.xml.sax.SAXException( ex );
 363         } catch( javax.xml.parsers.FactoryConfigurationError ex1 ) {
 364             throw new org.xml.sax.SAXException( ex1.toString() );
 365         } catch( NoSuchMethodError ex2 ) {
 366         }
 367         catch (AbstractMethodError ame){}
 368         if(null == reader)
 369           reader = XMLReaderFactory.createXMLReader();
 370       }
 371 
 372       try
 373       {
 374         reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
 375                           true);
 376       }
 377       catch (org.xml.sax.SAXException se)
 378       {
 379 
 380         // What can we do?
 381         // TODO: User diagnostics.
 382       }
 383 
 384       return reader;
 385     }
 386     catch (org.xml.sax.SAXException se)
 387     {
 388       throw new TransformerException(se.getMessage(), locator, se);
 389     }
 390   }
 391 }