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 }