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!
   */
  // Resolver.java - Represents an extension of OASIS Open Catalog files.
  
  /*
   * Copyright 2001-2004 The Apache Software Foundation or its licensors,
   * as applicable.
   *
   * 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.
   */
  
  package jaxp.sun.org.apache.xml.internal.resolver;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.FileNotFoundException;
  import java.util.Enumeration;
  import java.util.Vector;
  import java.net.URL;
  import java.net.URLConnection;
  import java.net.MalformedURLException;
  import jaxp.xml.parsers.SAXParserFactory;
  import jaxp.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl;
  import jaxp.sun.org.apache.xerces.internal.utils.SecuritySupport;
  import jaxp.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader;
  import jaxp.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader;
  import jaxp.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader;
  
  /**
   * An extension to OASIS Open Catalog files, this class supports
   * suffix-based matching and an external RFC2483 resolver.
   *
   * @see Catalog
   *
   * @author Norman Walsh
   * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
   *
   * @version 1.0
   */
  public class Resolver extends Catalog {
    /**
     * The URISUFFIX Catalog Entry type.
     *
     * <p>URI suffix entries match URIs that end in a specified suffix.</p>
     */
    public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2);
  
    /**
     * The SYSTEMSUFFIX Catalog Entry type.
     *
     * <p>System suffix entries match system identifiers that end in a
     * specified suffix.</p>
     */
    public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2);
  
    /**
     * The RESOLVER Catalog Entry type.
     *
     * <p>A hook for providing support for web-based backup resolvers.</p>
     */
    public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1);
  
    /**
     * The SYSTEMREVERSE Catalog Entry type.
     *
     * <p>This is a bit of a hack. There's no actual SYSTEMREVERSE entry,
     * but this entry type is used to indicate that a reverse lookup is
     * being performed. (This allows the Resolver to implement
     * RFC2483 I2N and I2NS.)
     */
    public static final int SYSTEMREVERSE
      = CatalogEntry.addEntryType("SYSTEMREVERSE", 1);
  
    /**
     * Setup readers.
     */
    public void setupReaders() {
      SAXParserFactory spf = catalogManager.useServicesMechanism() ?
                      SAXParserFactory.newInstance() : new SAXParserFactoryImpl();
      spf.setNamespaceAware(true);
      spf.setValidating(false);
  
      SAXCatalogReader saxReader = new SAXCatalogReader(spf);
  
      saxReader.setCatalogParser(null, "XMLCatalog",
                                 "jaxp.sun.org.apache.xml.internalresolver.readers.XCatalogReader");
  
      saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName,
                                 "catalog",
                                 "jaxp.sun.org.apache.xml.internalresolver.readers.ExtendedXMLCatalogReader");
  
      addReader("application/xml", saxReader);
  
      TR9401CatalogReader textReader = new TR9401CatalogReader();
      addReader("text/plain", textReader);
    }
  
    /**
     * Cleanup and process a Catalog entry.
     *
     * <p>This method processes each Catalog entry, changing mapped
     * relative system identifiers into absolute ones (based on the current
     * base URI), and maintaining other information about the current
     * catalog.</p>
     *
     * @param entry The CatalogEntry to process.
     */
    public void addEntry(CatalogEntry entry) {
      int type = entry.getEntryType();
  
      if (type == URISUFFIX) {
        String suffix = normalizeURI(entry.getEntryArg(0));
        String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
  
        entry.setEntryArg(1, fsi);
  
        catalogManager.debug.message(4, "URISUFFIX", suffix, fsi);
      } else if (type == SYSTEMSUFFIX) {
        String suffix = normalizeURI(entry.getEntryArg(0));
        String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
  
        entry.setEntryArg(1, fsi);
  
        catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi);
      }
  
      super.addEntry(entry);
    }
  
    /**
     * Return the applicable URI.
     *
     * <p>If a URI entry exists in the Catalog
     * for the URI specified, return the mapped value.</p>
     *
     * <p>In the Resolver (as opposed to the Catalog) class, if the
     * URI isn't found by the usual algorithm, URISUFFIX entries are
     * considered.</p>
     *
     * <p>URI comparison is case sensitive.</p>
     *
     * @param uri The URI to locate in the catalog.
     *
     * @return The resolved URI.
     *
     * @throws MalformedURLException The system identifier of a
     * subordinate catalog cannot be turned into a valid URL.
     * @throws IOException Error reading subordinate catalog file.
     */
    public String resolveURI(String uri)
      throws MalformedURLException, IOException {
  
      String resolved = super.resolveURI(uri);
      if (resolved != null) {
        return resolved;
      }
  
      Enumeration en = catalogEntries.elements();
      while (en.hasMoreElements()) {
        CatalogEntry e = (CatalogEntry) en.nextElement();
        if (e.getEntryType() == RESOLVER) {
          resolved = resolveExternalSystem(uri, e.getEntryArg(0));
          if (resolved != null) {
            return resolved;
          }
        } else if (e.getEntryType() == URISUFFIX) {
          String suffix = e.getEntryArg(0);
          String result = e.getEntryArg(1);
  
          if (suffix.length() <= uri.length()
              && uri.substring(uri.length()-suffix.length()).equals(suffix)) {
            return result;
          }
        }
      }
  
      // Otherwise, look in the subordinate catalogs
      return resolveSubordinateCatalogs(URI,
                                        null,
                                        null,
                                        uri);
    }
  
    /**
     * Return the applicable SYSTEM system identifier, resorting
     * to external RESOLVERs if necessary.
     *
     * <p>If a SYSTEM entry exists in the Catalog
     * for the system ID specified, return the mapped value.</p>
     *
     * <p>In the Resolver (as opposed to the Catalog) class, if the
     * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are
     * considered.</p>
     *
     * <p>On Windows-based operating systems, the comparison between
     * the system identifier provided and the SYSTEM entries in the
     * Catalog is case-insensitive.</p>
     *
     * @param systemId The system ID to locate in the catalog.
     *
     * @return The system identifier to use for systemId.
     *
     * @throws MalformedURLException The formal system identifier of a
     * subordinate catalog cannot be turned into a valid URL.
     * @throws IOException Error reading subordinate catalog file.
     */
    public String resolveSystem(String systemId)
      throws MalformedURLException, IOException {
  
      String resolved = super.resolveSystem(systemId);
      if (resolved != null) {
        return resolved;
      }
  
      Enumeration en = catalogEntries.elements();
      while (en.hasMoreElements()) {
        CatalogEntry e = (CatalogEntry) en.nextElement();
        if (e.getEntryType() == RESOLVER) {
          resolved = resolveExternalSystem(systemId, e.getEntryArg(0));
          if (resolved != null) {
            return resolved;
          }
        } else if (e.getEntryType() == SYSTEMSUFFIX) {
          String suffix = e.getEntryArg(0);
          String result = e.getEntryArg(1);
  
          if (suffix.length() <= systemId.length()
              && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) {
            return result;
          }
        }
      }
  
      return resolveSubordinateCatalogs(SYSTEM,
                                        null,
                                        null,
                                        systemId);
    }
  
    /**
     * Return the applicable PUBLIC or SYSTEM identifier, resorting
     * to external resolvers if necessary.
     *
     * <p>This method searches the Catalog and returns the system
     * identifier specified for the given system or
     * public identifiers. If
     * no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
     * null is returned.</p>
     *
     * <p>Note that a system or public identifier in the current catalog
     * (or subordinate catalogs) will be used in preference to an
     * external resolver. Further, if a systemId is present, the external
     * resolver(s) will be queried for that before the publicId.</p>
     *
     * @param publicId The public identifier to locate in the catalog.
     * Public identifiers are normalized before comparison.
     * @param systemId The nominal system identifier for the entity
     * in question (as provided in the source document).
     *
     * @throws MalformedURLException The formal system identifier of a
     * subordinate catalog cannot be turned into a valid URL.
     * @throws IOException Error reading subordinate catalog file.
     *
     * @return The system identifier to use.
     * Note that the nominal system identifier is not returned if a
     * match is not found in the catalog, instead null is returned
     * to indicate that no match was found.
     */
    public String resolvePublic(String publicId, String systemId)
      throws MalformedURLException, IOException {
  
      String resolved = super.resolvePublic(publicId, systemId);
      if (resolved != null) {
        return resolved;
      }
  
      Enumeration en = catalogEntries.elements();
      while (en.hasMoreElements()) {
        CatalogEntry e = (CatalogEntry) en.nextElement();
        if (e.getEntryType() == RESOLVER) {
          if (systemId != null) {
            resolved = resolveExternalSystem(systemId,
                                             e.getEntryArg(0));
            if (resolved != null) {
              return resolved;
            }
          }
          resolved = resolveExternalPublic(publicId, e.getEntryArg(0));
          if (resolved != null) {
            return resolved;
          }
        }
      }
  
      return resolveSubordinateCatalogs(PUBLIC,
                                        null,
                                        publicId,
                                        systemId);
    }
  
      /**
       * Query an external RFC2483 resolver for a system identifier.
       *
       * @param systemId The system ID to locate.
       * @param resolver The name of the resolver to use.
       *
       * @return The system identifier to use for the systemId.
       */
      protected String resolveExternalSystem(String systemId, String resolver)
          throws MalformedURLException, IOException {
          Resolver r = queryResolver(resolver, "i2l", systemId, null);
          if (r != null) {
              return r.resolveSystem(systemId);
          } else {
              return null;
          }
      }
  
      /**
       * Query an external RFC2483 resolver for a public identifier.
       *
       * @param publicId The system ID to locate.
       * @param resolver The name of the resolver to use.
       *
       * @return The system identifier to use for the systemId.
       */
      protected String resolveExternalPublic(String publicId, String resolver)
          throws MalformedURLException, IOException {
          Resolver r = queryResolver(resolver, "fpi2l", publicId, null);
          if (r != null) {
              return r.resolvePublic(publicId, null);
          } else {
              return null;
          }
      }
  
      /**
       * Query an external RFC2483 resolver.
       *
       * @param resolver The URL of the RFC2483 resolver.
       * @param command The command to send the resolver.
       * @param arg1 The first argument to the resolver.
       * @param arg2 The second argument to the resolver, usually null.
       *
       * @return The Resolver constructed.
       */
      protected Resolver queryResolver(String resolver,
                                       String command,
                                       String arg1,
                                       String arg2) {
          InputStream iStream = null;
          String RFC2483 = resolver + "?command=" + command
              + "&format=tr9401&uri=" + arg1
              + "&uri2=" + arg2;
          String line = null;
  
          try {
              URL url = new URL(RFC2483);
  
              URLConnection urlCon = url.openConnection();
  
              urlCon.setUseCaches(false);
  
              Resolver r = (Resolver) newCatalog();
  
              String cType = urlCon.getContentType();
  
              // I don't care about the character set or subtype
              if (cType.indexOf(";") > 0) {
                  cType = cType.substring(0, cType.indexOf(";"));
              }
  
              r.parseCatalog(cType, urlCon.getInputStream());
  
              return r;
          } catch (CatalogException cex) {
            if (cex.getExceptionType() == CatalogException.UNPARSEABLE) {
              catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483);
            } else if (cex.getExceptionType()
                       == CatalogException.UNKNOWN_FORMAT) {
              catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483);
            }
            return null;
          } catch (MalformedURLException mue) {
              catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483);
              return null;
          } catch (IOException ie) {
              catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483);
              return null;
          }
      }
  
      /**
       * Append two vectors, returning the result.
       *
       * @param vec The first vector
       * @param appvec The vector to be appended
       * @return The vector vec, with appvec's elements appended to it
       */
      private Vector appendVector(Vector vec, Vector appvec) {
          if (appvec != null) {
              for (int count = 0; count < appvec.size(); count++) {
                  vec.addElement(appvec.elementAt(count));
              }
          }
          return vec;
      }
  
      /**
       * Find the URNs for a given system identifier in all catalogs.
       *
       * @param systemId The system ID to locate.
       *
       * @return A vector of URNs that map to the systemId.
       */
      public Vector resolveAllSystemReverse(String systemId)
          throws MalformedURLException, IOException {
          Vector resolved = new Vector();
  
          // If there's a SYSTEM entry in this catalog, use it
          if (systemId != null) {
              Vector localResolved = resolveLocalSystemReverse(systemId);
              resolved = appendVector(resolved, localResolved);
          }
  
          // Otherwise, look in the subordinate catalogs
          Vector subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE,
                                                             null,
                                                             null,
                                                             systemId);
  
          return appendVector(resolved, subResolved);
      }
  
      /**
       * Find the URN for a given system identifier.
       *
       * @param systemId The system ID to locate.
       *
       * @return A (single) URN that maps to the systemId.
       */
      public String resolveSystemReverse(String systemId)
          throws MalformedURLException, IOException {
          Vector resolved = resolveAllSystemReverse(systemId);
          if (resolved != null && resolved.size() > 0) {
              return (String) resolved.elementAt(0);
          } else {
              return null;
          }
      }
  
      /**
       * Return the applicable SYSTEM system identifiers.
       *
       * <p>If one or more SYSTEM entries exists in the Catalog
       * for the system ID specified, return the mapped values.</p>
       *
       * <p>The caller is responsible for doing any necessary
       * normalization of the system identifier before calling
       * this method. For example, a relative system identifier in
       * a document might be converted to an absolute system identifier
       * before attempting to resolve it.</p>
       *
       * <p>Note that this function will force all subordinate catalogs
       * to be loaded.</p>
       *
       * <p>On Windows-based operating systems, the comparison between
       * the system identifier provided and the SYSTEM entries in the
       * Catalog is case-insensitive.</p>
       *
       * @param systemId The system ID to locate in the catalog.
       *
       * @return The system identifier to use for the notation.
       *
       * @throws MalformedURLException The formal system identifier of a
       * subordinate catalog cannot be turned into a valid URL.
       * @throws IOException Error reading subordinate catalog file.
       */
      public Vector resolveAllSystem(String systemId)
          throws MalformedURLException, IOException {
          Vector resolutions = new Vector();
  
          // If there are SYSTEM entries in this catalog, start with them
          if (systemId != null) {
              Vector localResolutions = resolveAllLocalSystem(systemId);
              resolutions = appendVector(resolutions, localResolutions);
          }
  
          // Then look in the subordinate catalogs
          Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM,
                                                                null,
                                                                null,
                                                                systemId);
          resolutions = appendVector(resolutions, subResolutions);
  
          if (resolutions.size() > 0) {
              return resolutions;
          } else {
              return null;
          }
      }
  
      /**
       * Return all applicable SYSTEM system identifiers in this
       * catalog.
       *
       * <p>If one or more SYSTEM entries exists in the catalog file
       * for the system ID specified, return the mapped values.</p>
       *
       * @param systemId The system ID to locate in the catalog
       *
       * @return A vector of the mapped system identifiers or null
       */
      private Vector resolveAllLocalSystem(String systemId) {
          Vector map = new Vector();
          String osname = SecuritySupport.getSystemProperty("os.name");
          boolean windows = (osname.indexOf("Windows") >= 0);
          Enumeration en = catalogEntries.elements();
          while (en.hasMoreElements()) {
              CatalogEntry e = (CatalogEntry) en.nextElement();
              if (e.getEntryType() == SYSTEM
                  && (e.getEntryArg(0).equals(systemId)
                      || (windows
                          && e.getEntryArg(0).equalsIgnoreCase(systemId)))) {
                  map.addElement(e.getEntryArg(1));
              }
          }
          if (map.size() == 0) {
              return null;
          } else {
              return map;
          }
      }
  
      /**
       * Find the URNs for a given system identifier in the current catalog.
       *
       * @param systemId The system ID to locate.
       *
       * @return A vector of URNs that map to the systemId.
       */
      private Vector resolveLocalSystemReverse(String systemId) {
          Vector map = new Vector();
          String osname = SecuritySupport.getSystemProperty("os.name");
          boolean windows = (osname.indexOf("Windows") >= 0);
          Enumeration en = catalogEntries.elements();
          while (en.hasMoreElements()) {
              CatalogEntry e = (CatalogEntry) en.nextElement();
              if (e.getEntryType() == SYSTEM
                  && (e.getEntryArg(1).equals(systemId)
                      || (windows
                          && e.getEntryArg(1).equalsIgnoreCase(systemId)))) {
                  map.addElement(e.getEntryArg(0));
              }
          }
          if (map.size() == 0) {
              return null;
          } else {
              return map;
          }
      }
  
      /**
       * Search the subordinate catalogs, in order, looking for all
       * match.
       *
       * <p>This method searches the Catalog and returns all of the system
       * identifiers specified for the given entity type with the given
       * name, public, and system identifiers. In some contexts, these
       * may be null.</p>
       *
       * @param entityType The CatalogEntry type for which this query is
       * being conducted. This is necessary in order to do the approprate
       * query on a subordinate catalog.
       * @param entityName The name of the entity being searched for, if
       * appropriate.
       * @param publicId The public identifier of the entity in question
       * (as provided in the source document).
       * @param systemId The nominal system identifier for the entity
       * in question (as provided in the source document).
       *
       * @throws MalformedURLException The formal system identifier of a
       * delegated catalog cannot be turned into a valid URL.
       * @throws IOException Error reading delegated catalog file.
       *
       * @return The system identifier to use.
       * Note that the nominal system identifier is not returned if a
       * match is not found in the catalog, instead null is returned
       * to indicate that no match was found.
       */
      private synchronized Vector resolveAllSubordinateCatalogs(int entityType,
                                                String entityName,
                                                String publicId,
                                                String systemId)
          throws MalformedURLException, IOException {
  
          Vector resolutions = new Vector();
  
          for (int catPos = 0; catPos < catalogs.size(); catPos++) {
              Resolver c = null;
  
              try {
                  c = (Resolver) catalogs.elementAt(catPos);
              } catch (ClassCastException e) {
                  String catfile = (String) catalogs.elementAt(catPos);
                  c = (Resolver) newCatalog();
  
                  try {
                      c.parseCatalog(catfile);
                  } catch (MalformedURLException mue) {
                      catalogManager.debug.message(1, "Malformed Catalog URL", catfile);
                  } catch (FileNotFoundException fnfe) {
                      catalogManager.debug.message(1, "Failed to load catalog, file not found",
                            catfile);
                  } catch (IOException ioe) {
                      catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile);
                  }
  
                  catalogs.setElementAt(c, catPos);
              }
  
              String resolved = null;
  
              // Ok, now what are we supposed to call here?
              if (entityType == DOCTYPE) {
                  resolved = c.resolveDoctype(entityName,
                                              publicId,
                                              systemId);
                  if (resolved != null) {
                      // Only find one DOCTYPE resolution
                      resolutions.addElement(resolved);
                      return resolutions;
                  }
              } else if (entityType == DOCUMENT) {
                  resolved = c.resolveDocument();
                  if (resolved != null) {
                      // Only find one DOCUMENT resolution
                      resolutions.addElement(resolved);
                      return resolutions;
                  }
              } else if (entityType == ENTITY) {
                  resolved = c.resolveEntity(entityName,
                                             publicId,
                                             systemId);
                  if (resolved != null) {
                      // Only find one ENTITY resolution
                      resolutions.addElement(resolved);
                      return resolutions;
                  }
              } else if (entityType == NOTATION) {
                  resolved = c.resolveNotation(entityName,
                                               publicId,
                                               systemId);
                  if (resolved != null) {
                      // Only find one NOTATION resolution
                      resolutions.addElement(resolved);
                      return resolutions;
                  }
              } else if (entityType == PUBLIC) {
                  resolved = c.resolvePublic(publicId, systemId);
                  if (resolved != null) {
                      // Only find one PUBLIC resolution
                      resolutions.addElement(resolved);
                      return resolutions;
                  }
              } else if (entityType == SYSTEM) {
                  Vector localResolutions = c.resolveAllSystem(systemId);
                  resolutions = appendVector(resolutions, localResolutions);
                  break;
              } else if (entityType == SYSTEMREVERSE) {
                  Vector localResolutions = c.resolveAllSystemReverse(systemId);
                  resolutions = appendVector(resolutions, localResolutions);
              }
          }
  
          if (resolutions != null) {
              return resolutions;
          } else {
              return null;
          }
      }
  }
  