ParseInfo.java

  1. /* Copyright 2022-2025 Luc Maisonobe
  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. import org.hipparchus.util.FastMath;
  19. import org.orekit.time.AbsoluteDate;
  20. import org.orekit.time.DateComponents;
  21. import org.orekit.time.TimeScale;
  22. import org.orekit.time.TimeScales;
  23. import org.orekit.utils.Constants;
  24. import org.orekit.utils.units.Unit;

  25. import java.util.regex.Pattern;

  26. /** Transient data used for parsing a SINEX file.
  27.  * @param <T> type of the SINEX files
  28.  * @author Luc Maisonobe
  29.  * @since 13.0
  30.  */
  31. public abstract class ParseInfo<T extends AbstractSinex> {

  32.     /** 00:000:00000 epoch. */
  33.     private static final String DEFAULT_EPOCH_TWO_DIGITS = "00:000:00000";

  34.     /** 0000:000:00000 epoch. */
  35.     private static final String DEFAULT_EPOCH_FOUR_DIGITS = "0000:000:00000";

  36.     /** Pattern for delimiting regular expressions. */
  37.     private static final Pattern SEPARATOR = Pattern.compile(":");

  38.     /** Time scales. */
  39.     private final TimeScales timeScales;

  40.     /** Name of the data source. */
  41.     private String name;

  42.     /** Current line. */
  43.     private String line;

  44.     /** Current line number of the navigation message. */
  45.     private int lineNumber;

  46.     /** SINEX file creation date as extracted for the first line. */
  47.     private AbsoluteDate creationDate;

  48.     /** SINEX file creation date as extracted for the first line. */
  49.     private String creationDateString;

  50.     /** Start time of the data used in the Sinex solution. */
  51.     private AbsoluteDate startDate;

  52.     /** Start time of the data used in the Sinex solution. */
  53.     private String startDateString;

  54.     /** End time of the data used in the Sinex solution. */
  55.     private AbsoluteDate endDate;

  56.     /** End time of the data used in the Sinex solution. */
  57.     private String endDateString;

  58.     /** Time scale. */
  59.     private TimeScale timeScale;

  60.     /** Simple constructor.
  61.      * @param timeScales time scales
  62.      */
  63.     protected ParseInfo(final TimeScales timeScales) {
  64.         this.timeScales = timeScales;
  65.     }

  66.     /** Start parsing of a new data source.
  67.      * @param newName name of the new data source
  68.      */
  69.     void newSource(final String newName) {
  70.         // initialize parsing
  71.         this.name = newName;
  72.         this.line = null;
  73.         this.lineNumber = 0;
  74.     }

  75.     /** Get name of data source.
  76.      * @return name of data source
  77.      */
  78.     protected String getName() {
  79.         return name;
  80.     }

  81.     /** Set current line.
  82.      * @param line current line
  83.      */
  84.     void setLine(final String line)
  85.     {
  86.         this.line = line;
  87.     }

  88.     /** Get current line.
  89.      * @return current line
  90.      */
  91.     String getLine() {
  92.         return line;
  93.     }

  94.     /** Increment line number.
  95.      */
  96.     void incrementLineNumber() {
  97.         ++lineNumber;
  98.     }

  99.     /** Get current line number.
  100.      * @return current line number
  101.      */
  102.     int getLineNumber() {
  103.         return lineNumber;
  104.     }

  105.     /** Set creation date.
  106.      * @param dateString creation date
  107.      */
  108.     protected void setCreationDate(final String dateString) {
  109.         this.creationDateString = dateString;
  110.         this.creationDate       = stringEpochToAbsoluteDate(creationDateString, false);
  111.     }

  112.     /** Get creation date.
  113.      * @return creation date
  114.      */
  115.     protected AbsoluteDate getCreationDate() {
  116.         return creationDate;
  117.     }

  118.     /** Set start date if earlier than previous setting.
  119.      * @param candidateStartDateString candidate start date
  120.      */
  121.     protected void setStartDateIfEarlier(final String candidateStartDateString) {
  122.         final AbsoluteDate candidateStart = stringEpochToAbsoluteDate(candidateStartDateString, true);
  123.         if (startDate == null || candidateStart.isBefore(startDate)) {
  124.             // this is either the first setting
  125.             // or we are parsing a new data source referring to an earlier date than previous ones
  126.             this.startDateString = candidateStartDateString;
  127.             this.startDate = candidateStart;
  128.         }
  129.     }

  130.     /** Get start date.
  131.      * @return start date
  132.      */
  133.     protected AbsoluteDate getStartDate() {
  134.         return startDate;
  135.     }

  136.     /** Set end date if later than previous setting.
  137.      * @param candidateEndDateString end date
  138.      */
  139.     protected void setEndDateIfLater(final String candidateEndDateString) {
  140.         final AbsoluteDate candidateEnd = stringEpochToAbsoluteDate(candidateEndDateString, true);
  141.         if (endDate == null || candidateEnd.isAfter(endDate)) {
  142.             // this is either the first setting
  143.             // or we are parsing a new data source referring to a later date than previous ones
  144.             this.endDateString = candidateEndDateString;
  145.             this.endDate = candidateEnd;
  146.         }
  147.     }

  148.     /** Get end date.
  149.      * @return end date
  150.      */
  151.     protected AbsoluteDate getEndDate() {
  152.         return endDate;
  153.     }

  154.     /** Set time scale.
  155.      * @param timeScale time scale
  156.      */
  157.     protected void setTimeScale(final TimeScale timeScale) {

  158.         this.timeScale = timeScale;

  159.         // A time scale has been parsed, update start, end, and creation dates
  160.         // to take into account the time scale
  161.         if (startDateString != null) {
  162.             startDate = stringEpochToAbsoluteDate(startDateString, true);
  163.         }
  164.         if (endDateString != null) {
  165.             endDate = stringEpochToAbsoluteDate(endDateString, false);
  166.         }
  167.         if (creationDateString != null) {
  168.             creationDate = stringEpochToAbsoluteDate(creationDateString, false);
  169.         }

  170.     }

  171.     /** Get time scales.
  172.      * @return time scales
  173.      */
  174.     TimeScales getTimeScales() {
  175.         return timeScales;
  176.     }

  177.     /** Build the parsed file.
  178.      * @return built parsed file
  179.      */
  180.     protected abstract T build();

  181.     /** Extract a string from current line.
  182.      * @param start  start index of the string
  183.      * @param length length of the string
  184.      * @return parsed string
  185.      */
  186.     protected String parseString(final int start, final int length) {
  187.         return line.substring(start, FastMath.min(line.length(), start + length)).trim();
  188.     }

  189.     /** Extract a double from current line.
  190.      * @param start  start index of the real
  191.      * @param length length of the real
  192.      * @return parsed real
  193.      */
  194.     protected double parseDouble(final int start, final int length) {
  195.         return Double.parseDouble(parseString(start, length));
  196.     }

  197.     /** Extract an integer from current line.
  198.      * @param start  start index of the real
  199.      * @param length length of the real
  200.      * @return parsed integer
  201.      */
  202.     protected int parseInt(final int start, final int length) {
  203.         return Integer.parseInt(parseString(start, length));
  204.     }

  205.     /** Extract a double from current line and convert in SI unit.
  206.      * @param startUnit    start index of the unit
  207.      * @param lengthUnit   length of the unit
  208.      * @param startDouble  start index of the real
  209.      * @param lengthDouble length of the real
  210.      * @return parsed double in SI unit
  211.      */
  212.     protected double parseDoubleWithUnit(final int startUnit, final int lengthUnit,
  213.                                          final int startDouble, final int lengthDouble) {
  214.         final Unit unit = Unit.parse(parseString(startUnit, lengthUnit));
  215.         return unit.toSI(parseDouble(startDouble, lengthDouble));
  216.     }

  217.     /** Transform a String epoch to an AbsoluteDate.
  218.      * @param stringDate string epoch
  219.      * @param isStart    true if epoch is a start validity epoch
  220.      * @return the corresponding AbsoluteDate
  221.      */
  222.     protected AbsoluteDate stringEpochToAbsoluteDate(final String stringDate, final boolean isStart) {

  223.         // Deal with 00:000:00000 epochs
  224.         if (DEFAULT_EPOCH_TWO_DIGITS.equals(stringDate) || DEFAULT_EPOCH_FOUR_DIGITS.equals(stringDate)) {
  225.             // If it's a start validity epoch, the file start date shall be used.
  226.             // For end validity epoch, future infinity is acceptable.
  227.             return isStart ? startDate : AbsoluteDate.FUTURE_INFINITY;
  228.         }

  229.         // Date components
  230.         final String[] fields = SEPARATOR.split(stringDate);

  231.         // Read fields
  232.         final int digitsYear = Integer.parseInt(fields[0]);
  233.         final int day = Integer.parseInt(fields[1]);
  234.         final int secInDay = Integer.parseInt(fields[2]);

  235.         // Data year
  236.         final int year;
  237.         if (digitsYear > 50 && digitsYear < 100) {
  238.             year = 1900 + digitsYear;
  239.         } else if (digitsYear < 100) {
  240.             year = 2000 + digitsYear;
  241.         } else {
  242.             year = digitsYear;
  243.         }

  244.         // Return an absolute date.
  245.         // Initialize to 1st January of the given year because
  246.         // sometimes day is equal to 0 in the file.
  247.         return new AbsoluteDate(new DateComponents(year, 1, 1), timeScale).
  248.                shiftedBy(Constants.JULIAN_DAY * (day - 1)).
  249.                shiftedBy(secInDay);

  250.     }

  251. }