1 /* Copyright 2002-2024 CS GROUP 2 * Licensed to CS GROUP (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 19 import java.io.File; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.text.ParseException; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.Iterator; 26 import java.util.LinkedHashSet; 27 import java.util.List; 28 import java.util.Set; 29 import java.util.regex.Pattern; 30 31 import org.orekit.errors.OrekitException; 32 import org.orekit.errors.OrekitMessages; 33 import org.orekit.files.rinex.HatanakaCompressFilter; 34 35 /** This class manages supported {@link DataProvider data providers}. 36 * <p> 37 * This class is the primary point of access for all data loading features. It 38 * is used for example to load Earth Orientation Parameters used by IERS frames, 39 * to load UTC leap seconds used by time scales, to load planetary ephemerides... 40 * 41 * <p> 42 * It is user-customizable: users can add their own data providers at will. This 43 * allows them for example to use a database or an existing data loading library 44 * in order to embed an Orekit enabled application in a global system with its 45 * own data handling mechanisms. There is no upper limitation on the number of 46 * providers, but often each application will use only a few. 47 * </p> 48 * 49 * <p> 50 * If the list of providers is empty when attempting to {@link #feed(String, DataLoader) 51 * feed} a file loader, the {@link #addDefaultProviders()} method is called 52 * automatically to set up a default configuration. This default configuration 53 * contains one {@link DataProvider data provider} for each component of the 54 * path-like list specified by the java property <code>orekit.data.path</code>. 55 * See the {@link #feed(String, DataLoader) feed} method documentation for further 56 * details. The default providers configuration is <em>not</em> set up if the list 57 * is not empty. If users want to have both the default providers and additional 58 * providers, they must call explicitly the {@link #addDefaultProviders()} method. 59 * </p> 60 * 61 * <p> 62 * The default configuration uses a predefined set of {@link DataFilter data filters} 63 * that already handled gzip-compressed files (recognized by the {@code .gz} suffix), 64 * Unix-compressed files (recognized by the {@code .Z} suffix) and Hatanaka compressed 65 * RINEX files. Users can access the {@link #getFiltersManager() filters manager} to 66 * set up custom filters for handling specific types of filters (decompression, 67 * deciphering...). 68 * </p> 69 * 70 * @author Luc Maisonobe 71 * @see DirectoryCrawler 72 * @see ClasspathCrawler 73 */ 74 public class DataProvidersManager { 75 76 /** Name of the property defining the root directories or zip/jar files path for default configuration. */ 77 public static final String OREKIT_DATA_PATH = "orekit.data.path"; 78 79 /** Supported data providers. */ 80 private final List<DataProvider> providers; 81 82 /** Manager for filters. 83 * @since 11.0 84 */ 85 private final FiltersManager filtersManager; 86 87 /** Loaded data. */ 88 private final Set<String> loaded; 89 90 /** Build an instance with default configuration. */ 91 public DataProvidersManager() { 92 providers = new ArrayList<>(); 93 filtersManager = new FiltersManager(); 94 loaded = new LinkedHashSet<>(); 95 resetFiltersToDefault(); 96 } 97 98 /** Get the manager for filters. 99 * @return filters manager 100 * @since 11.0 101 */ 102 public FiltersManager getFiltersManager() { 103 return filtersManager; 104 } 105 106 /** Reset all filters to default. 107 * <p> 108 * This method {@link FiltersManager#clearFilters() clears} the 109 * {@link #getFiltersManager() filter manager} and then 110 * {@link FiltersManager#addFilter(DataFilter) adds} back the 111 * default filters 112 * </p> 113 * @since 11.0 114 */ 115 public void resetFiltersToDefault() { 116 117 // clear the existing filters 118 filtersManager.clearFilters(); 119 120 // set up predefined filters 121 filtersManager.addFilter(new GzipFilter()); 122 filtersManager.addFilter(new UnixCompressFilter()); 123 filtersManager.addFilter(new HatanakaCompressFilter()); 124 125 } 126 127 /** Add the default providers configuration. 128 * <p> 129 * The default configuration contains one {@link DataProvider data provider} 130 * for each component of the path-like list specified by the java property 131 * <code>orekit.data.path</code>. 132 * </p> 133 * <p> 134 * If the property is not set or is null, no data will be available to the library 135 * (for example no pole corrections will be applied and only predefined UTC steps 136 * will be taken into account). No errors will be triggered in this case. 137 * </p> 138 * <p> 139 * If the property is set, it must contains a list of existing directories or zip/jar 140 * archives. One {@link DirectoryCrawler} instance will be set up for each 141 * directory and one {@link ZipJarCrawler} instance (configured to look for the 142 * archive in the filesystem) will be set up for each zip/jar archive. The list 143 * elements in the java property are separated using the standard path separator for 144 * the operating system as returned by {@link System#getProperty(String) 145 * System.getProperty("path.separator")}. This standard path separator is ":" on 146 * Linux and Unix type systems and ";" on Windows types systems. 147 * </p> 148 */ 149 public void addDefaultProviders() { 150 151 // get the path containing all components 152 final String path = System.getProperty(OREKIT_DATA_PATH); 153 if (path != null && !"".equals(path)) { 154 155 // extract the various components 156 for (final String name : path.split(System.getProperty("path.separator"))) { 157 if (!"".equals(name)) { 158 159 final File file = new File(name); 160 161 // check component 162 if (!file.exists()) { 163 if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) { 164 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, name); 165 } else { 166 throw new OrekitException(OrekitMessages.DATA_ROOT_DIRECTORY_DOES_NOT_EXIST, name); 167 } 168 } 169 170 if (file.isDirectory()) { 171 addProvider(new DirectoryCrawler(file)); 172 } else if (DataProvider.ZIP_ARCHIVE_PATTERN.matcher(name).matches()) { 173 addProvider(new ZipJarCrawler(file)); 174 } else { 175 throw new OrekitException(OrekitMessages.NEITHER_DIRECTORY_NOR_ZIP_OR_JAR, name); 176 } 177 178 } 179 } 180 } 181 182 } 183 184 /** Add a data provider to the supported list. 185 * @param provider data provider to add 186 * @see #removeProvider(DataProvider) 187 * @see #clearProviders() 188 * @see #isSupported(DataProvider) 189 * @see #getProviders() 190 */ 191 public void addProvider(final DataProvider provider) { 192 providers.add(provider); 193 } 194 195 /** Remove one provider. 196 * @param provider provider instance to remove 197 * @return instance removed (null if the provider was not already present) 198 * @see #addProvider(DataProvider) 199 * @see #clearProviders() 200 * @see #isSupported(DataProvider) 201 * @see #getProviders() 202 * @since 5.1 203 */ 204 public DataProvider removeProvider(final DataProvider provider) { 205 for (final Iterator<DataProvider> iterator = providers.iterator(); iterator.hasNext();) { 206 final DataProvider current = iterator.next(); 207 if (current == provider) { 208 iterator.remove(); 209 return provider; 210 } 211 } 212 return null; 213 } 214 215 /** Remove all data providers. 216 * @see #addProvider(DataProvider) 217 * @see #removeProvider(DataProvider) 218 * @see #isSupported(DataProvider) 219 * @see #getProviders() 220 */ 221 public void clearProviders() { 222 providers.clear(); 223 } 224 225 /** Check if some provider is supported. 226 * @param provider provider to check 227 * @return true if the specified provider instance is already in the supported list 228 * @see #addProvider(DataProvider) 229 * @see #removeProvider(DataProvider) 230 * @see #clearProviders() 231 * @see #getProviders() 232 * @since 5.1 233 */ 234 public boolean isSupported(final DataProvider provider) { 235 for (final DataProvider current : providers) { 236 if (current == provider) { 237 return true; 238 } 239 } 240 return false; 241 } 242 243 /** Get an unmodifiable view of the list of supported providers. 244 * @return unmodifiable view of the list of supported providers 245 * @see #addProvider(DataProvider) 246 * @see #removeProvider(DataProvider) 247 * @see #clearProviders() 248 * @see #isSupported(DataProvider) 249 */ 250 public List<DataProvider> getProviders() { 251 return Collections.unmodifiableList(providers); 252 } 253 254 /** Get an unmodifiable view of the set of data file names that have been loaded. 255 * <p> 256 * The names returned are exactly the ones that were given to the {@link 257 * DataLoader#loadData(InputStream, String) DataLoader.loadData} method. 258 * </p> 259 * @return unmodifiable view of the set of data file names that have been loaded 260 * @see #feed(String, DataLoader) 261 * @see #clearLoadedDataNames() 262 */ 263 public Set<String> getLoadedDataNames() { 264 return Collections.unmodifiableSet(loaded); 265 } 266 267 /** Clear the set of data file names that have been loaded. 268 * @see #getLoadedDataNames() 269 */ 270 public void clearLoadedDataNames() { 271 loaded.clear(); 272 } 273 274 /** Feed a data file loader by browsing all data providers. 275 * <p> 276 * If this method is called with an empty list of providers, a default 277 * providers configuration is set up. This default configuration contains 278 * only one {@link DataProvider data provider}: a {@link DirectoryCrawler} 279 * instance that loads data from files located somewhere in a directory hierarchy. 280 * This default provider is <em>not</em> added if the list is not empty. If users 281 * want to have both the default provider and other providers, they must add it 282 * explicitly. 283 * </p> 284 * <p> 285 * The providers are used in the order in which they were {@link #addProvider(DataProvider) 286 * added}. As soon as one provider is able to feed the data loader, the loop is 287 * stopped. If no provider is able to feed the data loader, then the last error 288 * triggered is thrown. 289 * </p> 290 * @param supportedNames regular expression for file names supported by the visitor 291 * @param loader data loader to use 292 * @return true if some data has been loaded 293 */ 294 public boolean feed(final String supportedNames, final DataLoader loader) { 295 296 final Pattern supported = Pattern.compile(supportedNames); 297 298 // set up a default configuration if no providers have been set 299 if (providers.isEmpty()) { 300 addDefaultProviders(); 301 } 302 303 // monitor the data that the loader will load 304 final DataLoader monitoredLoader = new MonitoringWrapper(loader); 305 306 // crawl the data collection 307 OrekitException delayedException = null; 308 for (final DataProvider provider : providers) { 309 try { 310 311 // try to feed the visitor using the current provider 312 if (provider.feed(supported, monitoredLoader, this)) { 313 return true; 314 } 315 316 } catch (OrekitException oe) { 317 // remember the last error encountered 318 delayedException = oe; 319 } 320 } 321 322 if (delayedException != null) { 323 throw delayedException; 324 } 325 326 return false; 327 328 } 329 330 /** Data loading monitoring wrapper class. */ 331 private class MonitoringWrapper implements DataLoader { 332 333 /** Wrapped loader. */ 334 private final DataLoader loader; 335 336 /** Simple constructor. 337 * @param loader loader to monitor 338 */ 339 MonitoringWrapper(final DataLoader loader) { 340 this.loader = loader; 341 } 342 343 /** {@inheritDoc} */ 344 public boolean stillAcceptsData() { 345 // delegate to monitored loader 346 return loader.stillAcceptsData(); 347 } 348 349 /** {@inheritDoc} */ 350 public void loadData(final InputStream input, final String name) 351 throws IOException, ParseException, OrekitException { 352 353 // delegate to monitored loader 354 loader.loadData(input, name); 355 356 // monitor the fact new data has been loaded 357 loaded.add(name); 358 359 } 360 361 } 362 363 }