1   /* Copyright 2002-2025 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.IOException;
20  import java.io.InputStream;
21  import java.text.ParseException;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.regex.Pattern;
26  import java.util.stream.Collectors;
27  
28  import org.hipparchus.exception.DummyLocalizable;
29  import org.orekit.errors.OrekitException;
30  
31  
32  /** Provider for data files defined in a list.
33   * <p>
34   * All {@link FiltersManager#addFilter(DataFilter) registered}
35   * {@link DataFilter filters} are applied.
36   * </p>
37   * <p>
38   * Zip archives entries are supported recursively.
39   * </p>
40   * @since 10.1
41   * @see DataProvidersManager
42   * @see NetworkCrawler
43   * @see FilesListCrawler
44   * @author Luc Maisonobe
45   * @param <T> The type of resource, e.g. File or URL.
46   */
47  public abstract class AbstractListCrawler<T> implements DataProvider {
48  
49      /** Inputs list. */
50      private final List<T> inputs;
51  
52      /** Build a data classpath crawler.
53       * @param inputs list of inputs (may be empty if {@link #addInput(Object) addInput} is called later)
54       */
55      @SafeVarargs
56      protected AbstractListCrawler(final T... inputs) {
57          this.inputs = Arrays.stream(inputs).collect(Collectors.toList());
58      }
59  
60      /** Add an input to the supported list.
61       * @param input input to add
62       */
63      public void addInput(final T input) {
64          inputs.add(input);
65      }
66  
67      /** Get the list of inputs supported by the instance.
68       * @return unmodifiable view of the list of inputs supported by the instance
69       */
70      public List<T> getInputs() {
71          return Collections.unmodifiableList(inputs);
72      }
73  
74      /** Get the complete name of a input.
75       * @param input input to consider
76       * @return complete name of the input
77       */
78      protected abstract String getCompleteName(T input);
79  
80      /** Get the base name of an input.
81       * @param input input to consider
82       * @return base name of the input
83       */
84      protected abstract String getBaseName(T input);
85  
86      /** Get a zip/jar crawler for an input.
87       * @param input input to consider
88       * @return zip/jar crawler for an input
89       */
90      protected abstract ZipJarCrawler getZipJarCrawler(T input);
91  
92      /** Get the stream to read from an input.
93       * @param input input to read from
94       * @return stream to read the content of the input
95       * @throws IOException if the input cannot be opened for reading
96       */
97      protected abstract InputStream getStream(T input) throws IOException;
98  
99      /** {@inheritDoc} */
100     @Override
101     public boolean feed(final Pattern supported,
102                         final DataLoader visitor,
103                         final DataProvidersManager manager) {
104 
105         try {
106             OrekitException delayedException = null;
107             boolean loaded = false;
108             for (T input : inputs) {
109                 try {
110 
111                     if (visitor.stillAcceptsData()) {
112                         final String name     = getCompleteName(input);
113                         final String fileName = getBaseName(input);
114                         if (ZIP_ARCHIVE_PATTERN.matcher(fileName).matches()) {
115 
116                             // browse inside the zip/jar file
117                             getZipJarCrawler(input).feed(supported, visitor, manager);
118                             loaded = true;
119 
120                         } else {
121 
122                             // apply all registered filters
123                             DataSource data = new DataSource(fileName, () -> getStream(input));
124                             data = manager.getFiltersManager().applyRelevantFilters(data);
125 
126                             if (supported.matcher(data.getName()).matches()) {
127                                 // visit the current file
128                                 try (InputStream is = data.getOpener().openStreamOnce()) {
129                                     visitor.loadData(is, name);
130                                     loaded = true;
131                                 }
132                             }
133 
134                         }
135                     }
136 
137                 } catch (OrekitException oe) {
138                     // maybe the next path component will be able to provide data
139                     // wait until all components have been tried
140                     delayedException = oe;
141                 }
142             }
143 
144             if (!loaded && delayedException != null) {
145                 throw delayedException;
146             }
147 
148             return loaded;
149 
150         } catch (IOException | ParseException e) {
151             throw new OrekitException(e, new DummyLocalizable(e.getMessage()));
152         }
153 
154     }
155 
156 }