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;
18
19 import org.hipparchus.exception.DummyLocalizable;
20 import org.hipparchus.exception.Localizable;
21 import org.hipparchus.geometry.euclidean.threed.Vector3D;
22 import org.hipparchus.util.FastMath;
23 import org.orekit.errors.OrekitException;
24 import org.orekit.errors.OrekitMessages;
25 import org.orekit.frames.Frame;
26 import org.orekit.frames.FramesFactory;
27 import org.orekit.frames.Predefined;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.time.TimeComponents;
30 import org.orekit.time.TimeScale;
31
32 import java.io.BufferedReader;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.lang.reflect.Array;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.NoSuchElementException;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46 /** Simple parser for key/value files.
47 * @param Key type of the parameter keys
48 */
49 public class KeyValueFileParser<Key extends Enum<Key>> {
50
51 /** Error message for unknown frame. */
52 private static final Localizable UNKNOWN_FRAME =
53 new DummyLocalizable("unknown frame {0}");
54
55 /** Error message for not Earth frame. */
56 private static final Localizable NOT_EARTH_FRAME =
57 new DummyLocalizable("frame {0} is not an Earth frame");
58
59 /** Enum type. */
60 private final Class<Key> enumType;
61
62 /** Key/scalar value map. */
63 private final Map<Key, String> scalarMap;
64
65 /** Key/array value map. */
66 private final Map<Key, List<String>> arrayMap;
67
68 /** Simple constructor.
69 * @param enumType type of the parameters keys enumerate
70 */
71 public KeyValueFileParser(Class<Key> enumType) {
72 this.enumType = enumType;
73 this.scalarMap = new HashMap<Key, String>();
74 this.arrayMap = new HashMap<Key, List<String>>();
75 }
76
77 /** Parse an input file.
78 * <p>
79 * The input file syntax is a set of {@code key=value} lines or
80 * {@code key[i]=value} lines. Blank lines and lines starting with '#'
81 * (after whitespace trimming) are silently ignored. The equal sign may
82 * be surrounded by space characters. Keys must correspond to the
83 * {@link Key} enumerate constants, given that matching is not case
84 * sensitive and that '_' characters may appear as '.' characters in
85 * the file. This means that the lines:
86 * <pre>
87 * # this is the semi-major axis
88 * orbit.circular.a = 7231582
89 * </pre>
90 * are perfectly right and correspond to key {@code Key#ORBIT_CIRCULAR_A} if
91 * such a constant exists in the enumerate.
92 * </p>
93 * <p>
94 * When the key[i] notation is used, all the values extracted from lines
95 * with the same key will be put in a general array that will be retrieved
96 * using one of the {@code getXxxArray(key)} methods. They will <em>not</em>
97 * be available with the {@code getXxx(key)} methods without the {@code Array}.
98 * </p>
99 * @param input input stream
100 * @exception IOException if input file cannot be read
101 */
102 public void parseInput(final String name, final InputStream input)
103 throws IOException, OrekitException {
104
105 final Pattern arrayPattern = Pattern.compile("([\\w\\.]+)\\s*\\[([0-9]+)\\]");
106 final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
107 int lineNumber = 0;
108 for (String line = reader.readLine(); line != null; line = reader.readLine()) {
109 ++lineNumber;
110 line = line.trim();
111 // we ignore blank lines and line starting with '#'
112 if ((line.length() > 0) && !line.startsWith("#")) {
113 String[] fields = line.split("\\s*=\\s*");
114 if (fields.length != 2) {
115 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
116 lineNumber, name, line);
117 }
118 final Matcher matcher = arrayPattern.matcher(fields[0]);
119 if (matcher.matches()) {
120 // this is a key[i]=value line
121 String canonicalized = matcher.group(1).toUpperCase().replaceAll("\\.", "_");
122 Key key = Key.valueOf(enumType, canonicalized);
123 Integer index = Integer.valueOf(matcher.group(2));
124 List<String> list = arrayMap.get(key);
125 if (list == null) {
126 list = new ArrayList<String>();
127 arrayMap.put(key, list);
128 }
129 while (index >= list.size()) {
130 // insert empty strings for missing elements
131 list.add("");
132 }
133 list.set(index, fields[1]);
134 } else {
135 // this is a key=value line
136 String canonicalized = fields[0].toUpperCase().replaceAll("\\.", "_");
137 Key key = Key.valueOf(enumType, canonicalized);
138 scalarMap.put(key, fields[1]);
139 }
140 }
141 }
142 reader.close();
143
144 }
145
146 /** Check if a key is contained in the map.
147 * @param key parameter key
148 * @return true if the key is contained in the map
149 */
150 public boolean containsKey(final Key key) {
151 return scalarMap.containsKey(key) || arrayMap.containsKey(key);
152 }
153
154 /** Get a raw string value from a parameters map.
155 * @param key parameter key
156 * @return string value corresponding to the key
157 * @exception NoSuchElementException if key is not in the map
158 */
159 public String getString(final Key key) throws NoSuchElementException {
160 final String value = scalarMap.get(key);
161 if (value == null) {
162 throw new NoSuchElementException(key.toString());
163 }
164 return value.trim();
165 }
166
167 /** Get a raw string values array from a parameters map.
168 * @param key parameter key
169 * @return string values array corresponding to the key
170 * @exception NoSuchElementException if key is not in the map
171 */
172 public String[] getStringArray(final Key key) throws NoSuchElementException {
173 final List<String> list = arrayMap.get(key);
174 if (list == null) {
175 throw new NoSuchElementException(key.toString());
176 }
177 String[] array = new String[list.size()];
178 for (int i = 0; i < array.length; ++i) {
179 array[i] = list.get(i).trim();
180 }
181 return array;
182 }
183
184 /** Get a raw double value from a parameters map.
185 * @param key parameter key
186 * @return double value corresponding to the key
187 * @exception NoSuchElementException if key is not in the map
188 */
189 public double getDouble(final Key key) throws NoSuchElementException {
190 return Double.parseDouble(getString(key));
191 }
192
193 /** Get a raw double values array from a parameters map.
194 * @param key parameter key
195 * @return double values array corresponding to the key
196 * @exception NoSuchElementException if key is not in the map
197 */
198 public double[] getDoubleArray(final Key key) throws NoSuchElementException {
199 String[] strings = getStringArray(key);
200 double[] array = new double[strings.length];
201 for (int i = 0; i < array.length; ++i) {
202 array[i] = Double.parseDouble(strings[i]);
203 }
204 return array;
205 }
206
207 /** Get a raw int value from a parameters map.
208 * @param key parameter key
209 * @return int value corresponding to the key
210 * @exception NoSuchElementException if key is not in the map
211 */
212 public int getInt(final Key key) throws NoSuchElementException {
213 return Integer.parseInt(getString(key));
214 }
215
216 /** Get a raw int values array from a parameters map.
217 * @param key parameter key
218 * @return int values array corresponding to the key
219 * @exception NoSuchElementException if key is not in the map
220 */
221 public int[] getIntArray(final Key key) throws NoSuchElementException {
222 String[] strings = getStringArray(key);
223 int[] array = new int[strings.length];
224 for (int i = 0; i < array.length; ++i) {
225 array[i] = Integer.parseInt(strings[i]);
226 }
227 return array;
228 }
229
230 /** Get a raw boolean value from a parameters map.
231 * @param key parameter key
232 * @return boolean value corresponding to the key
233 * @exception NoSuchElementException if key is not in the map
234 */
235 public boolean getBoolean(final Key key) throws NoSuchElementException {
236 return Boolean.parseBoolean(getString(key));
237 }
238
239 /** Get a raw boolean values array from a parameters map.
240 * @param key parameter key
241 * @return boolean values array corresponding to the key
242 * @exception NoSuchElementException if key is not in the map
243 */
244 public boolean[] getBooleanArray(final Key key) throws NoSuchElementException {
245 String[] strings = getStringArray(key);
246 boolean[] array = new boolean[strings.length];
247 for (int i = 0; i < array.length; ++i) {
248 array[i] = Boolean.parseBoolean(strings[i]);
249 }
250 return array;
251 }
252
253 /** Get an angle value from a parameters map.
254 * <p>
255 * The angle is considered to be in degrees in the file, it will be returned in radians
256 * </p>
257 * @param key parameter key
258 * @return angular value corresponding to the key, in radians
259 * @exception NoSuchElementException if key is not in the map
260 */
261 public double getAngle(final Key key) throws NoSuchElementException {
262 return FastMath.toRadians(getDouble(key));
263 }
264
265 /** Get an angle values array from a parameters map.
266 * @param key parameter key
267 * @return angle values array corresponding to the key
268 * @exception NoSuchElementException if key is not in the map
269 */
270 public double[] getAngleArray(final Key key) throws NoSuchElementException {
271 double[] array = getDoubleArray(key);
272 for (int i = 0; i < array.length; ++i) {
273 array[i] = FastMath.toRadians(array[i]);
274 }
275 return array;
276 }
277
278 /** Get a date value from a parameters map.
279 * @param key parameter key
280 * @param scale time scale in which the date is to be parsed
281 * @return date value corresponding to the key
282 * @exception NoSuchElementException if key is not in the map
283 */
284 public AbsoluteDate getDate(final Key key, TimeScale scale) throws NoSuchElementException {
285 return new AbsoluteDate(getString(key), scale);
286 }
287
288 /** Get a date values array from a parameters map.
289 * @param key parameter key
290 * @param scale time scale in which the date is to be parsed
291 * @return date values array corresponding to the key
292 * @exception NoSuchElementException if key is not in the map
293 */
294 public AbsoluteDate[] getDateArray(final Key key, TimeScale scale) throws NoSuchElementException {
295 String[] strings = getStringArray(key);
296 AbsoluteDate[] array = new AbsoluteDate[strings.length];
297 for (int i = 0; i < array.length; ++i) {
298 array[i] = new AbsoluteDate(strings[i], scale);
299 }
300 return array;
301 }
302
303 /** Get a time value from a parameters map.
304 * @param key parameter key
305 * @return time value corresponding to the key
306 * @exception NoSuchElementException if key is not in the map
307 */
308 public TimeComponents getTime(final Key key) throws NoSuchElementException {
309 return TimeComponents.parseTime(getString(key));
310 }
311
312 /** Get a time values array from a parameters map.
313 * @param key parameter key
314 * @return time values array corresponding to the key
315 * @exception NoSuchElementException if key is not in the map
316 */
317 public TimeComponents[] getTimeArray(final Key key) throws NoSuchElementException {
318 String[] strings = getStringArray(key);
319 TimeComponents[] array = new TimeComponents[strings.length];
320 for (int i = 0; i < array.length; ++i) {
321 array[i] = TimeComponents.parseTime(strings[i]);
322 }
323 return array;
324 }
325
326 /** Get a vector value from a parameters map.
327 * @param xKey parameter key for abscissa
328 * @param yKey parameter key for ordinate
329 * @param zKey parameter key for height
330 * @return date value corresponding to the key
331 * @exception NoSuchElementException if key is not in the map
332 */
333 public Vector3D getVector(final Key xKey, final Key yKey, final Key zKey)
334 throws NoSuchElementException {
335 return new Vector3D(getDouble(xKey), getDouble(yKey), getDouble(zKey));
336 }
337
338 /** Get a vector values array from a parameters map.
339 * @param xKey parameter key for abscissa
340 * @param yKey parameter key for ordinate
341 * @param zKey parameter key for height
342 * @return date value corresponding to the key
343 * @exception NoSuchElementException if key is not in the map
344 */
345 public Vector3D[] getVectorArray(final Key xKey, final Key yKey, final Key zKey)
346 throws NoSuchElementException {
347 String[] xStrings = getStringArray(xKey);
348 String[] yStrings = getStringArray(yKey);
349 String[] zStrings = getStringArray(zKey);
350 Vector3D[] array = new Vector3D[xStrings.length];
351 for (int i = 0; i < array.length; ++i) {
352 array[i] = new Vector3D(Double.parseDouble(xStrings[i]),
353 Double.parseDouble(yStrings[i]),
354 Double.parseDouble(zStrings[i]));
355 }
356 return array;
357 }
358
359 /** Get a strings list from a parameters map.
360 * @param key parameter key
361 * @param separator elements separator
362 * @return strings list value corresponding to the key
363 * @exception NoSuchElementException if key is not in the map
364 */
365 public List<String> getStringsList(final Key key, final char separator)
366 throws NoSuchElementException {
367 final String value = scalarMap.get(key);
368 if (value == null) {
369 throw new NoSuchElementException(key.toString());
370 }
371 return Arrays.asList(value.trim().split("\\s*" + separator + "\\s*"));
372 }
373
374 /** Get a strings list array from a parameters map.
375 * @param key parameter key
376 * @param separator elements separator
377 * @return strings list array corresponding to the key
378 * @exception NoSuchElementException if key is not in the map
379 */
380 public List<String>[] getStringsListArray(final Key key, final char separator)
381 throws NoSuchElementException {
382 final String[] strings = getStringArray(key);
383 @SuppressWarnings("unchecked")
384 final List<String>[] array = (List<String>[]) Array.newInstance(List.class, strings.length);
385 for (int i = 0; i < array.length; ++i) {
386 array[i] = Arrays.asList(strings[i].trim().split("\\s*" + separator + "\\s*"));
387 }
388 return array;
389 }
390
391 /** Get an inertial frame from a parameters map.
392 * @param key parameter key
393 * @return inertial frame corresponding to the key
394 * @exception NoSuchElementException if key is not in the map
395 */
396 public Frame getInertialFrame(final Key key) throws NoSuchElementException, OrekitException {
397
398 // get the name of the desired frame
399 final String frameName = getString(key);
400
401 // check the name against predefined frames
402 for (Predefined predefined : Predefined.values()) {
403 if (frameName.equals(predefined.getName())) {
404 if (FramesFactory.getFrame(predefined).isPseudoInertial()) {
405 return FramesFactory.getFrame(predefined);
406 } else {
407 throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME,
408 frameName);
409 }
410 }
411 }
412
413 // none of the frames match the name
414 throw new OrekitException(UNKNOWN_FRAME, frameName);
415
416 }
417
418 /** Get an Earth frame from a parameters map.
419 * <p>
420 * We consider Earth frames are the frames with name starting with "ITRF".
421 * </p>
422 * @param key parameter key
423 * @return Earth frame corresponding to the key
424 * @exception NoSuchElementException if key is not in the map
425 */
426 public Frame getEarthFrame(final Key key)
427 throws NoSuchElementException, OrekitException {
428
429 // get the name of the desired frame
430 final String frameName = getString(key);
431
432 // check the name against predefined frames
433 for (Predefined predefined : Predefined.values()) {
434 if (frameName.equals(predefined.getName())) {
435 if (predefined.toString().startsWith("ITRF") ||
436 predefined.toString().startsWith("GTOD")) {
437 return FramesFactory.getFrame(predefined);
438 } else {
439 throw new OrekitException(NOT_EARTH_FRAME, frameName);
440 }
441 }
442 }
443
444 // none of the frames match the name
445 throw new OrekitException(UNKNOWN_FRAME, frameName);
446
447 }
448
449 }