content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * Copyright 2001-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id: DocumentCache.java,v 1.2.4.1 2005/09/06 06:15:22 pvedula Exp $
   */
  
  package jaxp.sun.org.apache.xalan.internal.xsltc.dom;
  
  import java.io.File;
  import java.io.PrintWriter;
  import java.net.URL;
  import java.net.URLConnection;
  import java.net.URLDecoder;
  import java.util.Date;
  import java.util.Hashtable;
  
  import jaxp.xml.parsers.ParserConfigurationException;
  import jaxp.xml.parsers.SAXParser;
  import jaxp.xml.parsers.SAXParserFactory;
  import jaxp.xml.transform.TransformerException;
  import jaxp.xml.transform.sax.SAXSource;
  
  import jaxp.sun.org.apache.xalan.internal.xsltc.DOM;
  import jaxp.sun.org.apache.xalan.internal.xsltc.DOMCache;
  import jaxp.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
  import jaxp.sun.org.apache.xalan.internal.xsltc.Translet;
  import jaxp.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  import jaxp.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary;
  import jaxp.sun.org.apache.xalan.internal.xsltc.runtime.Constants;
  import jaxp.sun.org.apache.xml.internal.utils.SystemIDResolver;
  
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.XMLReader;
  
  /**
   * @author Morten Jorgensen
   */
  public final class DocumentCache implements DOMCache {
  
      private int       _size;
      private Hashtable _references;
      private String[]  _URIs;
      private int       _count;
      private int       _current;
      private SAXParser _parser;
      private XMLReader _reader;
      private XSLTCDTMManager _dtmManager;
  
      private static final int REFRESH_INTERVAL = 1000;
  
      /*
       * Inner class containing a DOMImpl object and DTD handler
       */
      public final class CachedDocument {
  
          // Statistics data
          private long _firstReferenced;
          private long _lastReferenced;
          private long _accessCount;
          private long _lastModified;
          private long _lastChecked;
          private long _buildTime;
  
          // DOM and DTD handler references
          private DOMEnhancedForDTM _dom = null;
  
          /**
           * Constructor - load document and initialise statistics
           */
          public CachedDocument(String uri) {
              // Initialise statistics variables
              final long stamp = System.currentTimeMillis();
              _firstReferenced = stamp;
              _lastReferenced  = stamp;
              _accessCount     = 0;
              loadDocument(uri);
  
              _buildTime = System.currentTimeMillis() - stamp;
          }
  
          /**
           * Loads the document and updates build-time (latency) statistics
           */
          public void loadDocument(String uri) {
  
              try {
                  final long stamp = System.currentTimeMillis();
                  _dom = (DOMEnhancedForDTM)_dtmManager.getDTM(
                                   new SAXSource(_reader, new InputSource(uri)),
                                   false, null, true, false);
                  _dom.setDocumentURI(uri);
  
                  // The build time can be used for statistics for a better
                  // priority algorithm (currently round robin).
                  final long thisTime = System.currentTimeMillis() - stamp;
                  if (_buildTime > 0)
                      _buildTime = (_buildTime + thisTime) >>> 1;
                  else
                      _buildTime = thisTime;
              }
              catch (Exception e) {
                  _dom = null;
              }
          }
  
          public DOM getDocument()       { return(_dom); }
  
          public long getFirstReferenced()   { return(_firstReferenced); }
  
          public long getLastReferenced()    { return(_lastReferenced); }
  
          public long getAccessCount()       { return(_accessCount); }
  
          public void incAccessCount()       { _accessCount++; }
  
          public long getLastModified()      { return(_lastModified); }
  
          public void setLastModified(long t){ _lastModified = t; }
  
          public long getLatency()           { return(_buildTime); }
  
          public long getLastChecked()       { return(_lastChecked); }
  
          public void setLastChecked(long t) { _lastChecked = t; }
  
          public long getEstimatedSize() {
              if (_dom != null)
                  return(_dom.getSize() << 5); // ???
              else
                  return(0);
          }
  
      }
  
      /**
       * DocumentCache constructor
       */
      public DocumentCache(int size) throws SAXException {
          this(size, null);
          try {
              _dtmManager = XSLTCDTMManager.createNewDTMManagerInstance();
          } catch (Exception e) {
              throw new SAXException(e);
          }
      }
  
      /**
       * DocumentCache constructor
       */
      public DocumentCache(int size, XSLTCDTMManager dtmManager) throws SAXException {
          _dtmManager = dtmManager;
          _count = 0;
          _current = 0;
          _size  = size;
          _references = new Hashtable(_size+2);
          _URIs = new String[_size];
  
          try {
              // Create a SAX parser and get the XMLReader object it uses
              final SAXParserFactory factory = SAXParserFactory.newInstance();
              try {
                  factory.setFeature(Constants.NAMESPACE_FEATURE,true);
              }
              catch (Exception e) {
                  factory.setNamespaceAware(true);
              }
              _parser = factory.newSAXParser();
              _reader = _parser.getXMLReader();
          }
          catch (ParserConfigurationException e) {
              BasisLibrary.runTimeError(BasisLibrary.NAMESPACES_SUPPORT_ERR);
          }
      }
  
      /**
       * Returns the time-stamp for a document's last update
       */
      private final long getLastModified(String uri) {
          try {
              URL url = new URL(uri);
              URLConnection connection = url.openConnection();
              long timestamp = connection.getLastModified();
              // Check for a "file:" URI (courtesy of Brian Ewins)
              if (timestamp == 0){ // get 0 for local URI
                  if ("file".equals(url.getProtocol())){
                      File localfile = new File(URLDecoder.decode(url.getFile()));
                      timestamp = localfile.lastModified();
                  }
              }
              return(timestamp);
          }
          // Brutal handling of all exceptions
          catch (Exception e) {
              return(System.currentTimeMillis());
          }
      }
  
      /**
       *
       */
      private CachedDocument lookupDocument(String uri) {
          return((CachedDocument)_references.get(uri));
      }
  
      /**
       *
       */
      private synchronized void insertDocument(String uri, CachedDocument doc) {
          if (_count < _size) {
              // Insert out URI in circular buffer
              _URIs[_count++] = uri;
              _current = 0;
          }
          else {
              // Remove oldest URI from reference Hashtable
              _references.remove(_URIs[_current]);
              // Insert our URI in circular buffer
              _URIs[_current] = uri;
              if (++_current >= _size) _current = 0;
          }
          _references.put(uri, doc);
      }
  
      /**
       *
       */
      private synchronized void replaceDocument(String uri, CachedDocument doc) {
          CachedDocument old = (CachedDocument)_references.get(uri);
          if (doc == null)
              insertDocument(uri, doc);
          else
              _references.put(uri, doc);
      }
  
      /**
       * Returns a document either by finding it in the cache or
       * downloading it and putting it in the cache.
       */
      @Override
      public DOM retrieveDocument(String baseURI, String href, Translet trs) {
          CachedDocument doc;
  
      String uri = href;
      if (baseURI != null && !baseURI.equals("")) {
          try {
              uri = SystemIDResolver.getAbsoluteURI(uri, baseURI);
          } catch (TransformerException te) {
              // ignore
          }
      }
  
          // Try to get the document from the cache first
          if ((doc = lookupDocument(uri)) == null) {
              doc = new CachedDocument(uri);
              if (doc == null) return null; // better error handling needed!!!
              doc.setLastModified(getLastModified(uri));
              insertDocument(uri, doc);
          }
          // If the document is in the cache we must check if it is still valid
          else {
              long now = System.currentTimeMillis();
              long chk = doc.getLastChecked();
              doc.setLastChecked(now);
              // Has the modification time for this file been checked lately?
              if (now > (chk + REFRESH_INTERVAL)) {
                  doc.setLastChecked(now);
                  long last = getLastModified(uri);
                  // Reload document if it has been modified since last download
                  if (last > doc.getLastModified()) {
                      doc = new CachedDocument(uri);
                      if (doc == null) return null;
                      doc.setLastModified(getLastModified(uri));
                      replaceDocument(uri, doc);
                  }
              }
  
          }
  
          // Get the references to the actual DOM and DTD handler
          final DOM dom = doc.getDocument();
  
          // The dom reference may be null if the URL pointed to a
          // non-existing document
          if (dom == null) return null;
  
          doc.incAccessCount(); // For statistics
  
          final AbstractTranslet translet = (AbstractTranslet)trs;
  
          // Give the translet an early opportunity to extract any
          // information from the DOM object that it would like.
          translet.prepassDocument(dom);
  
          return(doc.getDocument());
      }
  
      /**
       * Outputs the cache statistics
       */
      public void getStatistics(PrintWriter out) {
          out.println("<h2>DOM cache statistics</h2><center><table border=\"2\">"+
                      "<tr><td><b>Document URI</b></td>"+
                      "<td><center><b>Build time</b></center></td>"+
                      "<td><center><b>Access count</b></center></td>"+
                      "<td><center><b>Last accessed</b></center></td>"+
                      "<td><center><b>Last modified</b></center></td></tr>");
  
          for (int i=0; i<_count; i++) {
              CachedDocument doc = (CachedDocument)_references.get(_URIs[i]);
              out.print("<tr><td><a href=\""+_URIs[i]+"\">"+
                        "<font size=-1>"+_URIs[i]+"</font></a></td>");
              out.print("<td><center>"+doc.getLatency()+"ms</center></td>");
              out.print("<td><center>"+doc.getAccessCount()+"</center></td>");
              out.print("<td><center>"+(new Date(doc.getLastReferenced()))+
                        "</center></td>");
              out.print("<td><center>"+(new Date(doc.getLastModified()))+
                        "</center></td>");
              out.println("</tr>");
          }
  
          out.println("</table></center>");
      }
  }
  