XmlGenerator.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.util.List;

  20. import org.orekit.files.ccsds.utils.FileFormat;
  21. import org.orekit.utils.AccurateFormatter;
  22. import org.orekit.utils.units.Unit;

  23. /** Generator for eXtended Markup Language CCSDS messages.
  24.  * @author Luc Maisonobe
  25.  * @since 11.0
  26.  */
  27. public class XmlGenerator extends AbstractGenerator {

  28.     /** Default number of space for each indentation level. */
  29.     public static final int DEFAULT_INDENT = 2;

  30.     /** Name of the units attribute. */
  31.     public static final String UNITS = "units";

  32.     /** NDM/XML version 3 location.
  33.      * @since 12.0
  34.      */
  35.     public static final String NDM_XML_V3_SCHEMA_LOCATION = "https://sanaregistry.org/r/ndmxml_unqualified/ndmxml-3.0.0-master-3.0.xsd";

  36.     /** XML prolog. */
  37.     private static final String PROLOG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n";

  38.     /** Root element start tag, with schema.
  39.      * @since 12.0
  40.      */
  41.     private static final String ROOT_START_WITH_SCHEMA = "<%s xmlns:xsi=\"%s\" xsi:noNamespaceSchemaLocation=\"%s\" id=\"%s\" version=\"%.1f\">%n";

  42.     /** Root element start tag, without schema.
  43.      * @since 12.0
  44.      */
  45.     private static final String ROOT_START_WITHOUT_SCHEMA = "<%s id=\"%s\" version=\"%.1f\">%n";

  46.     /** Constant for XML schema instance.
  47.      * @since 12.0
  48.      */
  49.     private static final String XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance";

  50.     /** Element end tag.
  51.      * @since 12.0
  52.      */
  53.     private static final String START_TAG_WITH_SCHEMA = "<%s xmlns:xsi=\"%s\" xsi:noNamespaceSchemaLocation=\"%s\">%n";

  54.     /** Element end tag.
  55.      * @since 12.0
  56.      */
  57.     private static final String START_TAG_WITHOUT_SCHEMA = "<%s>%n";

  58.     /** Element end tag. */
  59.     private static final String END_TAG = "</%s>%n";

  60.     /** Leaf element format without attributes. */
  61.     private static final String LEAF_0_ATTRIBUTES = "<%s>%s</%s>%n";

  62.     /** Leaf element format with one attribute. */
  63.     private static final String LEAF_1_ATTRIBUTE = "<%s %s=\"%s\">%s</%s>%n";

  64.     /** Leaf element format with two attributes. */
  65.     private static final String LEAF_2_ATTRIBUTES = "<%s %s=\"%s\" %s=\"%s\">%s</%s>%n";

  66.     /** Comment key. */
  67.     private static final String COMMENT = "COMMENT";

  68.     /** Schema location. */
  69.     private final String schemaLocation;

  70.     /** Indentation size. */
  71.     private final int indentation;

  72.     /** Nesting level. */
  73.     private int level;

  74.     /** Simple constructor.
  75.      * @param output destination of generated output
  76.      * @param indentation number of space for each indentation level
  77.      * @param outputName output name for error messages
  78.      * @param maxRelativeOffset maximum offset in seconds to use relative dates
  79.      * (if a date is too far from reference, it will be displayed as calendar elements)
  80.      * @param writeUnits if true, units must be written
  81.      * @param schemaLocation schema location to use, may be null
  82.      * @see #DEFAULT_INDENT
  83.      * @see #NDM_XML_V3_SCHEMA_LOCATION
  84.      * @throws IOException if an I/O error occurs.
  85.      */
  86.     public XmlGenerator(final Appendable output, final int indentation,
  87.                         final String outputName, final double maxRelativeOffset,
  88.                         final boolean writeUnits, final String schemaLocation) throws IOException {
  89.         super(output, outputName, maxRelativeOffset, writeUnits);
  90.         this.schemaLocation = schemaLocation;
  91.         this.indentation    = indentation;
  92.         this.level          = 0;
  93.         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, PROLOG));
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public FileFormat getFormat() {
  98.         return FileFormat.XML;
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public void startMessage(final String root, final String messageTypeKey, final double version) throws IOException {
  103.         indent();
  104.         if (schemaLocation == null || level > 0) {
  105.             writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START_WITHOUT_SCHEMA,
  106.                                        root, messageTypeKey, version));
  107.         } else {
  108.             writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START_WITH_SCHEMA,
  109.                                        root, XMLNS_XSI, schemaLocation, messageTypeKey, version));
  110.         }
  111.         ++level;
  112.     }

  113.     /** {@inheritDoc} */
  114.     @Override
  115.     public void endMessage(final String root) throws IOException {
  116.         --level;
  117.         indent();
  118.         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, END_TAG,
  119.                                    root));
  120.     }

  121.     /** {@inheritDoc} */
  122.     @Override
  123.     public void writeComments(final List<String> comments) throws IOException {
  124.         for (final String comment : comments) {
  125.             writeEntry(COMMENT, comment, null, false);
  126.         }
  127.     }

  128.     /** Write an element with one attribute.
  129.      * @param name tag name
  130.      * @param value element value
  131.      * @param attributeName attribute name
  132.      * @param attributeValue attribute value
  133.      * @throws IOException if an I/O error occurs.
  134.      */
  135.     public void writeOneAttributeElement(final String name, final String value,
  136.                                          final String attributeName, final String attributeValue)
  137.         throws IOException {
  138.         indent();
  139.         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_1_ATTRIBUTE,
  140.                                    name, attributeName, attributeValue, value, name));
  141.     }

  142.     /** Write an element with two attributes.
  143.      * @param name tag name
  144.      * @param value element value
  145.      * @param attribute1Name attribute 1 name
  146.      * @param attribute1Value attribute 1 value
  147.      * @param attribute2Name attribute 2 name
  148.      * @param attribute2Value attribute 2 value
  149.      * @throws IOException if an I/O error occurs.
  150.      */
  151.     public void writeTwoAttributesElement(final String name, final String value,
  152.                                           final String attribute1Name, final String attribute1Value,
  153.                                           final String attribute2Name, final String attribute2Value)
  154.         throws IOException {
  155.         indent();
  156.         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_2_ATTRIBUTES,
  157.                                    name,
  158.                                    attribute1Name, attribute1Value, attribute2Name, attribute2Value,
  159.                                    value, name));
  160.     }

  161.     /** {@inheritDoc} */
  162.     @Override
  163.     public void writeEntry(final String key, final String value, final Unit unit, final boolean mandatory) throws IOException {
  164.         if (value == null) {
  165.             complain(key, mandatory);
  166.         } else {
  167.             indent();
  168.             if (writeUnits(unit)) {
  169.                 writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_1_ATTRIBUTE,
  170.                                            key, UNITS, siToCcsdsName(unit.getName()), value, key));
  171.             } else {
  172.                 writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_0_ATTRIBUTES,
  173.                                            key, value, key));
  174.             }
  175.         }
  176.     }

  177.     /** {@inheritDoc} */
  178.     @Override
  179.     public void enterSection(final String name) throws IOException {
  180.         indent();
  181.         if (schemaLocation != null && level == 0) {
  182.             // top level tag for ndm messages (it is called before enterMessage)
  183.             writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG_WITH_SCHEMA,
  184.                                        name, XMLNS_XSI, schemaLocation));
  185.         } else {
  186.             writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG_WITHOUT_SCHEMA, name));
  187.         }
  188.         ++level;
  189.         super.enterSection(name);
  190.     }

  191.     /** {@inheritDoc} */
  192.     @Override
  193.     public String exitSection() throws IOException {
  194.         final String name = super.exitSection();
  195.         --level;
  196.         indent();
  197.         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, END_TAG, name));
  198.         return name;
  199.     }

  200.     /** Indent line.
  201.      * @throws IOException if an I/O error occurs.
  202.      */
  203.     private void indent() throws IOException {
  204.         for (int i = 0; i < level * indentation; ++i) {
  205.             writeRawData(' ');
  206.         }
  207.     }

  208. }