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.files.sinex;
18
19 import org.hipparchus.exception.LocalizedCoreFormats;
20 import org.orekit.data.DataSource;
21 import org.orekit.errors.OrekitException;
22 import org.orekit.errors.OrekitMessages;
23 import org.orekit.time.TimeScales;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.Reader;
28 import java.util.Collections;
29
30 /** Base parser for Solution INdependent EXchange (SINEX) files.
31 * @param <T> type of the SINEX file
32 * @param <P> type of the SINEX files parse info
33 * @author Luc Maisonobe
34 * @since 13.0
35 */
36 public abstract class AbstractSinexParser<T extends AbstractSinex, P extends ParseInfo<T>> {
37
38 /** Time scales. */
39 private final TimeScales timeScales;
40
41 /** Simple constructor.
42 * @param timeScales time scales
43 */
44 protected AbstractSinexParser(final TimeScales timeScales) {
45 this.timeScales = timeScales;
46 }
47
48 /** Parse one or more SINEX files.
49 * @param sources sources providing the data to parse
50 * @return parsed file combining all sources
51 */
52 public T parse(final DataSource... sources) {
53
54 // placeholders for parsed data
55 final P parseInfo = buildParseInfo();
56
57 for (final DataSource source : sources) {
58
59 // start parsing a new file
60 parseInfo.newSource(source.getName());
61 Iterable<LineParser<P>> candidateParsers = Collections.singleton(firstLineParser());
62
63 try (Reader reader = source.getOpener().openReaderOnce(); BufferedReader br = new BufferedReader(reader)) {
64 nextLine:
65 for (parseInfo.setLine(br.readLine()); parseInfo.getLine() != null; parseInfo.setLine(br.readLine())) {
66 parseInfo.incrementLineNumber();
67
68 // ignore all comment lines
69 if (parseInfo.getLine().charAt(0) == '*') {
70 continue;
71 }
72
73 // find a parser for the current line among the available candidates
74 for (final LineParser<P> candidate : candidateParsers) {
75 try {
76 if (candidate.parseIfRecognized(parseInfo)) {
77 // candidate successfully parsed the line
78 candidateParsers = candidate.allowedNextParsers(parseInfo);
79 continue nextLine;
80 }
81 } catch (StringIndexOutOfBoundsException | NumberFormatException e) {
82 throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
83 parseInfo.getLineNumber(), parseInfo.getName(),
84 parseInfo.getLine());
85 }
86 }
87
88 // no candidate could parse this line
89 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
90 parseInfo.getLineNumber(), parseInfo.getName(),
91 parseInfo.getLine());
92
93 }
94
95 } catch (IOException ioe) {
96 throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
97 }
98
99 }
100
101 return parseInfo.build();
102
103 }
104
105 /** Get parser for the first line.
106 * @return parser for the firsty line of the file
107 */
108 protected abstract LineParser<P> firstLineParser();
109
110 /** Build the container for parsing info.
111 * @return container for parsing info
112 */
113 protected abstract P buildParseInfo();
114
115 /** Get the time scales.
116 * @return time scales
117 */
118 public TimeScales getTimeScales() {
119 return timeScales;
120 }
121
122 }