1   /* Copyright 2002-2021 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  
19  import java.io.IOException;
20  import java.util.List;
21  
22  import org.orekit.files.ccsds.utils.FileFormat;
23  import org.orekit.utils.AccurateFormatter;
24  import org.orekit.utils.units.Unit;
25  
26  /** Generator for eXtended Markup Language CCSDS messages.
27   * @author Luc Maisonobe
28   * @since 11.0
29   */
30  public class XmlGenerator extends AbstractGenerator {
31  
32      /** Default number of space for each indentation level. */
33      public static final int DEFAULT_INDENT = 2;
34  
35      /** Name of the units attribute. */
36      public static final String UNITS = "units";
37  
38      /** XML prolog. */
39      private static final String PROLOG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n";
40  
41      /** Root element start tag. */
42      private static final String ROOT_START = "<%s id=\"%s\" version=\"%.1f\">%n";
43  
44      /** Element end tag. */
45      private static final String START_TAG = "<%s>%n";
46  
47      /** Element end tag. */
48      private static final String END_TAG = "</%s>%n";
49  
50      /** Leaf element format without attributes. */
51      private static final String LEAF_0_ATTRIBUTES = "<%s>%s</%s>%n";
52  
53      /** Leaf element format with one attribute. */
54      private static final String LEAF_1_ATTRIBUTE = "<%s %s=\"%s\">%s</%s>%n";
55  
56      /** Leaf element format with two attributes. */
57      private static final String LEAF_2_ATTRIBUTES = "<%s %s=\"%s\" %s=\"%s\">%s</%s>%n";
58  
59      /** Comment key. */
60      private static final String COMMENT = "COMMENT";
61  
62      /** Indentation size. */
63      private final int indentation;
64  
65      /** Nesting level. */
66      private int level;
67  
68      /** Simple constructor.
69       * @param output destination of generated output
70       * @param indentation number of space for each indentation level
71       * @param outputName output name for error messages
72       * @param writeUnits if true, units must be written
73       * @see #DEFAULT_INDENT
74       * @throws IOException if an I/O error occurs.
75       */
76      public XmlGenerator(final Appendable output, final int indentation,
77                          final String outputName, final boolean writeUnits) throws IOException {
78          super(output, outputName, writeUnits);
79          this.indentation = indentation;
80          this.level       = 0;
81          writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, PROLOG));
82      }
83  
84      /** {@inheritDoc} */
85      @Override
86      public FileFormat getFormat() {
87          return FileFormat.XML;
88      }
89  
90      /** {@inheritDoc} */
91      @Override
92      public void startMessage(final String root, final String messageTypeKey, final double version) throws IOException {
93          indent();
94          writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, ROOT_START,
95                                     root, messageTypeKey, version));
96          ++level;
97      }
98  
99      /** {@inheritDoc} */
100     @Override
101     public void endMessage(final String root) throws IOException {
102         --level;
103         indent();
104         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, END_TAG,
105                                    root));
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public void writeComments(final List<String> comments) throws IOException {
111         for (final String comment : comments) {
112             writeEntry(COMMENT, comment, null, false);
113         }
114     }
115 
116     /** Write an element with one attribute.
117      * @param name tag name
118      * @param value element value
119      * @param attributeName attribute name
120      * @param attributeValue attribute value
121      * @throws IOException if an I/O error occurs.
122      */
123     public void writeOneAttributeElement(final String name, final String value,
124                                          final String attributeName, final String attributeValue)
125         throws IOException {
126         indent();
127         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_1_ATTRIBUTE,
128                                    name, attributeName, attributeValue, value, name));
129     }
130 
131     /** Write an element with two attributes.
132      * @param name tag name
133      * @param value element value
134      * @param attribute1Name attribute 1 name
135      * @param attribute1Value attribute 1 value
136      * @param attribute2Name attribute 2 name
137      * @param attribute2Value attribute 2 value
138      * @throws IOException if an I/O error occurs.
139      */
140     public void writeTwoAttributesElement(final String name, final String value,
141                                           final String attribute1Name, final String attribute1Value,
142                                           final String attribute2Name, final String attribute2Value)
143         throws IOException {
144         indent();
145         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_2_ATTRIBUTES,
146                                    name,
147                                    attribute1Name, attribute1Value, attribute2Name, attribute2Value,
148                                    value, name));
149     }
150 
151     /** {@inheritDoc} */
152     @Override
153     public void writeEntry(final String key, final String value, final Unit unit, final boolean mandatory) throws IOException {
154         if (value == null) {
155             complain(key, mandatory);
156         } else {
157             indent();
158             if (writeUnits(unit)) {
159                 writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_1_ATTRIBUTE,
160                                            key, UNITS, siToCcsdsName(unit.getName()), value, key));
161             } else {
162                 writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, LEAF_0_ATTRIBUTES,
163                                            key, value, key));
164             }
165         }
166     }
167 
168     /** {@inheritDoc} */
169     @Override
170     public void enterSection(final String name) throws IOException {
171         indent();
172         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, START_TAG, name));
173         ++level;
174         super.enterSection(name);
175     }
176 
177     /** {@inheritDoc} */
178     @Override
179     public String exitSection() throws IOException {
180         final String name = super.exitSection();
181         --level;
182         indent();
183         writeRawData(String.format(AccurateFormatter.STANDARDIZED_LOCALE, END_TAG, name));
184         return name;
185     }
186 
187     /** Indent line.
188      * @throws IOException if an I/O error occurs.
189      */
190     private void indent() throws IOException {
191         for (int i = 0; i < level * indentation; ++i) {
192             writeRawData(' ');
193         }
194     }
195 
196 }