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