/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.util.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import org.jboss.logging.Logger;
import org.jboss.util.xml.catalog.CatalogManager;
import org.jboss.util.xml.catalog.Resolver;
import org.xml.sax.InputSource;

/**
 * 
 * A ThreadSpecificCatalogs class maintains all catalogfiles <code>catolog.xml</code> found in the
 * <code>Thread.currentThread().getContextClassLoader()</code>.  
 * 
 * @author <a href="wiesed@gmail.com">Daniel Wiese</a>
 * @version $Revision: 2082 $
 */
public class CatalogLocation
{

   private static Logger log = Logger.getLogger(CatalogLocation.class);

   /** 
    * The catalog is assembled by taking into account all accessible resources whose name is 
    * META-INF/jax-ws-catalog.xml. Each resource MUST be a valid entity catalog according to the XML Catalogs
    */
   private static final String[] catalogFilesNames =
   {"META-INF/jax-ws-catalog.xml", "WEB-INF/jax-ws-catalog.xml", "jax-ws-catalog.xml"};

   private final Resolver catologResolver;
   
   private final URL location;

   private boolean isLastEntityResolved = false;

   static
   {
      // If the source document contains "oasis-xml-catalog" processing instructions,
      // should they be used?
      System.setProperty("xml.catalog.allowPI", "true");
      //Which identifier is preferred, "public" or "system"?
      System.setProperty("xml.catalog.prefer", "public");
      //If non-zero, the Catalog classes will print informative and debugging messages.
      //The higher the number, the more messages.
      System.setProperty("xml.catalog.verbosity", "0");
   }

   /**
    * Create a new CatalogLocation.
    * @param url - the location of the catalog xml file
    * @throws IOException if the catalog files cannot be loaded
    */
   public CatalogLocation(URL url) throws IOException
   {
      catologResolver = new Resolver();
      catologResolver.setCatalogManager(CatalogManager.getStaticManager());
      catologResolver.setupReaders();
      catologResolver.parseCatalog(url);
      this.location=url;
   }

   /**
    * Tries to resolve the entity using the thread specific catolog resolvers
    * 
    * @param publicId - Public ID of DTD, or null if it is a schema
    * @param systemId - the system ID of DTD or Schema
    * @return InputSource of entity
    * @throws MalformedURLException - if the url is wrong
    * @throws IOException - error reading the local file
    */
   public InputSource resolveEntity(String publicId, String systemId) throws MalformedURLException, IOException
   {
      String resolvedURI = catologResolver.resolveSystem(systemId);

      if (resolvedURI == null)
      {
         resolvedURI = catologResolver.resolvePublic(publicId, systemId);
      }

      if (resolvedURI != null)
      {
         final InputSource is = new InputSource();
         is.setPublicId(publicId);
         is.setSystemId(systemId);
         is.setByteStream(this.loadResource(resolvedURI));
         this.isLastEntityResolved = true;
         return is;
      }
      else
      {
         //resource could�t be resloved
         this.isLastEntityResolved = false;
         return null;
      }
   }

   /**
    * Seach the path for oasis catalog files. The classpath of
    * <code>Thread.currentThread().getContextClassLoader()</code>
    * is used for the lookup.
    * @return the url where the <code>jax-ws-catalog.xml</code> is located
    * @throws IOException if the catalog files cannot be loaded
    *
    */
   public static URL lookupCatalogFiles() throws IOException
   {
      URL url = null;
      //JAXWS-2.-0 spec, Line 27:the current context class loader MUST be used to
      //retrieve all the resources with the specified name
      ClassLoader loader = Thread.currentThread().getContextClassLoader();

      for (int i = 0; i < catalogFilesNames.length; i++)
      {
         url = loader.getResource(catalogFilesNames[i]);
         //use the first hit
         if (url != null)
         {
            break;
         }
      }

      return url;
   }

   /**
    * Returns the boolean value to inform id DTD was found in the XML file or not
    *
    *
    * @return boolean - true if DTD was found in XML
    */
   public boolean isEntityResolved()
   {
      return this.isLastEntityResolved;
   }

   /**
    * 
    * Loads the resolved resource.
    * 
    * @param resolvedURI - the full qualified URI of the resoved local ressource
    * @return - the inputstram represnting this resource
    * @throws IOException - if the resource cannot be opened
    */
   private InputStream loadResource(String resolvedURI) throws IOException
   {
      try
      {
         final URI toLoad = new URI(resolvedURI);
         InputStream inputStream = null;
         if (toLoad != null)
         {
            try
            {
               inputStream = new FileInputStream(new File(toLoad));
            }
            catch (IOException e)
            {
               log.error("Failed to open url stream", e);
               throw e;
            }
         }
         return inputStream;
      }
      catch (URISyntaxException e)
      {
         log.error("The URI (" + resolvedURI + ") is malfomed");
         throw new IOException("The URI (" + resolvedURI + ") is malfomed");
      }
   }

   /**
    * To catalog locations are qual if the location is equal.
    * @param other - the catlog location to compare
    * @return true if equal
    */
   public boolean equals(Object other)
   {
     boolean back=false;
      if (other!=null && other instanceof CatalogLocation){
         final CatalogLocation otherC=(CatalogLocation)other;
         back=this.location.equals(otherC.location);
      }
      
      return back;
   }

   /**
    * Two catalog locations have the same hash code if the location is equal.
    * @return - the hash code
    */
   public int hashCode()
   {
      return this.location.hashCode();
   }
   
   
}
