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 }