AbstractMessageWriter.java

  1. /* Copyright 2002-2024 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.  * @param <F> type of the NDM constituent
  39.  * @author Luc Maisonobe
  40.  * @since 11.0
  41.  */
  42. public abstract class AbstractMessageWriter<H extends Header, S extends Segment<?, ?>, F extends NdmConstituent<H, S>>
  43.     implements MessageWriter<H, S, F> {

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

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

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

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

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

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

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

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

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

  75.         setContext(context);

  76.     }

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

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

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

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

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

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

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

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

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

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

  122.             header.validate(version);

  123.         }

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

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

  128.         if (header != null) {

  129.             // comments are optional
  130.             generator.writeComments(header.getComments());

  131.             // classification is optional
  132.             generator.writeEntry(HeaderKey.CLASSIFICATION.name(), header.getClassification(), null, false);

  133.         }

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

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

  146.         if (header != null) {
  147.             generator.writeEntry(HeaderKey.MESSAGE_ID.name(), header.getMessageId(), null, false);
  148.         }

  149.         if (generator.getFormat() == FileFormat.XML) {
  150.             generator.exitSection();
  151.         }

  152.         // add an empty line for presentation
  153.         generator.newLine();

  154.         if (generator.getFormat() == FileFormat.XML) {
  155.             generator.enterSection(XmlStructureKey.body.name());
  156.         }

  157.     }

  158.     /** {@inheritDoc} */
  159.     @Override
  160.     public void writeSegment(final Generator generator, final S segment) throws IOException {

  161.         // validate before writing
  162.         segment.getMetadata().validate(version);
  163.         segment.getData().validate(version);

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

  171.     }

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

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

  187.     /** {@inheritDoc} */
  188.     @Override
  189.     public String getRoot() {
  190.         return root;
  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public String getFormatVersionKey() {
  195.         return formatVersionKey;
  196.     }

  197.     /** {@inheritDoc} */
  198.     @Override
  199.     public double getVersion() {
  200.         return version;
  201.     }

  202. }