CdmMessageWriter.java

  1. /* Copyright 2002-2023 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.ndm.cdm;

  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.section.HeaderKey;
  23. import org.orekit.files.ccsds.section.Segment;
  24. import org.orekit.files.ccsds.section.XmlStructureKey;
  25. import org.orekit.files.ccsds.utils.ContextBinding;
  26. import org.orekit.files.ccsds.utils.FileFormat;
  27. import org.orekit.files.ccsds.utils.generation.Generator;
  28. import org.orekit.files.ccsds.utils.generation.MessageWriter;
  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. /** Cdm message writer.
  35.  * @author Melina Vanel
  36.  * @since 11.2
  37.  */
  38. public abstract class CdmMessageWriter implements MessageWriter<CdmHeader, CdmSegment, Cdm> {

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

  41.     /** Root element for XML files. */
  42.     private final String root;

  43.     /** Key for format version. */
  44.     private final String formatVersionKey;

  45.     /** Default format version. */
  46.     private final double defaultVersion;

  47.     /** Current context binding. */
  48.     private ContextBinding context;

  49.     /** Current converter for dates. */
  50.     private TimeConverter timeConverter;

  51.     /** Current format version. */
  52.     private double version;

  53.     /** Boolean to ensure relative metadata will be written only once. */
  54.     private boolean isrelativemetadataWritten;

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

  68.         this.root                      = root;
  69.         this.defaultVersion            = defaultVersion;
  70.         this.formatVersionKey          = formatVersionKey;
  71.         this.version                   = defaultVersion;
  72.         this.isrelativemetadataWritten = false;

  73.         setContext(context);

  74.     }

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

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

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

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

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

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

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

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

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

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

  120.             header.validate(version);

  121.         }

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

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

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

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

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

  142.         if (header != null) {
  143.             generator.writeEntry(CdmHeaderKey.MESSAGE_FOR.name(), header.getMessageFor(), null, false);
  144.         }

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

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

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

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

  156.     }

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

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

  163.         // relative metadata should only be written once after the header at the beginning of the body
  164.         if (!isrelativemetadataWritten) {
  165.             writeRelativeMetadataContent(generator, version, segment.getMetadata().getRelativeMetadata());
  166.             isrelativemetadataWritten = true;
  167.         }

  168.         if (generator.getFormat() == FileFormat.XML) {
  169.             generator.enterSection(XmlStructureKey.segment.name());
  170.         }
  171.         writeSegmentContent(generator, version, segment);
  172.         if (generator.getFormat() == FileFormat.XML) {
  173.             generator.exitSection();
  174.         }

  175.     }

  176.     /** Write RelativeMetadata part only once after header.
  177.      * @param generator generator to use for producing output
  178.      * @param formatVersion format version to use
  179.      * @param relativeMetadata relative metadata to write
  180.      * @throws IOException if any buffer writing operations fails
  181.      */
  182.     public abstract void writeRelativeMetadataContent(Generator generator, double formatVersion,
  183.                                                       CdmRelativeMetadata relativeMetadata) throws IOException;

  184.     /** Write one segment content (without XML wrapping).
  185.      * @param generator generator to use for producing output
  186.      * @param formatVersion format version to use
  187.      * @param segment segment to write
  188.      * @throws IOException if any buffer writing operations fails
  189.      */
  190.     public abstract void writeSegmentContent(Generator generator, double formatVersion,
  191.                                              Segment<CdmMetadata, CdmData> segment) throws IOException;

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     public void writeFooter(final Generator generator) throws IOException {
  195.         if (generator.getFormat() == FileFormat.XML) {
  196.             generator.exitSection();
  197.         }
  198.         generator.endMessage(root);
  199.     }

  200.     /** {@inheritDoc} */
  201.     @Override
  202.     public String getRoot() {
  203.         return root;
  204.     }

  205.     /** {@inheritDoc} */
  206.     @Override
  207.     public String getFormatVersionKey() {
  208.         return formatVersionKey;
  209.     }

  210.     /** {@inheritDoc} */
  211.     @Override
  212.     public double getVersion() {
  213.         return version;
  214.     }

  215. }