DataProvidersManager.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.data;

  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.text.ParseException;
  22. import java.util.ArrayList;
  23. import java.util.Collections;
  24. import java.util.Iterator;
  25. import java.util.LinkedHashSet;
  26. import java.util.List;
  27. import java.util.Set;
  28. import java.util.regex.Pattern;

  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;

  31. /** Singleton class managing all supported {@link DataProvider data providers}.

  32.  * <p>
  33.  * This class is the single point of access for all data loading features. It
  34.  * is used for example to load Earth Orientation Parameters used by IERS frames,
  35.  * to load UTC leap seconds used by time scales, to load planetary ephemerides ...
  36.  * <p>
  37.  *
  38.  * </p>
  39.  * It is user-customizable: users can add their own data providers at will. This
  40.  * allows them for example to use a database or an existing data loading library
  41.  * in order to embed an Orekit enabled application in a global system with its
  42.  * own data handling mechanisms. There is no upper limitation on the number of
  43.  * providers, but often each application will use only a few.
  44.  * </p>
  45.  *
  46.  * <p>
  47.  * If the list of providers is empty when attempting to {@link #feed(String, DataLoader)
  48.  * feed} a file loader, the {@link #addDefaultProviders()} method is called
  49.  * automatically to set up a default configuration. This default configuration
  50.  * contains one {@link DataProvider data provider} for each component of the
  51.  * path-like list specified by the java property <code>orekit.data.path</code>.
  52.  * See the {@link #feed(String, DataLoader) feed} method documentation for further
  53.  * details. The default providers configuration is <em>not</em> set up if the list
  54.  * is not empty. If users want to have both the default providers and additional
  55.  * providers, they must call explicitly the {@link #addDefaultProviders()} method.
  56.  * </p>
  57.  *
  58.  * @author Luc Maisonobe
  59.  * @see DirectoryCrawler
  60.  * @see ClasspathCrawler
  61.  */
  62. public class DataProvidersManager {

  63.     /** Name of the property defining the root directories or zip/jar files path for default configuration. */
  64.     public static final String OREKIT_DATA_PATH = "orekit.data.path";

  65.     /** Supported data providers. */
  66.     private final List<DataProvider> providers;

  67.     /** Loaded data. */
  68.     private final Set<String> loaded;

  69.     /** Build an instance with default configuration.
  70.      * <p>
  71.      * This is a singleton, so the constructor is private.
  72.      * </p>
  73.      */
  74.     private DataProvidersManager() {
  75.         providers = new ArrayList<DataProvider>();
  76.         loaded    = new LinkedHashSet<String>();
  77.     }

  78.     /** Get the unique instance.
  79.      * @return unique instance of the manager.
  80.      */
  81.     public static DataProvidersManager getInstance() {
  82.         return LazyHolder.INSTANCE;
  83.     }

  84.     /** Add the default providers configuration.
  85.      * <p>
  86.      * The default configuration contains one {@link DataProvider data provider}
  87.      * for each component of the path-like list specified by the java property
  88.      * <code>orekit.data.path</code>.
  89.      * </p>
  90.      * <p>
  91.      * If the property is not set or is null, no data will be available to the library
  92.      * (for example no pole corrections will be applied and only predefined UTC steps
  93.      * will be taken into account). No errors will be triggered in this case.
  94.      * </p>
  95.      * <p>
  96.      * If the property is set, it must contains a list of existing directories or zip/jar
  97.      * archives. One {@link DirectoryCrawler} instance will be set up for each
  98.      * directory and one {@link ZipJarCrawler} instance (configured to look for the
  99.      * archive in the filesystem) will be set up for each zip/jar archive. The list
  100.      * elements in the java property are separated using the standard path separator for
  101.      * the operating system as returned by {@link System#getProperty(String)
  102.      * System.getProperty("path.separator")}. This standard path separator is ":" on
  103.      * Linux and Unix type systems and ";" on Windows types systems.
  104.      * </p>
  105.      * @exception OrekitException if an element of the list does not exist or exists but
  106.      * is neither a directory nor a zip/jar archive
  107.      */
  108.     public void addDefaultProviders() throws OrekitException {

  109.         // get the path containing all components
  110.         final String path = System.getProperty(OREKIT_DATA_PATH);
  111.         if ((path != null) && !"".equals(path)) {

  112.             // extract the various components
  113.             for (final String name : path.split(System.getProperty("path.separator"))) {
  114.                 if (!"".equals(name)) {

  115.                     final File file = new File(name);

  116.                     // check component
  117.                     if (!file.exists()) {
  118.                         if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) {
  119.                             throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, name);
  120.                         } else {
  121.                             throw new OrekitException(OrekitMessages.DATA_ROOT_DIRECTORY_DOES_NOT_EXIST, name);
  122.                         }
  123.                     }

  124.                     if (file.isDirectory()) {
  125.                         addProvider(new DirectoryCrawler(file));
  126.                     } else if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) {
  127.                         addProvider(new ZipJarCrawler(file));
  128.                     } else {
  129.                         throw new OrekitException(OrekitMessages.NEITHER_DIRECTORY_NOR_ZIP_OR_JAR, name);
  130.                     }

  131.                 }
  132.             }
  133.         }

  134.     }

  135.     /** Add a data provider to the supported list.
  136.      * @param provider data provider to add
  137.      * @see #removeProvider(Class)
  138.      * @see #clearProviders()
  139.      * @see #isSupported(Class)
  140.      * @see #getProviders()
  141.      */
  142.     public void addProvider(final DataProvider provider) {
  143.         providers.add(provider);
  144.     }

  145.     /** Remove one provider.
  146.      * <p>
  147.      * The first supported provider extending the specified class (or implementing
  148.      * the interface) is removed and returned. For example, removing the default
  149.      * provider that loads data from files located somewhere in a directory hierarchy
  150.      * can be done by calling:
  151.      * <pre>
  152.      *   DataProvidersManager.getInstance().remove(DataDirectoryCrawler.class);
  153.      * </pre>
  154.      * </p>
  155.      * @param providerClass class (or one of the superclass's) of the provider to remove
  156.      * @return instance removed (null if no provider of the given class was supported)
  157.      * @see #addProvider(DataProvider)
  158.      * @see #clearProviders()
  159.      * @see #isSupported(Class)
  160.      * @see #getProviders()
  161.      * @deprecated as of 6.0, replaced by {@link #removeProvider(DataProvider)}
  162.      */
  163.     @Deprecated
  164.     public DataProvider removeProvider(final Class<? extends DataProvider> providerClass) {
  165.         for (final DataProvider provider : providers) {
  166.             if (providerClass.isInstance(provider)) {
  167.                 return removeProvider(provider);
  168.             }
  169.         }
  170.         return null;
  171.     }

  172.     /** Remove one provider.
  173.      * @param provider provider instance to remove
  174.      * @return instance removed (null if the provider was not already present)
  175.      * @see #addProvider(DataProvider)
  176.      * @see #clearProviders()
  177.      * @see #isSupported(DataProvider)
  178.      * @see #getProviders()
  179.      * @since 5.1
  180.      */
  181.     public DataProvider removeProvider(final DataProvider provider) {
  182.         for (final Iterator<DataProvider> iterator = providers.iterator(); iterator.hasNext();) {
  183.             final DataProvider current = iterator.next();
  184.             if (current == provider) {
  185.                 iterator.remove();
  186.                 return provider;
  187.             }
  188.         }
  189.         return null;
  190.     }

  191.     /** Remove all data providers.
  192.      * @see #addProvider(DataProvider)
  193.      * @see #removeProvider(Class)
  194.      * @see #isSupported(Class)
  195.      * @see #getProviders()
  196.      */
  197.     public void clearProviders() {
  198.         providers.clear();
  199.     }

  200.     /** Check if some type of provider is supported.
  201.      * @param providerClass class (or one of the superclass's) of the provider to check
  202.      * @return true if one provider of the given class is already in the supported list
  203.      * @see #addProvider(DataProvider)
  204.      * @see #removeProvider(Class)
  205.      * @see #clearProviders()
  206.      * @see #getProviders()
  207.      * @deprecated as of 6.0, replaced by {@link #isSupported(DataProvider)}
  208.      */
  209.     @Deprecated
  210.     public boolean isSupported(final Class<? extends DataProvider> providerClass) {
  211.         for (final Iterator<DataProvider> iterator = providers.iterator(); iterator.hasNext();) {
  212.             final DataProvider provider = iterator.next();
  213.             if (providerClass.isInstance(provider)) {
  214.                 return true;
  215.             }
  216.         }
  217.         return false;
  218.     }

  219.     /** Check if some provider is supported.
  220.      * @param provider provider to check
  221.      * @return true if the specified provider instane is already in the supported list
  222.      * @see #addProvider(DataProvider)
  223.      * @see #removeProvider(DataProvider)
  224.      * @see #clearProviders()
  225.      * @see #getProviders()
  226.      * @since 5.1
  227.      */
  228.     public boolean isSupported(final DataProvider provider) {
  229.         for (final DataProvider current : providers) {
  230.             if (current == provider) {
  231.                 return true;
  232.             }
  233.         }
  234.         return false;
  235.     }

  236.     /** Get an unmodifiable view of the list of supported providers.
  237.      * @return unmodifiable view of the list of supported providers
  238.      * @see #addProvider(DataProvider)
  239.      * @see #removeProvider(Class)
  240.      * @see #clearProviders()
  241.      * @see #isSupported(Class)
  242.      */
  243.     public List<DataProvider> getProviders() {
  244.         return Collections.unmodifiableList(providers);
  245.     }

  246.     /** Get an unmodifiable view of the set of data file names that have been loaded.
  247.      * <p>
  248.      * The names returned are exactly the ones that were given to the {@link
  249.      * DataLoader#loadData(InputStream, String) DataLoader.loadData} method.
  250.      * </p>
  251.      * @return unmodifiable view of the set of data file names that have been loaded
  252.      * @see #feed(String, DataLoader)
  253.      * @see #clearLoadedDataNames()
  254.      */
  255.     public Set<String> getLoadedDataNames() {
  256.         return Collections.unmodifiableSet(loaded);
  257.     }

  258.     /** Clear the set of data file names that have been loaded.
  259.      * @see #getLoadedDataNames()
  260.      */
  261.     public void clearLoadedDataNames() {
  262.         loaded.clear();
  263.     }

  264.     /** Feed a data file loader by browsing all data providers.
  265.      * <p>
  266.      * If this method is called with an empty list of providers, a default
  267.      * providers configuration is set up. This default configuration contains
  268.      * only one {@link DataProvider data provider}: a {@link DirectoryCrawler}
  269.      * instance that loads data from files located somewhere in a directory hierarchy.
  270.      * This default provider is <em>not</em> added if the list is not empty. If users
  271.      * want to have both the default provider and other providers, they must add it
  272.      * explicitly.
  273.      * </p>
  274.      * <p>
  275.      * The providers are used in the order in which they were {@link #addProvider(DataProvider)
  276.      * added}. As soon as one provider is able to feed the data loader, the loop is
  277.      * stopped. If no provider is able to feed the data loader, then the last error
  278.      * triggered is thrown.
  279.      * </p>
  280.      * @param supportedNames regular expression for file names supported by the visitor
  281.      * @param loader data loader to use
  282.      * @return true if some data has been loaded
  283.      * @exception OrekitException if the data loader cannot be fed (read error ...)
  284.      * or if the default configuration cannot be set up
  285.      */
  286.     public boolean feed(final String supportedNames, final DataLoader loader)
  287.         throws OrekitException {

  288.         final Pattern supported = Pattern.compile(supportedNames);

  289.         // set up a default configuration if no providers have been set
  290.         if (providers.isEmpty()) {
  291.             addDefaultProviders();
  292.         }

  293.         // monitor the data that the loader will load
  294.         final DataLoader monitoredLoader = new MonitoringWrapper(loader);

  295.         // crawl the data collection
  296.         OrekitException delayedException = null;
  297.         for (final DataProvider provider : providers) {
  298.             try {

  299.                 // try to feed the visitor using the current provider
  300.                 if (provider.feed(supported, monitoredLoader)) {
  301.                     return true;
  302.                 }

  303.             } catch (OrekitException oe) {
  304.                 // remember the last error encountered
  305.                 delayedException = oe;
  306.             }
  307.         }

  308.         if (delayedException != null) {
  309.             throw delayedException;
  310.         }

  311.         return false;

  312.     }

  313.     /** Data loading monitoring wrapper class. */
  314.     private class MonitoringWrapper implements DataLoader {

  315.         /** Wrapped loader. */
  316.         private final DataLoader loader;

  317.         /** Simple constructor.
  318.          * @param loader loader to monitor
  319.          */
  320.         public MonitoringWrapper(final DataLoader loader) {
  321.             this.loader = loader;
  322.         }

  323.         /** {@inheritDoc} */
  324.         public boolean stillAcceptsData() {
  325.             // delegate to monitored loader
  326.             return loader.stillAcceptsData();
  327.         }

  328.         /** {@inheritDoc} */
  329.         public void loadData(final InputStream input, final String name)
  330.             throws IOException, ParseException, OrekitException {

  331.             // delegate to monitored loader
  332.             loader.loadData(input, name);

  333.             // monitor the fact new data has been loaded
  334.             loaded.add(name);

  335.         }

  336.     }

  337.     /** Holder for the manager singleton.
  338.      * <p>
  339.      * We use the Initialization on demand holder idiom to store
  340.      * the singletons, as it is both thread-safe, efficient (no
  341.      * synchronization) and works with all versions of java.
  342.      * </p>
  343.      */
  344.     private static class LazyHolder {

  345.         /** Unique instance. */
  346.         private static final DataProvidersManager INSTANCE = new DataProvidersManager();

  347.         /** Private constructor.
  348.          * <p>This class is a utility class, it should neither have a public
  349.          * nor a default constructor. This private constructor prevents
  350.          * the compiler from generating one automatically.</p>
  351.          */
  352.         private LazyHolder() {
  353.         }

  354.     }

  355. }