1   /* Copyright 2002-2020 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.FileInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.text.ParseException;
24  import java.util.Arrays;
25  import java.util.regex.Pattern;
26  
27  import org.hipparchus.exception.DummyLocalizable;
28  import org.orekit.annotation.DefaultDataContext;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitMessages;
31  
32  
33  /**  Provider for data files stored in a directories tree on filesystem.
34   * <p>
35   * This class handles data files recursively starting from a root directories
36   * tree. The organization of files in the directories is free. There may be
37   * sub-directories to any level. All sub-directories are browsed and all terminal
38   * files are checked for loading.
39   * </p>
40   * <p>
41   * All {@link DataProvidersManager#addFilter(DataFilter) registered}
42   * {@link DataFilter filters} are applied.
43   * </p>
44   * <p>
45   * Zip archives entries are supported recursively.
46   * </p>
47   * <p>
48   * This is a simple application of the <code>visitor</code> design pattern for
49   * directory hierarchy crawling.
50   * </p>
51   * @see DataProvidersManager
52   * @author Luc Maisonobe
53   */
54  public class DirectoryCrawler implements DataProvider {
55  
56      /** Root directory. */
57      private final File root;
58  
59      /** Build a data files crawler.
60       * @param root root of the directories tree (must be a directory)
61       */
62      public DirectoryCrawler(final File root) {
63          if (!root.isDirectory()) {
64              throw new OrekitException(OrekitMessages.NOT_A_DIRECTORY, root.getAbsolutePath());
65          }
66          this.root = root;
67      }
68  
69      @Override
70      @Deprecated
71      @DefaultDataContext
72      public boolean feed(final Pattern supported, final DataLoader visitor) {
73          return feed(supported, visitor, DataContext.getDefault().getDataProvidersManager());
74      }
75  
76      /** {@inheritDoc} */
77      public boolean feed(final Pattern supported,
78                          final DataLoader visitor,
79                          final DataProvidersManager manager) {
80          try {
81              return feed(supported, visitor, manager, root);
82          } catch (IOException | ParseException ioe) {
83              throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage()));
84          }
85      }
86  
87      /** Feed a data file loader by browsing a directory hierarchy.
88       * @param supported pattern for file names supported by the visitor
89       * @param visitor data file visitor to feed
90       * @param manager with the filters to apply.
91       * @param directory current directory
92       * @return true if something has been loaded
93       * @exception IOException if data cannot be read
94       * @exception ParseException if data cannot be read
95       */
96      private boolean feed(final Pattern supported,
97                           final DataLoader visitor,
98                           final DataProvidersManager manager,
99                           final File directory)
100         throws IOException, ParseException {
101 
102         // search in current directory
103         final File[] list = directory.listFiles();
104         if (list == null) {
105             // notify about race condition if directory is removed by another program
106             throw new OrekitException(OrekitMessages.NOT_A_DIRECTORY, directory.getAbsolutePath());
107         }
108         Arrays.sort(list, File::compareTo);
109 
110         OrekitException delayedException = null;
111         boolean loaded = false;
112         for (final File file : list) {
113             try {
114                 if (visitor.stillAcceptsData()) {
115                     if (file.isDirectory()) {
116 
117                         // recurse in the sub-directory
118                         loaded = feed(supported, visitor, manager, file) || loaded;
119 
120                     } else if (ZIP_ARCHIVE_PATTERN.matcher(file.getName()).matches()) {
121 
122                         // browse inside the zip/jar file
123                         final DataProvider zipProvider = new ZipJarCrawler(file);
124                         loaded = zipProvider.feed(supported, visitor, manager) || loaded;
125 
126                     } else {
127 
128                         // apply all registered filters
129                         NamedDataamedData">NamedData data = new NamedData(file.getName(), () -> new FileInputStream(file));
130                         data = manager.applyAllFilters(data);
131 
132                         if (supported.matcher(data.getName()).matches()) {
133                             // visit the current file
134                             try (InputStream input = data.getStreamOpener().openStream()) {
135                                 visitor.loadData(input, file.getPath());
136                                 loaded = true;
137                             }
138                         }
139 
140                     }
141                 }
142             } catch (OrekitException oe) {
143                 delayedException = oe;
144             }
145 
146         }
147 
148         if (!loaded && delayedException != null) {
149             throw delayedException;
150         }
151 
152         return loaded;
153 
154     }
155 
156 }