Package org.apache.uima.fit.factory

Source Code of org.apache.uima.fit.factory.ExternalResourceFactory

/*
* 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.fit.factory;

import static java.util.Arrays.asList;
import static org.apache.uima.fit.factory.ConfigurationParameterFactory.canParameterBeSet;
import static org.apache.uima.fit.factory.ConfigurationParameterFactory.createConfigurationData;

import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang.ArrayUtils;
import org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.collection.CollectionReaderDescription;
import org.apache.uima.fit.descriptor.ExternalResource;
import org.apache.uima.fit.factory.ConfigurationParameterFactory.ConfigurationData;
import org.apache.uima.fit.internal.ExtendedExternalResourceDescription_impl;
import org.apache.uima.fit.internal.ReflectionUtil;
import org.apache.uima.fit.internal.ResourceList;
import org.apache.uima.resource.ConfigurableDataResourceSpecifier;
import org.apache.uima.resource.CustomResourceSpecifier;
import org.apache.uima.resource.DataResource;
import org.apache.uima.resource.ExternalResourceDependency;
import org.apache.uima.resource.ExternalResourceDescription;
import org.apache.uima.resource.FileResourceSpecifier;
import org.apache.uima.resource.Parameter;
import org.apache.uima.resource.ParameterizedDataResource;
import org.apache.uima.resource.Resource;
import org.apache.uima.resource.ResourceCreationSpecifier;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.SharedResourceObject;
import org.apache.uima.resource.impl.ConfigurableDataResourceSpecifier_impl;
import org.apache.uima.resource.impl.ConfigurableDataResource_impl;
import org.apache.uima.resource.impl.ExternalResourceDependency_impl;
import org.apache.uima.resource.impl.ExternalResourceDescription_impl;
import org.apache.uima.resource.impl.FileResourceSpecifier_impl;
import org.apache.uima.resource.impl.Parameter_impl;
import org.apache.uima.resource.metadata.ExternalResourceBinding;
import org.apache.uima.resource.metadata.ResourceManagerConfiguration;
import org.apache.uima.resource.metadata.impl.ExternalResourceBinding_impl;
import org.apache.uima.resource.metadata.impl.ResourceManagerConfiguration_impl;
import org.apache.uima.resource.metadata.impl.ResourceMetaData_impl;
import org.apache.uima.util.InvalidXMLException;

/**
* Helper methods for external resources.
*
*/
public final class ExternalResourceFactory {
  public static final String PARAM_RESOURCE_NAME = "__UIMAFIT_RESOURCE_NAME__";

  /**
   * Used to separate resource name from key for nested resource.
   */
  public static final String PREFIX_SEPARATOR = "##";

  /**
   * Counter used to create unique resource names.
   */
  private final static AtomicLong DISAMBIGUATOR = new AtomicLong();

  private ExternalResourceFactory() {
    // This class is not meant to be instantiated
  }

  /**
   * This method determines if the field is annotated with
   * {@link org.apache.uima.fit.descriptor.ExternalResource}.
   *
   * @param field
   *          the field to analyze
   * @return whether the field is marked as an external resource
   */
  public static boolean isExternalResourceField(Field field) {
    return ReflectionUtil.isAnnotationPresent(field,
            org.apache.uima.fit.descriptor.ExternalResource.class);
  }

  /**
   * Create an external resource description for a custom resource. This is intended to be used
   * together with ....
   *
   * @param aInterface
   *          the interface the resource should implement.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see CustomResourceSpecifier
   */
  public static ExternalResourceDescription createExternalResourceDescription(
          Class<? extends Resource> aInterface, Object... aParams) {
    return createExternalResourceDescription(uniqueResourceKey(aInterface.getName()), aInterface,
            aParams);
  }

  /**
   * Create an external resource description for a custom resource.
   *
   * @param aName
   *          the name of the resource (the key).
   * @param aInterface
   *          the interface the resource should implement.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see CustomResourceSpecifier
   */
  public static ExternalResourceDescription createExternalResourceDescription(final String aName,
          Class<? extends Resource> aInterface, Object... aParams) {
    ConfigurationParameterFactory.ensureParametersComeInPairs(aParams);

    // Extract ExternalResourceDescriptions from configurationData
    List<ExternalResourceBinding> bindings = new ArrayList<ExternalResourceBinding>();
    List<ExternalResourceDescription> descs = new ArrayList<ExternalResourceDescription>();
    for (Entry<String, ExternalResourceDescription> res : extractExternalResourceParameters(aParams)
            .entrySet()) {
      bindings.add(createExternalResourceBinding(res.getKey(), res.getValue()));
      descs.add(res.getValue());
    }

    ResourceSpecifier spec;
    if (ConfigurableDataResource_impl.class.isAssignableFrom(aInterface)) {
      ConfigurationData cfg = ConfigurationParameterFactory.createConfigurationData(aParams);
      ResourceMetaData_impl meta = new ResourceMetaData_impl();

      ConfigurationData reflectedConfigurationData = createConfigurationData(aInterface);
      ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
              reflectedConfigurationData.configurationParameters,
              reflectedConfigurationData.configurationValues);
      ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
              cfg.configurationParameters, cfg.configurationValues);

      ConfigurableDataResourceSpecifier_impl spec1 = new ConfigurableDataResourceSpecifier_impl();
      spec1.setUrl("");
      spec1.setMetaData(meta);
      spec = spec1;
    } else {
      List<Parameter> params = new ArrayList<Parameter>();
      if (aParams != null) {
        for (int i = 0; i < aParams.length / 2; i++) {
          if (ExternalResourceFactory.getExternalResourceParameterType(aParams[i * 2 + 1]) != ResourceValueType.NO_RESOURCE) {
            continue;
          }

          Parameter param = new Parameter_impl();
          param.setName((String) aParams[i * 2]);
          param.setValue((String) aParams[i * 2 + 1]);
          params.add(param);
        }
      }

      CustomResourceSpecifier spec1 = UIMAFramework.getResourceSpecifierFactory()
              .createCustomResourceSpecifier();
      spec1.setResourceClassName(aInterface.getName());
      spec1.setParameters(params.toArray(new Parameter[params.size()]));
      spec = spec1;
    }

    ExtendedExternalResourceDescription_impl extRes = new ExtendedExternalResourceDescription_impl();
    extRes.setName(aName);
    extRes.setResourceSpecifier(spec);
    extRes.setExternalResourceBindings(bindings);
    extRes.setExternalResources(descs);

    return extRes;
  }

  /**
   * Create an external resource description for a {@link SharedResourceObject}.
   *
   * @param aInterface
   *          the interface the resource should implement.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see ConfigurableDataResourceSpecifier
   * @see SharedResourceObject
   */
  public static ExternalResourceDescription createExternalResourceDescription(
          Class<? extends SharedResourceObject> aInterface, String aUrl, Object... aParams) {
    return createExternalResourceDescription(uniqueResourceKey(aInterface.getName()), aInterface,
            aUrl, aParams);
  }

  /**
   * Create an external resource description for a {@link SharedResourceObject}.
   *
   * @param aInterface
   *          the interface the resource should implement.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see ConfigurableDataResourceSpecifier
   * @see SharedResourceObject
   */
  public static ExternalResourceDescription createExternalResourceDescription(
          Class<? extends SharedResourceObject> aInterface, URL aUrl, Object... aParams) {
    return createExternalResourceDescription(uniqueResourceKey(aInterface.getName()), aInterface,
            aUrl.toString(), aParams);
  }

  /**
   * Create an external resource description for a {@link SharedResourceObject}.
   *
   * @param aInterface
   *          the interface the resource should implement.
   * @param aFile
   *          the file from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see ConfigurableDataResourceSpecifier
   * @see SharedResourceObject
   */
  public static ExternalResourceDescription createExternalResourceDescription(
          Class<? extends SharedResourceObject> aInterface, File aFile, Object... aParams) {
    try {
      return createExternalResourceDescription(aInterface, aFile.toURI().toURL(), aParams);
    } catch (MalformedURLException e) {
      // This is something that usually cannot happen, so we degrade this to an
      // IllegalArgumentException which is a RuntimeException that does not need to be caught.
      throw new IllegalArgumentException("File converts to illegal URL [" + aFile + "]");
    }
  }

  /**
   * Create an external resource description for a {@link SharedResourceObject}.
   *
   * @param aName
   *          the name of the resource (the key).
   * @param aInterface
   *          the interface the resource should implement.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @return the description.
   * @see ConfigurableDataResourceSpecifier
   * @see SharedResourceObject
   */
  public static ExternalResourceDescription createExternalResourceDescription(final String aName,
          Class<? extends SharedResourceObject> aInterface, String aUrl, Object... aParams) {
    ConfigurationData cfg = ConfigurationParameterFactory.createConfigurationData(aParams);
    ResourceMetaData_impl meta = new ResourceMetaData_impl();

    ConfigurationData reflectedConfigurationData = createConfigurationData(aInterface);
    ResourceCreationSpecifierFactory.setConfigurationParameters(meta,
            reflectedConfigurationData.configurationParameters,
            reflectedConfigurationData.configurationValues);
    ResourceCreationSpecifierFactory.setConfigurationParameters(meta, cfg.configurationParameters,
            cfg.configurationValues);

    ConfigurableDataResourceSpecifier_impl spec = new ConfigurableDataResourceSpecifier_impl();
    spec.setUrl(aUrl);
    spec.setMetaData(meta);

    ExtendedExternalResourceDescription_impl extRes = new ExtendedExternalResourceDescription_impl();
    extRes.setName(aName);
    extRes.setResourceSpecifier(spec);
    extRes.setImplementationName(aInterface.getName());

    return extRes;
  }

  /**
   * Create an external resource description for a file addressable via an URL.
   *
   * @param aName
   *          the name of the resource (the key).
   * @param aUrl
   *          a URL.
   * @return the description.
   * @see FileResourceSpecifier
   */
  public static ExternalResourceDescription createExternalResourceDescription(final String aName,
          String aUrl) {
    ExternalResourceDescription extRes = new ExternalResourceDescription_impl();
    extRes.setName(aName);
    FileResourceSpecifier frs = new FileResourceSpecifier_impl();
    frs.setFileUrl(aUrl);
    extRes.setResourceSpecifier(frs);
    return extRes;
  }

  /**
   * Create an external resource binding.
   *
   * @param aKey
   *          the key to bind to.
   * @param aResource
   *          the resource to bind.
   * @return the description.
   */
  public static ExternalResourceBinding createExternalResourceBinding(final String aKey,
          final ExternalResourceDescription aResource) {
    return createExternalResourceBinding(aKey, aResource.getName());
  }

  /**
   * Create an external resource binding.
   *
   * @param aKey
   *          the key to bind to.
   * @param aResourceKey
   *          the resource key to bind.
   * @return the description.
   */
  public static ExternalResourceBinding createExternalResourceBinding(final String aKey,
          final String aResourceKey) {
    ExternalResourceBinding extResBind = new ExternalResourceBinding_impl();
    extResBind.setResourceName(aResourceKey);
    extResBind.setKey(aKey);
    return extResBind;
  }

  /**
   * Creates an ExternalResourceDependency for a field annotated with
   * {@link org.apache.uima.fit.descriptor.ExternalResource}.
   *
   * @param field
   *          the field to analyze
   * @return a external resource dependency
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public static ExternalResourceDependency createExternalResourceDependency(Field field) {
    ExternalResource era = ReflectionUtil.getAnnotation(field, ExternalResource.class);

    // Get the binding key for the specified field. If no key is set, use the field name as key.
    String key = era.key();
    if (key.length() == 0) {
      key = field.getName();
    }

    // Get the type of class/interface a resource has to implement to bind to the annotated field.
    // If no API is set, get it from the annotated field type.
    Class<? extends Resource> api = era.api();
    // If no api is specified, look at the annotated field
    if (api == Resource.class) {
      if (Resource.class.isAssignableFrom(field.getType())
              || SharedResourceObject.class.isAssignableFrom(field.getType())) {
        // If no API is set, check if the field type is already a resource type
        api = (Class<? extends Resource>) field.getType();
      } else {
        // If the field does not have a resource type, assume whatever. This allows to use
        // a resource locator without having to specify the api parameter. It also allows
        // to directly inject Java objects - yes, I know that Object does not extend
        // Resource - REC, 2011-03-25
        api = (Class) Object.class;
      }
    }

    return ExternalResourceFactory.createExternalResourceDependency(key, api, !era.mandatory(),
            era.description());
  }

  /**
   * Creates an ExternalResourceDependency for a given key and interface
   *
   * @param aKey
   *          the resource key
   * @param aInterface
   *          the resource interface
   * @param aOptional
   *          determines whether the dependency is optional
   * @param aDescription
   *          a description of the resource
   * @return the external resource dependency
   */
  public static ExternalResourceDependency createExternalResourceDependency(final String aKey,
          final Class<?> aInterface, final boolean aOptional, String aDescription) {
    ExternalResourceDependency dep = new ExternalResourceDependency_impl();
    dep.setInterfaceName(aInterface.getName());
    dep.setKey(aKey);
    dep.setOptional(aOptional);
    dep.setDescription(aDescription);
    return dep;
  }

  /**
   * @param cls
   *          the class to analyze
   * @return the external resource dependencies
   * @throws ResourceInitializationException
   *           if the external resource dependencies could not be created
   */
  public static ExternalResourceDependency[] createExternalResourceDependencies(
          Class<?> cls) throws ResourceInitializationException {
    Map<String, ExternalResourceDependency> depMap = new HashMap<String, ExternalResourceDependency>();
    ExternalResourceFactory.createExternalResourceDependencies(cls, cls, depMap);
    Collection<ExternalResourceDependency> deps = depMap.values();
    return deps.toArray(new ExternalResourceDependency[deps.size()]);
  }

  private static <T> void createExternalResourceDependencies(Class<?> baseCls, Class<?> cls,
          Map<String, ExternalResourceDependency> dependencies)
          throws ResourceInitializationException {
    if (cls.getSuperclass() != null) {
      createExternalResourceDependencies(baseCls, cls.getSuperclass(), dependencies);
    }

    for (Field field : cls.getDeclaredFields()) {
      if (!ReflectionUtil.isAnnotationPresent(field, ExternalResource.class)) {
        continue;
      }

      ExternalResourceDependency dep = createExternalResourceDependency(field);

      if (dependencies.containsKey(dep.getKey())) {
        throw new ResourceInitializationException(new IllegalStateException("Key [" + dep.getKey()
                + "] may only be used on a single field."));
      }

      dependencies.put(dep.getKey(), dep);
    }
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * is encounter that has the specified key, the resource will be bound.
   * <p>
   * <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
   * {@link DataResource#getUri()} will return the same URL that you have specified here. This may
   * <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
   * {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
   * a local file and will return some local path - or it may redirect it to some location as though
   * fit by the {@link ResourceManager}.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aUrl
   *          a URL.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see FileResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey, URL aUrl)
          throws InvalidXMLException {
    bindResource(aDesc, aKey, aUrl.toString());
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * is encounter that has the specified key, the resource will be bound.
   * <p>
   * <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
   * {@link DataResource#getUri()} will return the URL of the file that you have specified here.
   * This may <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
   * {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
   * a local file and will return some local path - or it may redirect it to some location as though
   * fit by the {@link ResourceManager}.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aFile
   *          a file.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see FileResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey, File aFile)
          throws InvalidXMLException {
    try {
      bindResource(aDesc, aKey, aFile.toURI().toURL());
    } catch (MalformedURLException e) {
      // This is something that usually cannot happen, so we degrade this to an
      // IllegalArgumentException which is a RuntimeException that does not need to be caught.
      throw new IllegalArgumentException("File converts to illegal URL [" + aFile + "]");
    }
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * is encounter that has the specified key, the resource will be bound.
   * <p>
   * <b>Caveat</b>: If you use this method, you may expect that {@link DataResource#getUrl()} or
   * {@link DataResource#getUri()} will return the same URL that you have specified here. This is
   * may <b>NOT</b> be the case. UIMA will internally try to resolve the URL via a
   * {@link ResourceManager}. If it cannot resolve a remove URL, this mechanism will think it may be
   * a local file and will return some local path - or it may redirect it to some location as though
   * fit by the {@link ResourceManager}.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aUrl
   *          a URL.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see FileResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey, String aUrl)
          throws InvalidXMLException {
    ExternalResourceDescription extRes = createExternalResourceDescription(aKey, aUrl);
    bindResource(aDesc, aKey, extRes);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with a compatible type is found, the resource will be bound.
   *
   * @param aDesc
   *          a description.
   * @param aRes
   *          the resource to bind.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @throws ClassNotFoundException
   *           if the resource implementation class or interface class could not be accessed
   * @see CustomResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, Class<? extends Resource> aRes,
          String... aParams) throws InvalidXMLException, ClassNotFoundException {
    bindResource(aDesc, aRes, aRes, aParams);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with a compatible type is found, the resource will be bound.
   *
   * @param aDesc
   *          a description.
   * @param aApi
   *          the resource interface.
   * @param aRes
   *          the resource to bind.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @throws ClassNotFoundException
   *           if the resource implementation class or interface class could not be accessed
   * @see CustomResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, Class<?> aApi,
          Class<? extends Resource> aRes, String... aParams) throws InvalidXMLException,
          ClassNotFoundException {
    // Appending a disambiguation suffix it possible to have multiple instances of the same
    // resource with different settings to different keys.
    ExternalResourceDescription extRes = createExternalResourceDescription(
            uniqueResourceKey(aRes.getName()), aRes, (Object[]) aParams);
    bindResource(aDesc, extRes);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with a compatible type is found, the resource will be bound.
   *
   * @param aDesc
   *          a description.
   * @param aRes
   *          the resource to bind.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @throws ClassNotFoundException
   *           if the resource implementation class or interface class could not be accessed
   * @see SharedResourceObject
   */
  public static void bindResource(ResourceSpecifier aDesc,
          Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
          throws InvalidXMLException, ClassNotFoundException {
    ExternalResourceDescription extRes = createExternalResourceDescription(
            uniqueResourceKey(aRes.getName()), aRes, aUrl, aParams);
    bind((AnalysisEngineDescription) aDesc, extRes);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * is encountered that has a key equal to the API class name, the resource will be bound.
   *
   * @param aDesc
   *          a description.
   * @param aApi
   *          the resource interface.
   * @param aRes
   *          the resource to bind.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see SharedResourceObject
   */
  public static void bindResource(ResourceSpecifier aDesc, Class<?> aApi,
          Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
          throws InvalidXMLException {
    bindResource(aDesc, aApi.getName(), aRes, aUrl, aParams);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with the given key is encountered the resource will be bound.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aRes
   *          the resource to bind.
   * @param aUrl
   *          the URL from which the resource is initialized.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see SharedResourceObject
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey,
          Class<? extends SharedResourceObject> aRes, String aUrl, Object... aParams)
          throws InvalidXMLException {
    ExternalResourceDescription extRes = createExternalResourceDescription(
            uniqueResourceKey(aRes.getName()), aRes, aUrl, aParams);
    bind((AnalysisEngineDescription) aDesc, aKey, extRes);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with the given key is encountered, the given resource is bound to it.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aRes
   *          the resource to bind.
   * @param aParams
   *          parameters passed to the resource when it is created.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @see CustomResourceSpecifier
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey,
          Class<? extends Resource> aRes, String... aParams) throws InvalidXMLException {
    if (ParameterizedDataResource.class.isAssignableFrom(aRes)) {
      createDependency(aDesc, aKey, DataResource.class);
    }

    // Appending a disambiguation suffix it possible to have multiple instances of the same
    // resource with different settings to different keys.
    ExternalResourceDescription extRes = createExternalResourceDescription(
            uniqueResourceKey(aRes.getName()), aRes, (Object[]) aParams);
    bindResource(aDesc, aKey, extRes);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with a compatible type is found, the given resource is bound to it.
   *
   * @param aDesc
   *          a description.
   * @param aResDesc
   *          the resource description.
   * @throws InvalidXMLException
   *           if import resolution failed
   * @throws ClassNotFoundException
   *           if the resource implementation class or interface class could not be accessed
   */
  public static void bindResource(ResourceSpecifier aDesc, ExternalResourceDescription aResDesc)
          throws InvalidXMLException, ClassNotFoundException {
    // Dispatch
    if (aDesc instanceof AnalysisEngineDescription) {
      bind((AnalysisEngineDescription) aDesc, aResDesc);
    }
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with the given key is encountered, the given resource is bound to it.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aResDesc
   *          the resource description.
   * @throws InvalidXMLException
   *           if import resolution failed
   */
  public static void bindResource(ResourceSpecifier aDesc, String aKey,
          ExternalResourceDescription aResDesc) throws InvalidXMLException {
    // Dispatch
    if (aDesc instanceof AnalysisEngineDescription) {
      bind((AnalysisEngineDescription) aDesc, aKey, aResDesc);
    }
  }

  /**
   * Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
   * components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
   * resource dependencies can be automatically generated by uimaFIT for such components.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aImpl
   *          the resource implementation.
   * @param aParams
   *          additional parameters supported by the resource.
   * @throws InvalidXMLException
   *           if import resolution failed
   */
  public static void createDependencyAndBind(ResourceSpecifier aDesc, String aKey,
          Class<? extends Resource> aImpl, String... aParams) throws InvalidXMLException {
    Class<?> api = (ParameterizedDataResource.class.isAssignableFrom(aImpl)) ? DataResource.class
            : aImpl;
    createDependencyAndBind(aDesc, aKey, aImpl, api, aParams);
  }

  /**
   * Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
   * components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
   * resource dependencies can be automatically generated by uimaFIT for such components.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aImpl
   *          the resource implementation.
   * @param aApi
   *          the resource interface
   * @param aParams
   *          additional parameters supported by the resource.
   * @throws InvalidXMLException
   *           if import resolution failed
   */
  public static void createDependencyAndBind(ResourceSpecifier aDesc, String aKey,
          Class<? extends Resource> aImpl, Class<?> aApi, String... aParams)
          throws InvalidXMLException {
    createDependency(aDesc, aKey, aApi);
    bindResource(aDesc, aKey, aImpl, aParams);
  }

  /**
   * Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
   * components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
   * resource dependencies can be automatically generated by uimaFIT for such components.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aApi
   *          the resource API.
   */
  public static void createDependency(ResourceSpecifier aDesc, String aKey, Class<?> aApi) {
    ExternalResourceDependency[] deps = getExternalResourceDependencies(aDesc);
    if (deps == null) {
      deps = new ExternalResourceDependency[] {};
    }

    // Check if the resource dependency is already present
    boolean found = false;
    for (ExternalResourceDependency dep : deps) {
      if (dep.getKey().equals(aKey)) {
        found = true;
        break;
      }
    }

    // If not, create one
    if (!found) {
      setExternalResourceDependencies(
              aDesc,
              (ExternalResourceDependency[]) ArrayUtils.add(deps,
                      createExternalResourceDependency(aKey, aApi, false, null)));
    }
  }

  /**
   * Convenience method to set the external resource dependencies on a resource specifier.
   * Unfortunately different methods need to be used for different sub-classes.
   *
   * @throws IllegalArgumentException
   *           if the sub-class passed is not supported.
   */
  private static void setExternalResourceDependencies(ResourceSpecifier aDesc,
          ExternalResourceDependency[] aDependencies) {
    if (aDesc instanceof CollectionReaderDescription) {
      ((CollectionReaderDescription) aDesc).setExternalResourceDependencies(aDependencies);
    } else if (aDesc instanceof AnalysisEngineDescription) {
      ((AnalysisEngineDescription) aDesc).setExternalResourceDependencies(aDependencies);
    } else {
      throw new IllegalArgumentException(
              "Resource specified cannot have external resource dependencies");
    }
  }

  /**
   * Convenience method to get the external resource dependencies from a resource specifier.
   * Unfortunately different methods need to be used for different sub-classes.
   *
   * @throws IllegalArgumentException
   *           if the sub-class passed is not supported.
   */
  private static ExternalResourceDependency[] getExternalResourceDependencies(
          ResourceSpecifier aDesc) {
    if (aDesc instanceof CollectionReaderDescription) {
      return ((CollectionReaderDescription) aDesc).getExternalResourceDependencies();
    } else if (aDesc instanceof AnalysisEngineDescription) {
      return ((AnalysisEngineDescription) aDesc).getExternalResourceDependencies();
    } else {
      throw new IllegalArgumentException(
              "Resource specified cannot have external resource dependencies");
    }
  }

  /**
   * Create a new dependency for the specified resource and bind it. This method is helpful for UIMA
   * components that do not use the uimaFIT {@link ExternalResource} annotation, because no external
   * resource dependencies can be automatically generated by uimaFIT for such components.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aImpl
   *          the resource implementation.
   * @param aUrl
   *          the resource URL.
   * @param aParams
   *          additional parameters supported by the resource.
   * @throws InvalidXMLException
   *           if import resolution failed
   */
  public static void createDependencyAndBind(AnalysisEngineDescription aDesc, String aKey,
          Class<? extends SharedResourceObject> aImpl, String aUrl, Object... aParams)
          throws InvalidXMLException {
    if (aDesc.getExternalResourceDependency(aKey) == null) {
      ExternalResourceDependency[] deps = aDesc.getExternalResourceDependencies();
      if (deps == null) {
        deps = new ExternalResourceDependency[] {};
      }
      aDesc.setExternalResourceDependencies((ExternalResourceDependency[]) ArrayUtils.add(deps,
              createExternalResourceDependency(aKey, aImpl, false, null)));
    }
    bindResource(aDesc, aKey, aImpl, aUrl, aParams);
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * a compatible type is found, the given resource is bound to it.
   *
   * @param aDesc
   *          a description.
   * @param aResDesc
   *          the resource description.
   */
  private static void bind(AnalysisEngineDescription aDesc, ExternalResourceDescription aResDesc)
          throws InvalidXMLException, ClassNotFoundException {
    // Recursively address delegates
    if (!aDesc.isPrimitive()) {
      for (Object delegate : aDesc.getDelegateAnalysisEngineSpecifiers().values()) {
        bindResource((ResourceSpecifier) delegate, aResDesc);
      }
    }

    // Bind if necessary
    Class<?> resClass = Class.forName(getImplementationName(aResDesc));
    for (ExternalResourceDependency dep : aDesc.getExternalResourceDependencies()) {
      Class<?> apiClass = Class.forName(dep.getInterfaceName());

      // Never bind fields of type Object. See also ExternalResourceInitializer#getApi()
      if (apiClass.equals(Object.class)) {
        continue;
      }

      if (apiClass.isAssignableFrom(resClass)) {
        bindExternalResource(aDesc, dep.getKey(), aResDesc);
      }
    }
  }

  /**
   * Scan the given resource specifier for external resource dependencies and whenever a dependency
   * with the given key is encountered, the given resource is bound to it.
   *
   * @param aDesc
   *          a description.
   * @param aKey
   *          the key to bind to.
   * @param aResDesc
   *          the resource description.
   */
  private static void bind(AnalysisEngineDescription aDesc, String aKey,
          ExternalResourceDescription aResDesc) throws InvalidXMLException {
    // Recursively address delegates
    if (!aDesc.isPrimitive()) {
      for (Object delegate : aDesc.getDelegateAnalysisEngineSpecifiers().values()) {
        bindResource((ResourceSpecifier) delegate, aKey, aResDesc);
      }
    }

    // Bind if necessary
    for (ExternalResourceDependency dep : aDesc.getExternalResourceDependencies()) {
      if (aKey.equals(dep.getKey())) {
        bindExternalResource(aDesc, aKey, aResDesc);
      }
    }
  }

  /**
   * Create a new external resource binding.
   *
   * @param aResMgrCfg
   *          the resource manager to create the binding in.
   * @param aBindTo
   *          what key to bind to.
   * @param aRes
   *          the resource that should be bound.
   */
  public static void bindExternalResource(ResourceManagerConfiguration aResMgrCfg, String aBindTo,
          ExternalResourceDescription aRes) {
    // Create a map of all bindings
    Map<String, ExternalResourceBinding> bindings = new HashMap<String, ExternalResourceBinding>();
    for (ExternalResourceBinding b : aResMgrCfg.getExternalResourceBindings()) {
      bindings.put(b.getKey(), b);
    }

    // Create a map of all resources
    Map<String, ExternalResourceDescription> resources = new HashMap<String, ExternalResourceDescription>();
    for (ExternalResourceDescription r : aResMgrCfg.getExternalResources()) {
      resources.put(r.getName(), r);
    }

    // For the current resource, add resource and binding
    ExternalResourceBinding extResBind = createExternalResourceBinding(aBindTo, aRes);
    bindings.put(extResBind.getKey(), extResBind);
    resources.put(aRes.getName(), aRes);

    // Handle nested resources
    bindNestedResources(aRes, bindings, resources);

    // Commit everything to the resource manager configuration
    aResMgrCfg.setExternalResourceBindings(bindings.values().toArray(
            new ExternalResourceBinding[bindings.size()]));
    aResMgrCfg.setExternalResources(resources.values().toArray(
            new ExternalResourceDescription[resources.size()]));
  }

  /**
   * Create a new external resource binding.
   *
   * @param aRes
   *          the resource to bind to
   * @param aBindTo
   *          what key to bind to.
   * @param aNestedRes
   *          the resource that should be bound.
   */
  public static void bindExternalResource(ExternalResourceDescription aRes, String aBindTo,
          ExternalResourceDescription aNestedRes) {
    if (!(aRes instanceof ExtendedExternalResourceDescription_impl)) {
      throw new IllegalArgumentException("Nested resources are only supported on instances of ["
              + ExtendedExternalResourceDescription_impl.class.getName() + "] which"
              + "can be created with uimaFIT's createExternalResourceDescription() methods.");
    }

    ExtendedExternalResourceDescription_impl extRes = (ExtendedExternalResourceDescription_impl) aRes;

    // Create a map of all bindings
    Map<String, ExternalResourceBinding> bindings = new HashMap<String, ExternalResourceBinding>();
    for (ExternalResourceBinding b : extRes.getExternalResourceBindings()) {
      bindings.put(b.getKey(), b);
    }

    // Create a map of all resources
    Map<String, ExternalResourceDescription> resources = new HashMap<String, ExternalResourceDescription>();
    for (ExternalResourceDescription r : extRes.getExternalResources()) {
      resources.put(r.getName(), r);
    }

    // For the current resource, add resource and binding
    ExternalResourceBinding extResBind = createExternalResourceBinding(aBindTo, aNestedRes);
    bindings.put(extResBind.getKey(), extResBind);
    resources.put(aRes.getName(), aRes);

    // Handle nested resources
    bindNestedResources(aRes, bindings, resources);

    // Commit everything to the resource manager configuration
    extRes.setExternalResourceBindings(bindings.values());
    extRes.setExternalResources(resources.values());

  }

  /**
   * Helper method to recursively bind resources bound to resources.
   *
   * @param aRes
   *          resource.
   * @param aBindings
   *          bindings already made.
   * @param aResources
   *          resources already bound.
   */
  private static void bindNestedResources(ExternalResourceDescription aRes,
          Map<String, ExternalResourceBinding> aBindings,
          Map<String, ExternalResourceDescription> aResources) {
    // Handle nested resources
    if (aRes instanceof ExtendedExternalResourceDescription_impl) {
      ExtendedExternalResourceDescription_impl extRes = (ExtendedExternalResourceDescription_impl) aRes;

      // Tell the external resource its name. This is needed in order to find the resources
      // bound to this resource later on. Set only if the resource supports this parameter.
      // Mind that supporting this parameter is mandatory for resource implementing
      // ExternalResourceAware.
      if (canParameterBeSet(extRes.getResourceSpecifier(), PARAM_RESOURCE_NAME)) {
        ConfigurationParameterFactory.setParameter(extRes.getResourceSpecifier(),
                PARAM_RESOURCE_NAME, aRes.getName());
      }

      // Create a map of all resources
      Map<String, ExternalResourceDescription> res = new HashMap<String, ExternalResourceDescription>();
      for (ExternalResourceDescription r : extRes.getExternalResources()) {
        res.put(r.getName(), r);
      }

      // Bind nested resources
      for (ExternalResourceBinding b : extRes.getExternalResourceBindings()) {
        // Avoid re-prefixing the resource name
        String key = b.getKey();
        if (!key.startsWith(aRes.getName() + PREFIX_SEPARATOR)) {
          key = aRes.getName() + PREFIX_SEPARATOR + b.getKey();
        }
        // Avoid unnecessary binding and an infinite loop when a resource binds to itself
        if (!aBindings.containsKey(key)) {
          // Mark the current binding as processed so we do not recurse
          aBindings.put(key, b);
          ExternalResourceDescription nestedRes = res.get(b.getResourceName());
          aResources.put(nestedRes.getName(), nestedRes);
          bindNestedResources(nestedRes, aBindings, aResources);
          // Set the proper key on the binding.
          b.setKey(key);
        }
      }
    }
  }

  /**
   * Create a new external resource binding.
   *
   * @param aDesc
   *          the specifier to create the binding in.
   * @param aBindTo
   *          what key to bind to.
   * @param aRes
   *          the resource that should be bound.
   */
  public static void bindExternalResource(ResourceCreationSpecifier aDesc, String aBindTo,
          ExternalResourceDescription aRes) {
    ResourceManagerConfiguration resMgrCfg = aDesc.getResourceManagerConfiguration();
    if (resMgrCfg == null) {
      resMgrCfg = new ResourceManagerConfiguration_impl();
      aDesc.setResourceManagerConfiguration(resMgrCfg);
    }

    bindExternalResource(resMgrCfg, aBindTo, aRes);
  }

  /**
   * Create a new external resource binding.
   *
   * @param aResMgrCfg
   *          the resource manager to create the binding in.
   * @param aBindTo
   *          what key to bind to.
   * @param aRes
   *          the resource that should be bound.
   */
  public static void bindExternalResource(ResourceManagerConfiguration aResMgrCfg, String aBindTo,
          String aRes) {
    ExternalResourceBinding extResBind = createExternalResourceBinding(aBindTo, aRes);
    aResMgrCfg.addExternalResourceBinding(extResBind);
  }

  /**
   * Create a new external resource binding.
   *
   * @param aDesc
   *          the specifier to create the binding in.
   * @param aBindTo
   *          what key to bind to.
   * @param aRes
   *          the resource that should be bound.
   */
  public static void bindExternalResource(ResourceCreationSpecifier aDesc, String aBindTo,
          String aRes) {
    ResourceManagerConfiguration resMgrCfg = aDesc.getResourceManagerConfiguration();
    if (resMgrCfg == null) {
      resMgrCfg = new ResourceManagerConfiguration_impl();
      aDesc.setResourceManagerConfiguration(resMgrCfg);
    }

    bindExternalResource(resMgrCfg, aBindTo, aRes);
  }

  static String uniqueResourceKey(String aKey) {
    return aKey + '-' + DISAMBIGUATOR.getAndIncrement();
  }

  /**
   * Find the name of the class implementing this resource. The location where this name is stored
   * varies, depending if the resource extends {@link SharedResourceObject} or implements
   * {@link Resource}.
   *
   * @param aDesc
   *          the external resource description.
   * @return the implementation name.
   */
  protected static String getImplementationName(ExternalResourceDescription aDesc) {
    if (aDesc.getResourceSpecifier() instanceof CustomResourceSpecifier) {
      return ((CustomResourceSpecifier) aDesc.getResourceSpecifier()).getResourceClassName();
    } else {
      return aDesc.getImplementationName();
    }
  }

  /**
   * Extracts the external resource from the configuration parameters and nulls out these
   * parameters. Mind that the array passed to this method is modified by the method.
   *
   * @param configurationData
   *          the configuration parameters.
   * @return extRes the external resource parameters.
   */
  protected static Map<String, ExternalResourceDescription> extractExternalResourceParameters(
          final Object[] configurationData) {
    if (configurationData == null) {
      return Collections.emptyMap();
    }

    Map<String, ExternalResourceDescription> extRes = new HashMap<String, ExternalResourceDescription>();
    for (int i = 0; i < configurationData.length - 1; i += 2) {
      String key = (String) configurationData[i];
      Object value = configurationData[i + 1];

      if (value == null) {
        continue;
      }
     
      // Store External Resource parameters separately
      ResourceValueType type = getExternalResourceParameterType(value);
      if (type == ResourceValueType.PRIMITIVE) {
        ExternalResourceDescription description = (ExternalResourceDescription) value;
        extRes.put(key, description);
      }
      else if (type.isMultiValued()) {
        Collection<ExternalResourceDescription> resList;
        if (type == ResourceValueType.ARRAY) {
          resList = asList((ExternalResourceDescription[]) value);
        }
        else {
          resList = (Collection<ExternalResourceDescription>) value;
        }

        // Record the list elements
        List<Object> params = new ArrayList<Object>();
        params.add(ResourceList.PARAM_SIZE);
        params.add(String.valueOf(resList.size())); // "Resource" only supports String parameters!
        int n = 0;
        for (ExternalResourceDescription res : resList) {
          params.add(ResourceList.ELEMENT_KEY + "[" + n + "]");
          params.add(res);
          n++;
        }
       
        // Record the list and attach the list elements to the list
        extRes.put(key, createExternalResourceDescription(ResourceList.class, params.toArray()));
      }
    }

    return extRes;
  }
 
  /**
   * Determine which kind of external resource the given value is. This is only meant for
   * uimaFIT internal use. This method is required by the ConfigurationParameterFactory, so it is
   * package private instead of private.
   */
  static ResourceValueType getExternalResourceParameterType(Object aValue)
  {
    if (aValue == null) {
      return ResourceValueType.NO_RESOURCE;
    }
   
    boolean isResourcePrimitive = aValue instanceof ExternalResourceDescription;
    boolean isResourceArray = aValue.getClass().isArray()
            && ExternalResourceDescription.class.isAssignableFrom(aValue.getClass()
                    .getComponentType());
    boolean isResourceCollection = (Collection.class.isAssignableFrom(aValue
            .getClass()) && !((Collection) aValue).isEmpty() && ((Collection) aValue)
            .iterator().next() instanceof ExternalResourceDescription);
    if (isResourcePrimitive) {
      return ResourceValueType.PRIMITIVE;
    }
    else if (isResourceArray) {
      return ResourceValueType.ARRAY;
    }
    else if (isResourceCollection) {
      return ResourceValueType.COLLECTION;
    }
    else {
      return ResourceValueType.NO_RESOURCE;
    }
  }
 
  /**
   * Types of external resource values.
   */
  static enum ResourceValueType {
    NO_RESOURCE,
    PRIMITIVE,
    ARRAY,
    COLLECTION;
   
    public boolean isMultiValued()
    {
      return this == COLLECTION || this == ARRAY;
    }
  }
}
TOP

Related Classes of org.apache.uima.fit.factory.ExternalResourceFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.