UTCTAIHistoryFilesLoader.java

  1. /* Copyright 2002-2013 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.SortedMap;
  24. import java.util.TreeMap;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;

  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitMessages;


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

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

  42.     /** Regular data lines pattern. */
  43.     private Pattern regularPattern;

  44.     /** Last line pattern pattern. */
  45.     private Pattern lastPattern;

  46.     /** Time scales offsets. */
  47.     private SortedMap<DateComponents, Integer> entries;

  48.     /** Build a loader for UTC-TAI history file. */
  49.     public UTCTAIHistoryFilesLoader() {

  50.         // the data lines in the UTC time steps data files have the following form:
  51.         // 1966  Jan.  1 - 1968  Feb.  1     4.313 170 0s + (MJD - 39 126) x 0.002 592s
  52.         // 1968  Feb.  1 - 1972  Jan.  1     4.213 170 0s +        ""
  53.         // 1972  Jan.  1 -       Jul.  1    10s
  54.         //       Jul.  1 - 1973  Jan.  1    11s
  55.         // 1973  Jan.  1 - 1974  Jan.  1    12s
  56.         //  ...
  57.         // 2006  Jan.  1.- 2009  Jan.  1    33s
  58.         // 2009  Jan.  1.- 2012  Jul   1    34s
  59.         // 2012  Jul   1 -                  35s

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

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

  64.         // second group: month as a three letters capitalized abbreviation
  65.         final StringBuilder builder = new StringBuilder("\\p{Blank}+(");
  66.         for (final Month month : Month.values()) {
  67.             builder.append(month.getCapitalizedAbbreviation());
  68.             builder.append('|');
  69.         }
  70.         builder.delete(builder.length() - 1, builder.length());
  71.         builder.append(")\\.?");
  72.         final String monthField = builder.toString();

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

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

  77.         final String separator   = "\\p{Blank}*-\\p{Blank}+";
  78.         final String finalBlanks = "\\p{Blank}*$";
  79.         regularPattern = Pattern.compile(start + yearField + monthField + dayField +
  80.                                          separator + yearField + monthField + dayField +
  81.                                          offsetField + finalBlanks);
  82.         lastPattern    = Pattern.compile(start + yearField + monthField + dayField +
  83.                                          separator + offsetField + finalBlanks);

  84.         entries = new TreeMap<DateComponents, Integer>();

  85.     }

  86.     /** Get the regular expression for supported files names.
  87.      * @return regular expression for supported files names
  88.      */
  89.     public String getSupportedNames() {
  90.         return SUPPORTED_NAMES;
  91.     }

  92.     /** Load stored UTC-TAI offsets entries.
  93.      * @return sorted UTC-TAI offsets entries (may be empty if no data file is available)
  94.      */
  95.     public SortedMap<DateComponents, Integer> loadTimeSteps() {
  96.         return entries;
  97.     }

  98.     /** {@inheritDoc} */
  99.     public boolean stillAcceptsData() {
  100.         return (entries == null) || entries.isEmpty();
  101.     }

  102.     /** Load UTC-TAI offsets entries read from some file.
  103.      * <p>The time steps are extracted from some <code>UTC-TAI.history[.gz]</code>
  104.      * file. Since entries are stored in a {@link java.util.SortedMap SortedMap},
  105.      * they are chronologically sorted and only one entry remains for a given date.</p>
  106.      * @param input data input stream
  107.      * @param name name of the file (or zip entry)
  108.      * @exception IOException if data can't be read
  109.      * @exception ParseException if data can't be parsed
  110.      * @exception OrekitException if some data is missing
  111.      * or if some loader specific error occurs
  112.      */
  113.     public void loadData(final InputStream input, final String name)
  114.         throws OrekitException, IOException, ParseException {

  115.         entries.clear();

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

  118.         // read all file, ignoring not recognized lines
  119.         boolean foundEntries = false;
  120.         final String emptyYear = "    ";
  121.         int lineNumber = 0;
  122.         DateComponents lastDate = null;
  123.         int lastLine = 0;
  124.         String previousYear = emptyYear;
  125.         for (String line = reader.readLine(); line != null; line = reader.readLine()) {
  126.             ++lineNumber;

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

  141.             if (matcher.matches()) {
  142.                 try {
  143.                     // build an entry from the extracted fields

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

  158.                     final Integer offset = Integer.valueOf(matcher.group(matcher.groupCount()));
  159.                     if ((lastDate != null) && leapDay.compareTo(lastDate) <= 0) {
  160.                         throw new OrekitException(OrekitMessages.NON_CHRONOLOGICAL_DATES_IN_FILE,
  161.                                                   name, lineNumber);
  162.                     }
  163.                     lastDate = leapDay;
  164.                     foundEntries = true;
  165.                     entries.put(leapDay, offset);

  166.                 } catch (NumberFormatException nfe) {
  167.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  168.                                               lineNumber, name, line);
  169.                 }
  170.             }
  171.         }

  172.         if (!foundEntries) {
  173.             throw new OrekitException(OrekitMessages.NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE, name);
  174.         }

  175.     }

  176. }