UTCTAIHistoryFilesLoader.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.time;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.text.ParseException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;

  27. import org.orekit.data.DataLoader;
  28. import org.orekit.data.DataProvidersManager;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;


  31. /** Loader for UTC versus TAI history files.
  32.  * <p>UTC versus TAI history files contain {@link UTCTAIOffset
  33.  * leap seconds} data since.</p>
  34.  * <p>The UTC versus TAI history files are recognized thanks to their
  35.  * base names, which must match the pattern <code>UTC-TAI.history</code>
  36.  * (or <code>UTC-TAI.history.gz</code> for gzip-compressed files)</p>
  37.  * <p>Only one history file must be present in the IERS directories
  38.  * hierarchy.</p>
  39.  * @author Luc Maisonobe
  40.  */
  41. public class UTCTAIHistoryFilesLoader implements UTCTAIOffsetsLoader {

  42.     /** Supported files name pattern. */
  43.     private static final String SUPPORTED_NAMES = "^UTC-TAI\\.history$";

  44.     /** Build a loader for UTC-TAI history file. */
  45.     public UTCTAIHistoryFilesLoader() {
  46.     }

  47.     /** {@inheritDoc} */
  48.     @Override
  49.     public List<OffsetModel> loadOffsets() throws OrekitException {
  50.         final Parser parser = new Parser();
  51.         DataProvidersManager.getInstance().feed(SUPPORTED_NAMES, parser);
  52.         return parser.getOffsets();
  53.     }

  54.     /** Internal class performing the parsing. */
  55.     private static class Parser implements DataLoader {

  56.         /** Regular data lines pattern. */
  57.         private Pattern regularPattern;

  58.         /** Last line pattern pattern. */
  59.         private Pattern lastPattern;

  60.         /** Parsed offsets. */
  61.         private final List<OffsetModel> offsets;

  62.         /** Simple constructor.
  63.          */
  64.         Parser() {

  65.             // the data lines in the UTC time steps data files have the following form:
  66.             // 1966  Jan.  1 - 1968  Feb.  1     4.313 170 0s + (MJD - 39 126) x 0.002 592s
  67.             // 1968  Feb.  1 - 1972  Jan.  1     4.213 170 0s +        ""
  68.             // 1972  Jan.  1 -       Jul.  1    10s
  69.             //       Jul.  1 - 1973  Jan.  1    11s
  70.             // 1973  Jan.  1 - 1974  Jan.  1    12s
  71.             //  ...
  72.             // 2006  Jan.  1.- 2009  Jan.  1    33s
  73.             // 2009  Jan.  1.- 2012  Jul   1    34s
  74.             // 2012  Jul   1 -                  35s

  75.             // we ignore the non-constant and non integer offsets before 1972-01-01
  76.             final String start = "^";

  77.             // year group
  78.             final String yearField = "\\p{Blank}*((?:\\p{Digit}\\p{Digit}\\p{Digit}\\p{Digit})|(?:    ))";

  79.             // second group: month as a three letters capitalized abbreviation
  80.             final StringBuilder builder = new StringBuilder("\\p{Blank}+(");
  81.             for (final Month month : Month.values()) {
  82.                 builder.append(month.getCapitalizedAbbreviation());
  83.                 builder.append('|');
  84.             }
  85.             builder.delete(builder.length() - 1, builder.length());
  86.             builder.append(")\\.?");
  87.             final String monthField = builder.toString();

  88.             // day group
  89.             final String dayField = "\\p{Blank}+([ 0-9]+)\\.?";

  90.             // offset group
  91.             final String offsetField = "\\p{Blank}+(\\p{Digit}+)s";

  92.             final String separator   = "\\p{Blank}*-\\p{Blank}+";
  93.             final String finalBlanks = "\\p{Blank}*$";
  94.             regularPattern = Pattern.compile(start + yearField + monthField + dayField +
  95.                                              separator + yearField + monthField + dayField +
  96.                                              offsetField + finalBlanks);
  97.             lastPattern    = Pattern.compile(start + yearField + monthField + dayField +
  98.                                              separator + offsetField + finalBlanks);

  99.             offsets = new ArrayList<OffsetModel>();

  100.         }

  101.         /** Get the parsed offsets.
  102.          * @return parsed offsets
  103.          */
  104.         public List<OffsetModel> getOffsets() {
  105.             return offsets;
  106.         }

  107.         /** {@inheritDoc} */
  108.         public boolean stillAcceptsData() {
  109.             return offsets.isEmpty();
  110.         }

  111.         /** Load UTC-TAI offsets entries read from some file.
  112.          * @param input data input stream
  113.          * @param name name of the file (or zip entry)
  114.          * @exception IOException if data can't be read
  115.          * @exception ParseException if data can't be parsed
  116.          * @exception OrekitException if some data is missing
  117.          * or if some loader specific error occurs
  118.          */
  119.         public void loadData(final InputStream input, final String name)
  120.             throws OrekitException, IOException, ParseException {

  121.             offsets.clear();

  122.             // set up a reader for line-oriented file
  123.             final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));

  124.             // read all file, ignoring not recognized lines
  125.             final String emptyYear = "    ";
  126.             int lineNumber = 0;
  127.             DateComponents lastDate = null;
  128.             int lastLine = 0;
  129.             String previousYear = emptyYear;
  130.             for (String line = reader.readLine(); line != null; line = reader.readLine()) {
  131.                 ++lineNumber;

  132.                 // check matching for regular lines and last line
  133.                 Matcher matcher = regularPattern.matcher(line);
  134.                 if (matcher.matches()) {
  135.                     if (lastLine > 0) {
  136.                         throw new OrekitException(OrekitMessages.UNEXPECTED_DATA_AFTER_LINE_IN_FILE,
  137.                                                   lastLine, name, line);
  138.                     }
  139.                 } else {
  140.                     matcher = lastPattern.matcher(line);
  141.                     if (matcher.matches()) {
  142.                         // this is the last line (there is a start date but no end date)
  143.                         lastLine = lineNumber;
  144.                     }
  145.                 }

  146.                 if (matcher.matches()) {
  147.                     try {
  148.                         // build an entry from the extracted fields

  149.                         String year = matcher.group(1);
  150.                         if (emptyYear.equals(year)) {
  151.                             year = previousYear;
  152.                         }
  153.                         if (lineNumber != lastLine) {
  154.                             if (emptyYear.equals(matcher.group(4))) {
  155.                                 previousYear = year;
  156.                             } else {
  157.                                 previousYear = matcher.group(4);
  158.                             }
  159.                         }
  160.                         final DateComponents leapDay = new DateComponents(Integer.parseInt(year.trim()),
  161.                                                                           Month.parseMonth(matcher.group(2)),
  162.                                                                           Integer.parseInt(matcher.group(3).trim()));

  163.                         final Integer offset = Integer.valueOf(matcher.group(matcher.groupCount()));
  164.                         if ((lastDate != null) && leapDay.compareTo(lastDate) <= 0) {
  165.                             throw new OrekitException(OrekitMessages.NON_CHRONOLOGICAL_DATES_IN_FILE,
  166.                                                       name, lineNumber);
  167.                         }
  168.                         lastDate = leapDay;
  169.                         offsets.add(new OffsetModel(leapDay, offset));

  170.                     } catch (NumberFormatException nfe) {
  171.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  172.                                                   lineNumber, name, line);
  173.                     }
  174.                 }
  175.             }

  176.             if (offsets.isEmpty()) {
  177.                 throw new OrekitException(OrekitMessages.NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE, name);
  178.             }

  179.         }

  180.     }

  181. }