content: // CatalogManager.java - Access CatalogManager.properties
  
  /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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 mf.org.apache.xml.resolver;
  
  import java.io.InputStream;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.MissingResourceException;
  import java.util.PropertyResourceBundle;
  import java.util.ResourceBundle;
  import java.util.StringTokenizer;
  import java.util.Vector;
  
  import mf.org.apache.xml.resolver.helpers.BootstrapResolver;
  import mf.org.apache.xml.resolver.helpers.Debug;
  
  /**
   * CatalogManager provides an interface to the catalog properties.
   * <p/>
   * <p>Properties can come from two places: from system properties or
   * from a <i>CatalogManager.properties</i> file. This class provides a transparent
   * interface to both, with system properties preferred over property file values.</p>
   * <p/>
   * <p>The following table summarizes the properties:</p>
   * <p/>
   * <table border="1">
   * <thead>
   * <tr>
   * <td>System Property</td>
   * <td>CatalogManager.properties<br/>Property</td>
   * <td>Description</td>
   * </tr>
   * </thead>
   * <tbody>
   * <tr>
   * <td>xml.catalog.ignoreMissing</td>
   * <td>&#160;</td>
   * <td>If true, a missing <i>CatalogManager.properties</i> file or missing properties
   * within that file will not generate warning messages. See also the
   * <i>ignoreMissingProperties</i> method.</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.files</td>
   * <td>catalogs</td>
   * <td>The <emph>semicolon-delimited</emph> list of catalog files.</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>&#160;</td>
   * <td>relative-catalogs</td>
   * <td>If false, relative catalog URIs are made absolute with respect to the base URI of
   * the <i>CatalogManager.properties</i> file. This setting only applies to catalog
   * URIs obtained from the <i>catalogs</i> property <emph>in the</emph>
   * <i>CatalogManager.properties</i> file</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.verbosity</td>
   * <td>verbosity</td>
   * <td>If non-zero, the Catalog classes will print informative and debugging messages.
   * The higher the number, the more messages.</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.prefer</td>
   * <td>prefer</td>
   * <td>Which identifier is preferred, "public" or "system"?</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.staticCatalog</td>
   * <td>static-catalog</td>
   * <td>Should a single catalog be constructed for all parsing, or should a different
   * catalog be created for each parser?</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.allowPI</td>
   * <td>allow-oasis-xml-catalog-pi</td>
   * <td>If the source document contains "oasis-xml-catalog" processing instructions,
   * should they be used?</td>
   * </tr>
   * <p/>
   * <tr>
   * <td>xml.catalog.className</td>
   * <td>catalog-class-name</td>
   * <td>If you're using the convenience classes
   * <tt>org.apache.xml.resolver.tools.*</tt>), this setting
   * allows you to specify an alternate class name to use for the underlying
   * catalog.</td>
   * </tr>
   * </tbody>
   * </table>
   *
   * @author Norman Walsh
   *         <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
   * @version 1.0
   * @see Catalog
   */
  
  public class CatalogManager {
      private static String pFiles = "xml.catalog.files";
      private static String pVerbosity = "xml.catalog.verbosity";
      private static String pPrefer = "xml.catalog.prefer";
      private static String pStatic = "xml.catalog.staticCatalog";
      private static String pAllowPI = "xml.catalog.allowPI";
      private static String pClassname = "xml.catalog.className";
      private static String pIgnoreMissing = "xml.catalog.ignoreMissing";
  
      /**
       * A static CatalogManager instance for sharing
       */
      private static CatalogManager staticManager = new CatalogManager();
      /**
       * The static catalog used by this manager.
       */
      private static Catalog staticCatalog = null;
      /**
       * The manager's debug object. Used for printing debugging messages.
       * <p/>
       * <p>This field is public so that objects that have access to this
       * CatalogManager can use this debug object.</p>
       */
      public Debug debug = null;
      /**
       * The bootstrap resolver to use when loading XML Catalogs.
       */
      private BootstrapResolver bResolver = new BootstrapResolver();
      /**
       * Flag to ignore missing property files and/or properties
       */
      private boolean ignoreMissingProperties
              = (System.getProperty(pIgnoreMissing) != null
              || System.getProperty(pFiles) != null);
      /**
       * Holds the resources after they are loaded from the file.
       */
      private ResourceBundle resources;
      /**
       * The name of the CatalogManager properties file.
       */
      private String propertyFile = "CatalogManager.properties";
      /**
       * The location of the propertyFile
       */
      private URL propertyFileURI = null;
      /**
       * Default catalog files list.
       */
      private String defaultCatalogFiles = "./xcatalog";
      /**
       * Current catalog files list.
       */
      private String catalogFiles = null;
      /**
       * Did the catalogFiles come from the properties file?
       */
      private boolean fromPropertiesFile = false;
      /**
       * Default verbosity level if there is no property setting for it.
       */
      private int defaultVerbosity = 1;
      /**
       * Current verbosity level.
       */
      private Integer verbosity = null;
      /**
       * Default preference setting.
       */
      private boolean defaultPreferPublic = true;
      /**
       * Current preference setting.
       */
      private Boolean preferPublic = null;
      /**
       * Default setting of the static catalog flag.
       */
      private boolean defaultUseStaticCatalog = true;
      /**
       * Current setting of the static catalog flag.
       */
      private Boolean useStaticCatalog = null;
      /**
       * Default setting of the oasisXMLCatalogPI flag.
       */
      private boolean defaultOasisXMLCatalogPI = true;
      /**
       * Current setting of the oasisXMLCatalogPI flag.
       */
      private Boolean oasisXMLCatalogPI = null;
      /**
       * Default setting of the relativeCatalogs flag.
       */
      private boolean defaultRelativeCatalogs = true;
      /**
       * Current setting of the relativeCatalogs flag.
       */
      private Boolean relativeCatalogs = null;
      /**
       * Current catalog class name.
       */
      private String catalogClassName = null;
  
      /**
       * Constructor.
       */
      public CatalogManager() {
          debug = new Debug();
          // Note that we don't setDebug() here; we do that lazily. Either the
          // user will set it explicitly, or we'll do it automagically if they
          // read from the propertyFile for some other reason. That way, there's
          // no attempt to read from the file before the caller has had a chance
          // to avoid it.
      }
  
      /**
       * Constructor that specifies an explicit property file.
       */
      public CatalogManager(String propertyFile) {
          this.propertyFile = propertyFile;
  
          debug = new Debug();
          // Note that we don't setDebug() here; we do that lazily. Either the
          // user will set it explicitly, or we'll do it automagically if they
          // read from the propertyFile for some other reason. That way, there's
          // no attempt to read from the file before the caller has had a chance
          // to avoid it.
      }
  
      /**
       * Allow access to the static CatalogManager
       */
      public static CatalogManager getStaticManager() {
          return staticManager;
      }
  
      /**
       * Get the bootstrap resolver.
       */
      public BootstrapResolver getBootstrapResolver() {
          return bResolver;
      }
  
      /**
       * Set the bootstrap resolver.
       */
      public void setBootstrapResolver(BootstrapResolver resolver) {
          bResolver = resolver;
      }
  
      /**
       * Load the properties from the propertyFile and build the
       * resources from it.
       */
      private synchronized void readProperties() {
          try {
              propertyFileURI = CatalogManager.class.getResource("/" + propertyFile);
              InputStream in =
                      CatalogManager.class.getResourceAsStream("/" + propertyFile);
              if (in == null) {
                  if (!ignoreMissingProperties) {
                      System.err.println("Cannot find " + propertyFile);
                      // there's no reason to give this warning more than once
                      ignoreMissingProperties = true;
                  }
                  return;
              }
              resources = new PropertyResourceBundle(in);
          } catch (MissingResourceException mre) {
              if (!ignoreMissingProperties) {
                  System.err.println("Cannot read " + propertyFile);
              }
          } catch (java.io.IOException e) {
              if (!ignoreMissingProperties) {
                  System.err.println("Failure trying to read " + propertyFile);
              }
          }
  
          // This is a bit of a hack. After we've successfully read the properties,
          // use them to set the default debug level, if the user hasn't already set
          // the default debug level.
          if (verbosity == null) {
              try {
                  String verbStr = resources.getString("verbosity");
                  int verb = Integer.parseInt(verbStr.trim());
                  debug.setDebug(verb);
                  verbosity = new Integer(verb);
              } catch (Exception e) {
                  // nop
              }
          }
      }
  
      /**
       * How are missing properties handled?
       * <p/>
       * <p>If true, missing or unreadable property files will
       * not be reported. Otherwise, a message will be sent to System.err.
       * </p>
       */
      public boolean getIgnoreMissingProperties() {
          return ignoreMissingProperties;
      }
  
      /**
       * How should missing properties be handled?
       * <p/>
       * <p>If ignore is true, missing or unreadable property files will
       * not be reported. Otherwise, a message will be sent to System.err.
       * </p>
       */
      public void setIgnoreMissingProperties(boolean ignore) {
          ignoreMissingProperties = ignore;
      }
  
      /**
       * How are missing properties handled?
       * <p/>
       * <p>If ignore is true, missing or unreadable property files will
       * not be reported. Otherwise, a message will be sent to System.err.
       * </p>
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public void ignoreMissingProperties(boolean ignore) {
          setIgnoreMissingProperties(ignore);
      }
  
      /**
       * Obtain the verbosity setting from the properties.
       *
       * @return The verbosity level from the propertyFile or the
       * defaultVerbosity.
       */
      private int queryVerbosity() {
          String defaultVerbStr = Integer.toString(defaultVerbosity);
  
          String verbStr = System.getProperty(pVerbosity);
  
          if (verbStr == null) {
              if (resources == null) readProperties();
              if (resources != null) {
                  try {
                      verbStr = resources.getString("verbosity");
                  } catch (MissingResourceException e) {
                      verbStr = defaultVerbStr;
                  }
              } else {
                  verbStr = defaultVerbStr;
              }
          }
  
          int verb = defaultVerbosity;
  
          try {
              verb = Integer.parseInt(verbStr.trim());
          } catch (Exception e) {
              System.err.println("Cannot parse verbosity: \"" + verbStr + "\"");
          }
  
          // This is a bit of a hack. After we've successfully got the verbosity,
          // we have to use it to set the default debug level,
          // if the user hasn't already set the default debug level.
          if (verbosity == null) {
              debug.setDebug(verb);
              verbosity = new Integer(verb);
          }
  
          return verb;
      }
  
      /**
       * What is the current verbosity?
       */
      public int getVerbosity() {
          if (verbosity == null) {
              verbosity = new Integer(queryVerbosity());
          }
  
          return verbosity.intValue();
      }
  
      /**
       * Set the current verbosity.
       */
      public void setVerbosity(int verbosity) {
          this.verbosity = new Integer(verbosity);
          debug.setDebug(verbosity);
      }
  
      /**
       * What is the current verbosity?
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public int verbosity() {
          return getVerbosity();
      }
  
      /**
       * Obtain the relativeCatalogs setting from the properties.
       *
       * @return The relativeCatalogs setting from the propertyFile or the
       * defaultRelativeCatalogs.
       */
      private boolean queryRelativeCatalogs() {
          if (resources == null) readProperties();
  
          if (resources == null) return defaultRelativeCatalogs;
  
          try {
              String allow = resources.getString("relative-catalogs");
              return (allow.equalsIgnoreCase("true")
                      || allow.equalsIgnoreCase("yes")
                      || allow.equalsIgnoreCase("1"));
          } catch (MissingResourceException e) {
              return defaultRelativeCatalogs;
          }
      }
  
      /**
       * Get the relativeCatalogs setting.
       * <p/>
       * <p>This property is used when the catalogFiles property is
       * interrogated. If true, then relative catalog entry file names
       * are returned. If false, relative catalog entry file names are
       * made absolute with respect to the properties file before returning
       * them.</p>
       * <p/>
       * <p>This property <emph>only applies</emph> when the catalog files
       * come from a properties file. If they come from a system property or
       * the default list, they are never considered relative. (What would
       * they be relative to?)</p>
       * <p/>
       * <p>In the properties, a value of 'yes', 'true', or '1' is considered
       * true, anything else is false.</p>
       *
       * @return The relativeCatalogs setting from the propertyFile or the
       * defaultRelativeCatalogs.
       */
      public boolean getRelativeCatalogs() {
          if (relativeCatalogs == null) {
              relativeCatalogs = new Boolean(queryRelativeCatalogs());
          }
  
          return relativeCatalogs.booleanValue();
      }
  
      /**
       * Set the relativeCatalogs setting.
       *
       * @see #getRelativeCatalogs()
       */
      public void setRelativeCatalogs(boolean relative) {
          relativeCatalogs = new Boolean(relative);
      }
  
      /**
       * Get the relativeCatalogs setting.
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public boolean relativeCatalogs() {
          return getRelativeCatalogs();
      }
  
      /**
       * Obtain the list of catalog files from the properties.
       *
       * @return A semicolon delimited list of catlog file URIs
       */
      private String queryCatalogFiles() {
          String catalogList = System.getProperty(pFiles);
          fromPropertiesFile = false;
  
          if (catalogList == null) {
              if (resources == null) readProperties();
              if (resources != null) {
                  try {
                      catalogList = resources.getString("catalogs");
                      fromPropertiesFile = true;
                  } catch (MissingResourceException e) {
                      System.err.println(propertyFile + ": catalogs not found.");
                      catalogList = null;
                  }
              }
          }
  
          if (catalogList == null) {
              catalogList = defaultCatalogFiles;
          }
  
          return catalogList;
      }
  
      /**
       * Return the current list of catalog files.
       *
       * @return A vector of the catalog file names or null if no catalogs
       * are available in the properties.
       */
      public Vector getCatalogFiles() {
          if (catalogFiles == null) {
              catalogFiles = queryCatalogFiles();
          }
  
          StringTokenizer files = new StringTokenizer(catalogFiles, ";");
          Vector catalogs = new Vector();
          while (files.hasMoreTokens()) {
              String catalogFile = files.nextToken();
              URL absURI = null;
  
              if (fromPropertiesFile && !relativeCatalogs()) {
                  try {
                      absURI = new URL(propertyFileURI, catalogFile);
                      catalogFile = absURI.toString();
                  } catch (MalformedURLException mue) {
                      absURI = null;
                  }
              }
  
              catalogs.add(catalogFile);
          }
  
          return catalogs;
      }
  
      /**
       * Set the list of catalog files.
       */
      public void setCatalogFiles(String fileList) {
          catalogFiles = fileList;
          fromPropertiesFile = false;
      }
  
      /**
       * Return the current list of catalog files.
       *
       * @return A vector of the catalog file names or null if no catalogs
       * are available in the properties.
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public Vector catalogFiles() {
          return getCatalogFiles();
      }
  
      /**
       * Obtain the preferPublic setting from the properties.
       * <p/>
       * <p>In the properties, a value of 'public' is true,
       * anything else is false.</p>
       *
       * @return True if prefer is public or the
       * defaultPreferSetting.
       */
      private boolean queryPreferPublic() {
          String prefer = System.getProperty(pPrefer);
  
          if (prefer == null) {
              if (resources == null) readProperties();
              if (resources == null) return defaultPreferPublic;
              try {
                  prefer = resources.getString("prefer");
              } catch (MissingResourceException e) {
                  return defaultPreferPublic;
              }
          }
  
          if (prefer == null) {
              return defaultPreferPublic;
          }
  
          return (prefer.equalsIgnoreCase("public"));
      }
  
      /**
       * Return the current prefer public setting.
       *
       * @return True if public identifiers are preferred.
       */
      public boolean getPreferPublic() {
          if (preferPublic == null) {
              preferPublic = new Boolean(queryPreferPublic());
          }
          return preferPublic.booleanValue();
      }
  
      /**
       * Set the prefer public setting.
       */
      public void setPreferPublic(boolean preferPublic) {
          this.preferPublic = new Boolean(preferPublic);
      }
  
      /**
       * Return the current prefer public setting.
       *
       * @return True if public identifiers are preferred.
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public boolean preferPublic() {
          return getPreferPublic();
      }
  
      /**
       * Obtain the static-catalog setting from the properties.
       * <p/>
       * <p>In the properties, a value of 'yes', 'true', or '1' is considered
       * true, anything else is false.</p>
       *
       * @return The static-catalog setting from the propertyFile or the
       * defaultUseStaticCatalog.
       */
      private boolean queryUseStaticCatalog() {
          String staticCatalog = System.getProperty(pStatic);
  
          if (staticCatalog == null) {
              if (resources == null) readProperties();
              if (resources == null) return defaultUseStaticCatalog;
              try {
                  staticCatalog = resources.getString("static-catalog");
              } catch (MissingResourceException e) {
                  return defaultUseStaticCatalog;
              }
          }
  
          if (staticCatalog == null) {
              return defaultUseStaticCatalog;
          }
  
          return (staticCatalog.equalsIgnoreCase("true")
                  || staticCatalog.equalsIgnoreCase("yes")
                  || staticCatalog.equalsIgnoreCase("1"));
      }
  
      /**
       * Get the current use static catalog setting.
       */
      public boolean getUseStaticCatalog() {
          if (useStaticCatalog == null) {
              useStaticCatalog = new Boolean(queryUseStaticCatalog());
          }
  
          return useStaticCatalog.booleanValue();
      }
  
      /**
       * Set the use static catalog setting.
       */
      public void setUseStaticCatalog(boolean useStatic) {
          useStaticCatalog = new Boolean(useStatic);
      }
  
      /**
       * Get the current use static catalog setting.
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public boolean staticCatalog() {
          return getUseStaticCatalog();
      }
  
      /**
       * Get a new catalog instance.
       * <p/>
       * This method always returns a new instance of the underlying catalog class.
       */
      public Catalog getPrivateCatalog() {
          Catalog catalog = staticCatalog;
  
          if (useStaticCatalog == null) {
              useStaticCatalog = new Boolean(getUseStaticCatalog());
          }
  
          if (catalog == null || !useStaticCatalog.booleanValue()) {
  
              try {
                  String catalogClassName = getCatalogClassName();
  
                  if (catalogClassName == null) {
                      catalog = new Catalog();
                  } else {
                      try {
                          catalog = (Catalog) Class.forName(catalogClassName).newInstance();
                      } catch (ClassNotFoundException cnfe) {
                          debug.message(1, "Catalog class named '"
                                  + catalogClassName
                                  + "' could not be found. Using default.");
                          catalog = new Catalog();
                      } catch (ClassCastException cnfe) {
                          debug.message(1, "Class named '"
                                  + catalogClassName
                                  + "' is not a Catalog. Using default.");
                          catalog = new Catalog();
                      }
                  }
  
                  catalog.setCatalogManager(this);
                  catalog.setupReaders();
                  catalog.loadSystemCatalogs();
              } catch (Exception ex) {
                  ex.printStackTrace();
              }
  
              if (useStaticCatalog.booleanValue()) {
                  staticCatalog = catalog;
              }
          }
  
          return catalog;
      }
  
      /**
       * Get a catalog instance.
       * <p/>
       * If this manager uses static catalogs, the same static catalog will
       * always be returned. Otherwise a new catalog will be returned.
       */
      public Catalog getCatalog() {
          Catalog catalog = staticCatalog;
  
          if (useStaticCatalog == null) {
              useStaticCatalog = new Boolean(getUseStaticCatalog());
          }
  
          if (catalog == null || !useStaticCatalog.booleanValue()) {
              catalog = getPrivateCatalog();
              if (useStaticCatalog.booleanValue()) {
                  staticCatalog = catalog;
              }
          }
  
          return catalog;
      }
  
      /**
       * <p>Obtain the oasisXMLCatalogPI setting from the properties.</p>
       * <p/>
       * <p>In the properties, a value of 'yes', 'true', or '1' is considered
       * true, anything else is false.</p>
       *
       * @return The oasisXMLCatalogPI setting from the propertyFile or the
       * defaultOasisXMLCatalogPI.
       */
      public boolean queryAllowOasisXMLCatalogPI() {
          String allow = System.getProperty(pAllowPI);
  
          if (allow == null) {
              if (resources == null) readProperties();
              if (resources == null) return defaultOasisXMLCatalogPI;
              try {
                  allow = resources.getString("allow-oasis-xml-catalog-pi");
              } catch (MissingResourceException e) {
                  return defaultOasisXMLCatalogPI;
              }
          }
  
          if (allow == null) {
              return defaultOasisXMLCatalogPI;
          }
  
          return (allow.equalsIgnoreCase("true")
                  || allow.equalsIgnoreCase("yes")
                  || allow.equalsIgnoreCase("1"));
      }
  
      /**
       * Get the current XML Catalog PI setting.
       */
      public boolean getAllowOasisXMLCatalogPI() {
          if (oasisXMLCatalogPI == null) {
              oasisXMLCatalogPI = new Boolean(queryAllowOasisXMLCatalogPI());
          }
  
          return oasisXMLCatalogPI.booleanValue();
      }
  
      /**
       * Set the XML Catalog PI setting
       */
      public void setAllowOasisXMLCatalogPI(boolean allowPI) {
          oasisXMLCatalogPI = new Boolean(allowPI);
      }
  
      /**
       * Get the current XML Catalog PI setting.
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public boolean allowOasisXMLCatalogPI() {
          return getAllowOasisXMLCatalogPI();
      }
  
      /**
       * Obtain the Catalog class name setting from the properties.
       */
      public String queryCatalogClassName() {
          String className = System.getProperty(pClassname);
  
          if (className == null) {
              if (resources == null) readProperties();
              if (resources == null) return null;
              try {
                  return resources.getString("catalog-class-name");
              } catch (MissingResourceException e) {
                  return null;
              }
          }
  
          return className;
      }
  
      /**
       * Get the current Catalog class name.
       */
      public String getCatalogClassName() {
          if (catalogClassName == null) {
              catalogClassName = queryCatalogClassName();
          }
  
          return catalogClassName;
      }
  
      /**
       * Set the Catalog class name.
       */
      public void setCatalogClassName(String className) {
          catalogClassName = className;
      }
  
      /**
       * Get the current Catalog class name.
       *
       * @deprecated No longer static; use get/set methods.
       */
      @Deprecated
      public String catalogClassName() {
          return getCatalogClassName();
      }
  }
  