/*
 * 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 org.apache.uima.resource;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import org.apache.uima.resource.impl.ResourceManager_impl;
import org.apache.uima.resource.metadata.ResourceManagerConfiguration;
import org.apache.uima.util.XMLizable;

/**
 * A <code>ResourceManager</code> holds a collection of {@link org.apache.uima.resource.Resource}
 * objects, each registered under a specified key.
 * 
 * 
 */
public interface ResourceManager {

  /**
   * Gets the data path used to resolve relative paths. More than one directory may be specified by
   * separating them with the System <code>path.separator</code> character (; on windows, : on
   * UNIX).
   * 
   * @return the data path
   * 
   * @deprecated Use {@link #getDataPathElements()} instead.
   */
  @Deprecated
  String getDataPath();

  /**
   * Gets the data path elements used to resolve relative paths.
   * 
   * @return the data path elements
   */
  List<String> getDataPathElements();

  /**
   * Sets the data path used to resolve relative paths. More than one directory may be specified by
   * separating them with the System <code>path.separator</code> character (; on windows, : on
   * UNIX). Elements of this path may be absolute or relative file paths.
   * 
   * @param aPath
   *          the data path
   * 
   * @throws MalformedURLException
   *           if an element of the path is neither a valid URL or a valid file path
   * @deprecated Use {@link #setDataPathElements} instead.
   */
  @Deprecated
  void setDataPath(String aPath) throws MalformedURLException;

  /**
   * Sets the data path elements used to resolve relative paths. Elements of this path may be
   * absolute or relative file paths.
   * 
   * @param aElements
   *          the data path
   * 
   * @throws MalformedURLException
   *           if an element of the path is neither a valid URL or a valid file path
   */
  void setDataPathElements(String... aElements) throws MalformedURLException;

  /**
   * Sets the data path elements used to resolve relative paths. Elements of this path may be
   * absolute or relative file paths.
   * 
   * @param aElements
   *          the data path
   * 
   * @throws MalformedURLException
   *           if an element of the path is neither a valid URL or a valid file path
   */
  void setDataPathElements(File... aElements) throws MalformedURLException;

  /**
   * Attempts to resolve a relative path to an absolute path using the same mechanism that the
   * ResourceManager uses to find resources -- a lookup in the datapath followed by a lookup in the
   * classpath.
   * 
   * @param aRelativePath
   *          a relative URL or file path
   * 
   * @return the absolute URL of an actual file in the datapath or classpath, null if no file
   *         matching <code>aRelativePath</code> is found.
   * @throws MalformedURLException
   *           if the path cannot be converted to a URL
   */
  URL resolveRelativePath(String aRelativePath) throws MalformedURLException;

  /**
   * Gets the instance of the implementation object for a resource that has been registered under
   * the specified name.
   * 
   * @param aName
   *          the name of the resource to retrieve
   * 
   * @return the instance of the implementation object for the resource, registered under
   *         <code>aName</code>, <code>null</code> if none exists.
   * 
   * @throws ResourceAccessException
   *           if the requested resource could not be initialized. A common cause is that it
   *           requires parameters and the {@link #getResource(String,String[])} method should have
   *           been called instead of this method.
   * @throws ResourceAccessException
   *           tbd
   */
  Object getResource(String aName) throws ResourceAccessException;

//@formatter:off
  /**
   * Returns one of two kinds of objects (or null):
   *   - an instance of the implementation object for a resource, that has 
   *     been loaded with a DataResource resource produced by the resource given the aParms
   *     
   *   - (if there is no implementation defined for this resource) 
   *     returns an instance of the DataResource, itself, produced by the resource given the aParms
   *    
   *   An example of a parameterized Resource is a
   *     dictionary whose data depend on a specified language identifier.
   *   
   *   If the implementation object class exists, but no instance has been 
   *   created (yet) for the particular data resource corresponding to the parameters,
   *   then this method will create and register a new instance and call its
   *   load() api using the data resource corresponding to the parameters, and
   *   return that.
   * 
   * @param aName
   *          the name of the parameterized resource to retrieve
   * @param aParams
   *          the parameters determining which particular instance is returned and specifying a
   *          particular DataResource instance to use in initializing the implementation of the
   *          resource (if there is an implementation).
   * 
   *          If there is no implementation, the DataResource instance produced from the named
   *          Resource given these parameters is returned instead.
   * 
   * @return one of two kinds of objects (or null): an instance of the requested implementation of
   *         the named resource where that instance has been initialized by calling its load method
   *         with the DataResource instance produced from the Resource given aParams,
   * 
   *         or, (if the named resource has no implementation) the DataResource instance
   *         corresponding to the named Resource, given aParams,
   * 
   *         or if no resource with this name exists, <code>null</code>.
   * 
   * @throws ResourceAccessException
   *           if there is a resource registered under <code>aName</code> but it could not be
   *           instantiated for the specified parameters.
   */
//@formatter:on
  Object getResource(String aName, String[] aParams) throws ResourceAccessException;

  /**
   * Gets the Class of the Resource that will be returned by a call to {@link #getResource(String)}
   * or {@link #getResource(String,String[])}.
   * 
   * For those resource specifications which include an implementation class, this call returns that
   * class.
   * 
   * @param aName
   *          the name of a resource
   * @param <N>
   *          The generic type for the returned class
   * 
   * @return the Class for the resource named <code>aName</code>, <code>null</code> if there is no
   *         resource registered under that name.
   */
  <N> Class<N> getResourceClass(String aName);

  /**
   * Retrieves the URL to the named resource. This can be used, for example, to locate configuration
   * or authority files.
   * 
   * @param aKey
   *          the key by which the resource is identified. If this key was declared in the
   *          &lt;externalResourceDependencies&gt; section of the descriptor, then the resource
   *          manager is used to locate the resource. If not, the key is assumed to be the resource
   *          name and is looked up in the {@link #getDataPath() data path} or in the class path
   *          using {@link java.lang.ClassLoader#getResource(String)}.
   * 
   * @return the <code>URL</code> at which the named resource is located, <code>null</code> if the
   *         named resource could not be found.
   * 
   * @throws ResourceAccessException
   *           if a failure occurs in accessing the resource
   */
  URL getResourceURL(String aKey) throws ResourceAccessException;

  /**
   * Retrieves an InputStream for reading from the named resource. This can be used, for example, to
   * read configuration or authority files.
   * 
   * @param aKey
   *          the key by which the resource is identified. If this key was declared in the
   *          &lt;externalResourceDependencies&gt; section of the annotator's descriptor, then the
   *          resource manager is used to locate the resource. If not, the key is assumed to be the
   *          resource name and is looked up in the {@link #getDataPath() data path} or in the class
   *          path using {@link java.lang.ClassLoader#getResource(String)}.
   * 
   * @return an <code>InputStream</code> for reading from the named resource, <code>null</code> if
   *         the named resource could not be found. It is the caller's responsibility to close this
   *         stream once it is no longer needed.
   * 
   * @throws ResourceAccessException
   *           if a failure occurs in accessing the resource
   */
  InputStream getResourceAsStream(String aKey) throws ResourceAccessException;

  /**
   * Retrieves the URL to the named resource. This can be used, for example, to locate configuration
   * or authority files. This version of this method takes an array of parameters used to further
   * identify the resource. This can be used, for example, with resources that vary depending on the
   * language of the document being analyzed.
   * 
   * @param aKey
   *          the key by which the resource is identified. If this key was declared in the
   *          &lt;externalResourceDependencies&gt; section of the annotator's descriptor, then the
   *          resource manager is used to locate the resource. If not, the key is assumed to be the
   *          resource name and is looked up in the {@link #getDataPath() data path} or in the class
   *          path using {@link java.lang.ClassLoader#getResource(String)}.
   * @param aParams
   *          parameters used to further identify the resource
   * 
   * @return the <code>URL</code> at which the named resource is located, <code>null</code> if the
   *         named resource could not be found.
   * 
   * @throws ResourceAccessException
   *           if a failure occurs in accessing the resource
   */
  URL getResourceURL(String aKey, String[] aParams) throws ResourceAccessException;

  /**
   * Retrieves an InputStream for reading from the named resource. This can be used, for example, to
   * read configuration or authority files. This version of this method takes an array of parameters
   * used to further identify the resource. This can be used, for example, with resources that vary
   * depending on the language of the document being analyzed.
   * 
   * @param aKey
   *          the key by which the resource is identified. If this key was declared in the
   *          &lt;externalResourceDependencies&gt; section of the annotator's descriptor, then the
   *          resource manager is used to locate the resource. If not, the key is assumed to be the
   *          resource name and is looked up in the {@link #getDataPath() data path} or in the class
   *          path using {@link java.lang.ClassLoader#getResource(String)}.
   * @param aParams
   *          parameters used to further identify the resource
   * 
   * @return an <code>InputStream</code> for reading from the named resource, <code>null</code> if
   *         the named resource could not be found. It is the caller's responsibility to close this
   *         stream once it is no longer needed.
   * 
   * @throws ResourceAccessException
   *           if a failure occurs in accessing the resource
   */
  InputStream getResourceAsStream(String aKey, String[] aParams) throws ResourceAccessException;

  /**
   * Initializes all external resources declared in a ResourceCreationSpecifier. Multi-threading:
   * may be called on multiple threads.
   * 
   * Initialization should be done once, on the first call
   * 
   * External resources have a Container class representing the resource, which are instances of
   * Resource.
   * 
   * This may act as the implementation class, or they may also have a separately specified
   * implementation class, which may or may not implement Resource.
   * 
   * As part of the initialization of the Container class, by default, External Resource Bindings
   * are processed to hook them up with defined External Resources, using the default implementation
   * of resolveAndValidateResourceDependencies.
   * 
   * @param aConfiguration
   *          the ResourceManagerConfiguration containing resource declarations and bindings
   * @param aQualifiedContextName
   *          qualified name of UimaContext for the component (e.g. analysis engine) that is
   *          declaring these external resources
   * @param aAdditionalParams
   *          additional parameters to be passed to resource initialize methods
   * 
   * @throws ResourceInitializationException
   *           if an initialization failure occurs
   */
  void initializeExternalResources(ResourceManagerConfiguration aConfiguration,
          String aQualifiedContextName, Map<String, Object> aAdditionalParams)
          throws ResourceInitializationException;

//@formatter:off
  /**
   * Resolves a component's external resource dependencies (bindings) using this resource manager.
   * 
   * The default implementation has special defaulting logic:
   * 
   *   If a binding specifies a non-existing resource, 
   *   an attempt is made to interpret the key as a file name, looked up 
   *   using the current context for relative path resolution.  
   *     - If successfully found, a FileResourceSpecifier is created using the file
   *       and used as the implementing class. 
   * 
   * If no resource can be found at all, then unless the dependency is marked "optional", an
   * ResourceInitializationException is thrown.
   * 
   * Multi-threading: may be called on multiple threads, repeatedly for the same set of resources.
   * Implementations should recognize this and skip repeated resolutions.
   * 
   * @param aDependencies
   *          declarations of a component's dependencies on external resources
   * @param aQualifiedContextName
   *          qualified name of UimaContext for the component (e.g. analysis engine) that is
   *          declaring these dependencies
   * 
   * @throws ResourceInitializationException
   *           if a required dependency is not satisfied
   */
//@formatter:on
  void resolveAndValidateResourceDependencies(ExternalResourceDependency[] aDependencies,
          String aQualifiedContextName) throws ResourceInitializationException;

  /**
   * Sets the classpath for the UIMA extension ClassLoader and specifies if the extension
   * ClassLoader should also be used to resolve resources.
   * 
   * @param classpath
   *          extension ClassLoader classpath
   * @param resolveResource
   *          if true ClassLoad resolves resources
   * 
   * @throws MalformedURLException
   *           if a malformed URL has occurred in the classpath string.
   */
  void setExtensionClassPath(String classpath, boolean resolveResource)
          throws MalformedURLException;

  /**
   * Sets the classpath for the UIMA extension ClassLoader and specifies if the extension
   * ClassLoader should also be used to resolve resources. Also allows a parent ClassLoader to be
   * specified.
   * 
   * @param parent
   *          parent ClassLoader for the extension ClassLoader
   * @param classpath
   *          extension ClassLoader classpath
   * @param resolveResource
   *          if true ClassLoad resolves resources
   * 
   * @throws MalformedURLException
   *           if a malformed URL has occurred in the classpath string.
   */
  void setExtensionClassPath(ClassLoader parent, String classpath, boolean resolveResource)
          throws MalformedURLException;

  /**
   * Set an extension class loader into the Resource Manager
   * 
   * @param classLoader
   *          the loader to use. If this is an instance of UIMAClassLoader, it is used directly;
   *          otherwise, a new UIMAClassLoader with no classpath, having the classLoader as a parent
   *          is created and used.
   * @param resolveResources
   *          true to also use this to resolve resources
   */
  default void setExtensionClassLoader(ClassLoader classLoader, boolean resolveResources) {
    ((ResourceManager_impl) this).setExtensionClassLoaderImpl(classLoader, resolveResources);
  }

  /**
   * Returns the UIMA extension class loader.
   * 
   * @return ClassLoader - returns the UIMA extension class loader of null if it is not available.
   */
  ClassLoader getExtensionClassLoader();

  /**
   * Gets the CasManager, which manages the creation and pooling of CASes.
   * 
   * @return the CasManager
   */
  CasManager getCasManager();

  /**
   * Sets the CasManager, which manages the creation and pooling of CASes. This method does not
   * normally need to be called by an application. It allows a custom CAS Manager implementation to
   * be substituted for the default one, which may be useful when embedding UIMA in other middleware
   * where a different CAS Manager implementation may be desired.
   * <p>
   * This method can only be called once, and must be called before creating any AnalysisEngines
   * that use this ResourceManager. An Exception will be thrown if this method is called twice or is
   * called after {@link #getCasManager()} has already been called (which happens during AE
   * initialization).
   * 
   * @param aCasManager
   *          CAS Manager instance to plug in
   */
  void setCasManager(CasManager aCasManager);

  /**
   * Gets a cache of imported descriptors, so that the parsed objects can be reused if the same URL
   * is imported more than once.
   * 
   * @return A map from absolute URL to the XMLizable object that was parsed from that URL
   * @deprecated Intended just for internal use.
   */
  @Deprecated
  Map<String, XMLizable> getImportCache();

  /**
   * Loads a user class using either the UIMA extension class loader (if specified) or the
   * ThreadLocal Context Class Loader (if available) or the loader the UIMA framework is running in.
   * 
   * If the class is not found in the ThreadLocal COntext Class Loader, then the loader the UIMA
   * framework is running in will be searched.
   * 
   * @param name
   *          the class to load
   * @param <N>
   *          generic class of class
   * @return the class
   * @throws ClassNotFoundException
   *           -
   */
  <N> Class<N> loadUserClass(String name) throws ClassNotFoundException;

  /**
   * Frees all resources held by this ResourceManager, and marks the ResourceManager as having been
   * destroyed. A destroyed ResourceManager will throw an exception if an attempt is made to
   * continue using it.
   * 
   * Resources managed by a ResourceManager include all of the external shared Resources. If there
   * is an extension class loader in use, it will be closed. Any class loaders (including PEAR class
   * loaders) that might use this as a parent will be affected by this closure.
   * 
   * Any streams opened using getResourceAsStream will also be closed.
   * 
   * The Resources managed by this manager will have their destroy() methods called, as part of the
   * execution of this API.
   * 
   * The framework does not call this method; it is up to the containing application to decide if
   * and when a ResourceManager instance should be destroyed. This is because the containing
   * application is the only knowledgeable source; for example a single ResourceManager might be
   * used for multiple UIMA Pipelines.
   */
  void destroy();

  /**
   * 
   * @return a List of External Shared Resource instances instantiated by this Resource Manager. For
   *         parameterized resources, those which have been asked for (having unique parameter sets)
   *         are included.
   */
  List<Object> getExternalResources();
}
