DataProvidersManager.java
- /* Copyright 2002-2018 CS Systèmes d'Information
- * Licensed to CS Systèmes d'Information (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS 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.orekit.data;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.text.ParseException;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Set;
- import java.util.regex.Pattern;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- /** Singleton class managing all supported {@link DataProvider data providers}.
- * <p>
- * This class is the single point of access for all data loading features. It
- * is used for example to load Earth Orientation Parameters used by IERS frames,
- * to load UTC leap seconds used by time scales, to load planetary ephemerides ...
- *
- * <p>
- * It is user-customizable: users can add their own data providers at will. This
- * allows them for example to use a database or an existing data loading library
- * in order to embed an Orekit enabled application in a global system with its
- * own data handling mechanisms. There is no upper limitation on the number of
- * providers, but often each application will use only a few.
- * </p>
- *
- * <p>
- * If the list of providers is empty when attempting to {@link #feed(String, DataLoader)
- * feed} a file loader, the {@link #addDefaultProviders()} method is called
- * automatically to set up a default configuration. This default configuration
- * contains one {@link DataProvider data provider} for each component of the
- * path-like list specified by the java property <code>orekit.data.path</code>.
- * See the {@link #feed(String, DataLoader) feed} method documentation for further
- * details. The default providers configuration is <em>not</em> set up if the list
- * is not empty. If users want to have both the default providers and additional
- * providers, they must call explicitly the {@link #addDefaultProviders()} method.
- * </p>
- *
- * <p>
- * The default configuration uses a predefined set of {@link DataFilter data filters}
- * that already handled gzip-compressed files (recognized by the {@code .gz} suffix)
- * and Unix-compressed files (recognized by the {@code .Z} suffix).
- * Users can {@link #addFilter(DataFilter) add} custom filters for handling specific
- * types of filters (decompression, deciphering...).
- * </p>
- *
- * @author Luc Maisonobe
- * @see DirectoryCrawler
- * @see ClasspathCrawler
- */
- public class DataProvidersManager {
- /** Name of the property defining the root directories or zip/jar files path for default configuration. */
- public static final String OREKIT_DATA_PATH = "orekit.data.path";
- /** Supported data providers. */
- private final List<DataProvider> providers;
- /** Supported filters.
- * @since 9.2
- */
- private final List<DataFilter> filters;
- /** Number of predefined filters. */
- private final int predefinedFilters;
- /** Loaded data. */
- private final Set<String> loaded;
- /** Build an instance with default configuration.
- * <p>
- * This is a singleton, so the constructor is private.
- * </p>
- */
- private DataProvidersManager() {
- providers = new ArrayList<DataProvider>();
- filters = new ArrayList<>();
- loaded = new LinkedHashSet<String>();
- // set up predefined filters
- addFilter(new GzipFilter());
- addFilter(new UnixCompressFilter());
- predefinedFilters = filters.size();
- }
- /** Get the unique instance.
- * @return unique instance of the manager.
- */
- public static DataProvidersManager getInstance() {
- return LazyHolder.INSTANCE;
- }
- /** Add the default providers configuration.
- * <p>
- * The default configuration contains one {@link DataProvider data provider}
- * for each component of the path-like list specified by the java property
- * <code>orekit.data.path</code>.
- * </p>
- * <p>
- * If the property is not set or is null, no data will be available to the library
- * (for example no pole corrections will be applied and only predefined UTC steps
- * will be taken into account). No errors will be triggered in this case.
- * </p>
- * <p>
- * If the property is set, it must contains a list of existing directories or zip/jar
- * archives. One {@link DirectoryCrawler} instance will be set up for each
- * directory and one {@link ZipJarCrawler} instance (configured to look for the
- * archive in the filesystem) will be set up for each zip/jar archive. The list
- * elements in the java property are separated using the standard path separator for
- * the operating system as returned by {@link System#getProperty(String)
- * System.getProperty("path.separator")}. This standard path separator is ":" on
- * Linux and Unix type systems and ";" on Windows types systems.
- * </p>
- * @exception OrekitException if an element of the list does not exist or exists but
- * is neither a directory nor a zip/jar archive
- */
- public void addDefaultProviders() throws OrekitException {
- // get the path containing all components
- final String path = System.getProperty(OREKIT_DATA_PATH);
- if ((path != null) && !"".equals(path)) {
- // extract the various components
- for (final String name : path.split(System.getProperty("path.separator"))) {
- if (!"".equals(name)) {
- final File file = new File(name);
- // check component
- if (!file.exists()) {
- if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) {
- throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, name);
- } else {
- throw new OrekitException(OrekitMessages.DATA_ROOT_DIRECTORY_DOES_NOT_EXIST, name);
- }
- }
- if (file.isDirectory()) {
- addProvider(new DirectoryCrawler(file));
- } else if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) {
- addProvider(new ZipJarCrawler(file));
- } else {
- throw new OrekitException(OrekitMessages.NEITHER_DIRECTORY_NOR_ZIP_OR_JAR, name);
- }
- }
- }
- }
- }
- /** Add a data provider to the supported list.
- * @param provider data provider to add
- * @see #removeProvider(DataProvider)
- * @see #clearProviders()
- * @see #isSupported(DataProvider)
- * @see #getProviders()
- */
- public void addProvider(final DataProvider provider) {
- providers.add(provider);
- }
- /** Remove one provider.
- * @param provider provider instance to remove
- * @return instance removed (null if the provider was not already present)
- * @see #addProvider(DataProvider)
- * @see #clearProviders()
- * @see #isSupported(DataProvider)
- * @see #getProviders()
- * @since 5.1
- */
- public DataProvider removeProvider(final DataProvider provider) {
- for (final Iterator<DataProvider> iterator = providers.iterator(); iterator.hasNext();) {
- final DataProvider current = iterator.next();
- if (current == provider) {
- iterator.remove();
- return provider;
- }
- }
- return null;
- }
- /** Remove all data providers.
- * @see #addProvider(DataProvider)
- * @see #removeProvider(DataProvider)
- * @see #isSupported(DataProvider)
- * @see #getProviders()
- */
- public void clearProviders() {
- providers.clear();
- }
- /** Add a data filter.
- * @param filter filter to add
- * @see #applyAllFilters(NamedData)
- * @see #clearFilters()
- * @since 9.2
- */
- public void addFilter(final DataFilter filter) {
- filters.add(filter);
- }
- /** Remove all data filters, except the predefined ones.
- * @see #addFilter(DataFilter)
- * @since 9.2
- */
- public void clearFilters() {
- for (int i = filters.size() - 1; i >= predefinedFilters; --i) {
- filters.remove(i);
- }
- }
- /** Apply all the relevant data filters, taking care of layers.
- * <p>
- * If several filters can be applied, they will all be applied
- * as a stack, even recursively if required. This means that if
- * filter A applies to files with names of the form base.ext.a
- * and filter B applies to files with names of the form base.ext.b,
- * then providing base.ext.a.b.a will result in filter A being
- * applied on top of filter B which itself is applied on top of
- * another instance of filter A.
- * </p>
- * @param original original named data
- * @return fully filtered named data
- * @exception IOException if some data stream cannot be filtered
- * @see #addFilter(DataFilter)
- * @see #clearFilters()
- * @since 9.2
- */
- public NamedData applyAllFilters(final NamedData original)
- throws IOException {
- NamedData top = original;
- for (boolean filtering = true; filtering;) {
- filtering = false;
- for (final DataFilter filter : filters) {
- final NamedData filtered = filter.filter(top);
- if (filtered != top) {
- // the filter has been applied, we need to restart the loop
- top = filtered;
- filtering = true;
- break;
- }
- }
- }
- return top;
- }
- /** Check if some provider is supported.
- * @param provider provider to check
- * @return true if the specified provider instance is already in the supported list
- * @see #addProvider(DataProvider)
- * @see #removeProvider(DataProvider)
- * @see #clearProviders()
- * @see #getProviders()
- * @since 5.1
- */
- public boolean isSupported(final DataProvider provider) {
- for (final DataProvider current : providers) {
- if (current == provider) {
- return true;
- }
- }
- return false;
- }
- /** Get an unmodifiable view of the list of supported providers.
- * @return unmodifiable view of the list of supported providers
- * @see #addProvider(DataProvider)
- * @see #removeProvider(DataProvider)
- * @see #clearProviders()
- * @see #isSupported(DataProvider)
- */
- public List<DataProvider> getProviders() {
- return Collections.unmodifiableList(providers);
- }
- /** Get an unmodifiable view of the set of data file names that have been loaded.
- * <p>
- * The names returned are exactly the ones that were given to the {@link
- * DataLoader#loadData(InputStream, String) DataLoader.loadData} method.
- * </p>
- * @return unmodifiable view of the set of data file names that have been loaded
- * @see #feed(String, DataLoader)
- * @see #clearLoadedDataNames()
- */
- public Set<String> getLoadedDataNames() {
- return Collections.unmodifiableSet(loaded);
- }
- /** Clear the set of data file names that have been loaded.
- * @see #getLoadedDataNames()
- */
- public void clearLoadedDataNames() {
- loaded.clear();
- }
- /** Feed a data file loader by browsing all data providers.
- * <p>
- * If this method is called with an empty list of providers, a default
- * providers configuration is set up. This default configuration contains
- * only one {@link DataProvider data provider}: a {@link DirectoryCrawler}
- * instance that loads data from files located somewhere in a directory hierarchy.
- * This default provider is <em>not</em> added if the list is not empty. If users
- * want to have both the default provider and other providers, they must add it
- * explicitly.
- * </p>
- * <p>
- * The providers are used in the order in which they were {@link #addProvider(DataProvider)
- * added}. As soon as one provider is able to feed the data loader, the loop is
- * stopped. If no provider is able to feed the data loader, then the last error
- * triggered is thrown.
- * </p>
- * @param supportedNames regular expression for file names supported by the visitor
- * @param loader data loader to use
- * @return true if some data has been loaded
- * @exception OrekitException if the data loader cannot be fed (read error ...)
- * or if the default configuration cannot be set up
- */
- public boolean feed(final String supportedNames, final DataLoader loader)
- throws OrekitException {
- final Pattern supported = Pattern.compile(supportedNames);
- // set up a default configuration if no providers have been set
- if (providers.isEmpty()) {
- addDefaultProviders();
- }
- // monitor the data that the loader will load
- final DataLoader monitoredLoader = new MonitoringWrapper(loader);
- // crawl the data collection
- OrekitException delayedException = null;
- for (final DataProvider provider : providers) {
- try {
- // try to feed the visitor using the current provider
- if (provider.feed(supported, monitoredLoader)) {
- return true;
- }
- } catch (OrekitException oe) {
- // remember the last error encountered
- delayedException = oe;
- }
- }
- if (delayedException != null) {
- throw delayedException;
- }
- return false;
- }
- /** Data loading monitoring wrapper class. */
- private class MonitoringWrapper implements DataLoader {
- /** Wrapped loader. */
- private final DataLoader loader;
- /** Simple constructor.
- * @param loader loader to monitor
- */
- MonitoringWrapper(final DataLoader loader) {
- this.loader = loader;
- }
- /** {@inheritDoc} */
- public boolean stillAcceptsData() {
- // delegate to monitored loader
- return loader.stillAcceptsData();
- }
- /** {@inheritDoc} */
- public void loadData(final InputStream input, final String name)
- throws IOException, ParseException, OrekitException {
- // delegate to monitored loader
- loader.loadData(input, name);
- // monitor the fact new data has been loaded
- loaded.add(name);
- }
- }
- /** Holder for the manager singleton.
- * <p>
- * We use the Initialization on demand holder idiom to store
- * the singletons, as it is both thread-safe, efficient (no
- * synchronization) and works with all versions of java.
- * </p>
- */
- private static class LazyHolder {
- /** Unique instance. */
- private static final DataProvidersManager INSTANCE = new DataProvidersManager();
- /** Private constructor.
- * <p>This class is a utility class, it should neither have a public
- * nor a default constructor. This private constructor prevents
- * the compiler from generating one automatically.</p>
- */
- private LazyHolder() {
- }
- }
- }