AbstractMessageWriter.java

  1. /* Copyright 2002-2022 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.ccsds.utils.generation;

  18. import java.io.IOException;
  19. import java.time.ZoneOffset;
  20. import java.time.ZonedDateTime;

  21. import org.orekit.files.ccsds.definitions.TimeConverter;
  22. import org.orekit.files.ccsds.ndm.NdmConstituent;
  23. import org.orekit.files.ccsds.section.Header;
  24. import org.orekit.files.ccsds.section.HeaderKey;
  25. import org.orekit.files.ccsds.section.Segment;
  26. import org.orekit.files.ccsds.section.XmlStructureKey;
  27. import org.orekit.files.ccsds.utils.ContextBinding;
  28. import org.orekit.files.ccsds.utils.FileFormat;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.DateComponents;
  31. import org.orekit.time.DateTimeComponents;
  32. import org.orekit.time.TimeComponents;
  33. import org.orekit.time.TimeScale;

  34. /**
  35.  * Base class for Navigation Data Message (NDM) files.
  36.  * @param <H> type of the header
  37.  * @param <S> type of the segments
  38.  * @author Luc Maisonobe
  39.  * @since 11.0
  40.  */
  41. public abstract class AbstractMessageWriter<H extends Header, S extends Segment<?, ?>, F extends NdmConstituent<H, S>>
  42.     implements MessageWriter<H, S, F> {

  43.     /** Default value for {@link HeaderKey#ORIGINATOR}. */
  44.     public static final String DEFAULT_ORIGINATOR = "OREKIT";

  45.     /** Root element for XML files. */
  46.     private final String root;

  47.     /** Key for format version. */
  48.     private final String formatVersionKey;

  49.     /** Default format version. */
  50.     private final double defaultVersion;

  51.     /** Current context binding. */
  52.     private ContextBinding context;

  53.     /** Current converter for dates. */
  54.     private TimeConverter timeConverter;

  55.     /** Current format version. */
  56.     private double version;

  57.     /**
  58.      * Constructor used to create a new NDM writer configured with the necessary parameters
  59.      * to successfully fill in all required fields that aren't part of a standard object.
  60.      * <p>
  61.      * If creation date and originator are not present in header, built-in defaults will be used
  62.      * </p>
  63.      * @param root root element for XML files
  64.      * @param formatVersionKey key for format version
  65.      * @param defaultVersion default format version
  66.      * @param context context binding (may be reset for each segment)
  67.      */
  68.     public AbstractMessageWriter(final String root, final String formatVersionKey,
  69.                                  final double defaultVersion, final ContextBinding context) {

  70.         this.root             = root;
  71.         this.defaultVersion   = defaultVersion;
  72.         this.formatVersionKey = formatVersionKey;
  73.         this.version          = defaultVersion;

  74.         setContext(context);

  75.     }

  76.     /** Reset context binding.
  77.      * @param context context binding to use
  78.      */
  79.     public void setContext(final ContextBinding context) {
  80.         this.context       = context;
  81.         this.timeConverter = context.getTimeSystem().getConverter(context);
  82.     }

  83.     /** Get the current context.
  84.      * @return current context
  85.      */
  86.     public ContextBinding getContext() {
  87.         return context;
  88.     }

  89.     /** Get the current time converter.
  90.      * @return current time converter
  91.      */
  92.     public TimeConverter getTimeConverter() {
  93.         return timeConverter;
  94.     }

  95.     /** Get the default format version.
  96.      * @return default format version
  97.      */
  98.     public double getDefaultVersion() {
  99.         return defaultVersion;
  100.     }

  101.     /** {@inheritDoc} */
  102.     @Override
  103.     public void writeHeader(final Generator generator, final H header) throws IOException {

  104.         final ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
  105.         final TimeScale     utc = context.getDataContext().getTimeScales().getUTC();
  106.         final AbsoluteDate date = new AbsoluteDate(zdt.getYear(), zdt.getMonthValue(), zdt.getDayOfMonth(),
  107.                                                    zdt.getHour(), zdt.getMinute(), zdt.getSecond(),
  108.                                                    utc);

  109.         // validate before writing
  110.         if (header != null) {

  111.             if (!Double.isNaN(header.getFormatVersion())) {
  112.                 // save format version for validating segments
  113.                 version = header.getFormatVersion();
  114.             }

  115.             if (header.getCreationDate() == null) {
  116.                 header.setCreationDate(date);
  117.             }

  118.             if (header.getOriginator() == null) {
  119.                 header.setOriginator(DEFAULT_ORIGINATOR);
  120.             }

  121.             header.validate(version);

  122.         }

  123.         generator.startMessage(root, formatVersionKey, version);

  124.         if (generator.getFormat() == FileFormat.XML) {
  125.             generator.enterSection(XmlStructureKey.header.name());
  126.         }

  127.         // comments are optional
  128.         if (header != null) {
  129.             generator.writeComments(header.getComments());
  130.         }

  131.         // creation date is informational only, but mandatory and always in UTC
  132.         final DateTimeComponents creationDate = ((header == null) ? date : header.getCreationDate()).getComponents(utc);
  133.         final DateComponents     dc           = creationDate.getDate();
  134.         final TimeComponents     tc           = creationDate.getTime();
  135.         generator.writeEntry(HeaderKey.CREATION_DATE.name(),
  136.                              generator.dateToString(dc.getYear(), dc.getMonth(), dc.getDay(),
  137.                                                     tc.getHour(), tc.getMinute(), tc.getSecond()),
  138.                              null, true);

  139.         // Use built-in default if mandatory originator not present
  140.         generator.writeEntry(HeaderKey.ORIGINATOR.name(),
  141.                              header == null ? DEFAULT_ORIGINATOR : header.getOriginator(),
  142.                              null, true);

  143.         if (header != null) {
  144.             generator.writeEntry(HeaderKey.MESSAGE_ID.name(), header.getMessageId(), null, false);
  145.         }

  146.         if (generator.getFormat() == FileFormat.XML) {
  147.             generator.exitSection();
  148.         }

  149.         // add an empty line for presentation
  150.         generator.newLine();

  151.         if (generator.getFormat() == FileFormat.XML) {
  152.             generator.enterSection(XmlStructureKey.body.name());
  153.         }

  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public void writeSegment(final Generator generator, final S segment) throws IOException {

  158.         // validate before writing
  159.         segment.getMetadata().validate(version);
  160.         segment.getData().validate(version);

  161.         if (generator.getFormat() == FileFormat.XML) {
  162.             generator.enterSection(XmlStructureKey.segment.name());
  163.         }
  164.         writeSegmentContent(generator, version, segment);
  165.         if (generator.getFormat() == FileFormat.XML) {
  166.             generator.exitSection();
  167.         }

  168.     }

  169.     /** Write one segment content (without XML wrapping).
  170.      * @param generator generator to use for producing output
  171.      * @param formatVersion format version to use
  172.      * @param segment segment to write
  173.      * @throws IOException if any buffer writing operations fails
  174.      */
  175.     public abstract void writeSegmentContent(Generator generator, double formatVersion, S segment) throws IOException;

  176.     /** {@inheritDoc} */
  177.     @Override
  178.     public void writeFooter(final Generator generator) throws IOException {
  179.         if (generator.getFormat() == FileFormat.XML) {
  180.             generator.exitSection();
  181.         }
  182.         generator.endMessage(root);
  183.     }

  184. }