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 if (!parseInfo.getLine().isEmpty()) {
69
70 // ignore all comment lines
71 if (parseInfo.getLine().charAt(0) == '*') {
72 continue;
73 }
74
75 // find a parser for the current line among the available candidates
76 for (final LineParser<P> candidate : candidateParsers) {
77 try {
78 if (candidate.parseIfRecognized(parseInfo)) {
79 // candidate successfully parsed the line
80 candidateParsers = candidate.allowedNextParsers(parseInfo);
81 continue nextLine;
82 }
83 }
84 catch (StringIndexOutOfBoundsException | NumberFormatException e) {
85 throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
86 parseInfo.getLineNumber(), parseInfo.getName(),
87 parseInfo.getLine());
88 }
89 }
90
91 // no candidate could parse this line
92 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
93 parseInfo.getLineNumber(), parseInfo.getName(),
94 parseInfo.getLine());
95 }
96
97 }
98
99 } catch (IOException ioe) {
100 throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
101 }
102
103 }
104
105 return parseInfo.build();
106
107 }
108
109 /** Get parser for the first line.
110 * @return parser for the firsty line of the file
111 */
112 protected abstract LineParser<P> firstLineParser();
113
114 /** Build the container for parsing info.
115 * @return container for parsing info
116 */
117 protected abstract P buildParseInfo();
118
119 /** Get the time scales.
120 * @return time scales
121 */
122 public TimeScales getTimeScales() {
123 return timeScales;
124 }
125
126 }