1   /* Copyright 2002-2026 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  import java.util.Optional;
22  
23  import org.orekit.files.ccsds.definitions.TimeConverter;
24  import org.orekit.files.ccsds.utils.FileFormat;
25  import org.orekit.time.AbsoluteDate;
26  import org.orekit.time.DateComponents;
27  import org.orekit.time.DateTimeComponents;
28  import org.orekit.time.TimeComponents;
29  import org.orekit.utils.Formatter;
30  import org.orekit.utils.units.Unit;
31  
32  /** Generation interface for CCSDS messages.
33   * @author Luc Maisonobe
34   * @since 11.0
35   */
36  public interface Generator extends AutoCloseable {
37  
38      /** Get the name of the output (for error messages).
39       * @return name of the output
40       */
41      String getOutputName();
42  
43      /** Get the generated file format.
44       * @return generated file format
45       */
46      FileFormat getFormat();
47  
48      /**
49       *  Used to format dates and doubles to string.
50       * @return formatter
51       */
52      Formatter getFormatter();
53  
54      /** Start CCSDS message.
55       * @param messageTypeKey key for message type
56       * @param root root element for XML files
57       * @param version format version
58       * @throws IOException if an I/O error occurs.
59       */
60      void startMessage(String root, String messageTypeKey, double version) throws IOException;
61  
62      /** End CCSDS message.
63       * @param root root element for XML files
64       * @throws IOException if an I/O error occurs.
65       */
66      void endMessage(String root) throws IOException;
67  
68      /** Write comment lines.
69       * @param comments comments to write
70       * @throws IOException if an I/O error occurs.
71       */
72      void writeComments(List<String> comments) throws IOException;
73  
74      /** Write a single key/value entry.
75       * @param key   the keyword to write
76       * @param value the value to write
77       * @param unit output unit (may be null)
78       * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
79       * @throws IOException if an I/O error occurs.
80       */
81      void writeEntry(String key, String value, Unit unit, boolean mandatory) throws IOException;
82  
83      /** Write a single key/value entry.
84       * <p>
85       * This method is a convenience method that allows to write an optional String value
86       * without having to call {@code orElse(null)} on it.
87       * </p>
88       * @param key   the keyword to write
89       * @param value the optional value to write
90       * @param unit output unit (may be null)
91       * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
92       * @throws IOException if an I/O error occurs.
93       */
94      @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // we want to use Optional here to avoid having to call orElse(null) everywhere
95      default void writeOptionalStringEntry(final String key, final Optional<String> value,
96                                            final Unit unit, final boolean mandatory) throws IOException {
97          writeEntry(key, value.orElse(null), unit, mandatory);
98      }
99  
100     /** Write a single key/value entry.
101      * @param key   the keyword to write
102      * @param value the value to write
103      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
104      * @throws IOException if an I/O error occurs.
105      */
106     void writeEntry(String key, List<String> value, boolean mandatory) throws IOException;
107 
108     /** Write a single key/value entry.
109      * @param key   the keyword to write
110      * @param value the value to write
111      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
112      * @throws IOException if an I/O error occurs.
113      */
114     void writeEntry(String key, Enum<?> value, boolean mandatory) throws IOException;
115 
116     /** Write a single key/value entry.
117      * <p>
118      * This method is a convenience method that allows to write an optional enum value
119      * without having to map on it.
120      * </p>
121      * @param key   the keyword to write
122      * @param value the optional value to write
123      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
124      * @throws IOException if an I/O error occurs.
125      */
126     @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // we want to use Optional here to avoid having to call value.map(Enum::name) everywhere
127     default void writeOptionalEnumEntry(final String key, final Optional<? extends Enum<?>> value,
128                                         final boolean mandatory) throws IOException {
129         writeEntry(key, value.map(Enum::name).orElse(null), null, mandatory);
130     }
131 
132     /** Write a single key/value entry.
133      * @param key   the keyword to write
134      * @param converter converter to use for dates
135      * @param date the date to write
136      * @param forceCalendar if true, the date is forced to calendar format
137      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
138      * @throws IOException if an I/O error occurs.
139      */
140     void writeEntry(String key, TimeConverter converter, AbsoluteDate date, boolean forceCalendar, boolean mandatory) throws IOException;
141 
142     /** Write a single key/value entry.
143      * <p>
144      * This method is a convenience method that allows to write an optional String value
145      * without having to call {@code orElse(null)} on it.
146      * </p>
147      * @param key   the keyword to write
148      * @param converter converter to use for dates
149      * @param date the date to write
150      * @param forceCalendar if true, the date is forced to calendar format
151      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
152      * @throws IOException if an I/O error occurs.
153      */
154     @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // we want to use Optional here to avoid having to call orElse(null) everywhere
155     default void writeOptionalDateEntry(final String key, final TimeConverter converter,
156                                         final Optional<AbsoluteDate> date, final boolean forceCalendar,
157                                         final boolean mandatory) throws IOException {
158         writeEntry(key, converter, date.orElse(null), forceCalendar, mandatory);
159     }
160 
161     /** Write a single key/value entry.
162      * @param key   the keyword to write
163      * @param value the value to write
164      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
165      * @throws IOException if an I/O error occurs.
166      */
167     void writeEntry(String key, char value, boolean mandatory) throws IOException;
168 
169     /**
170      * Write a single key/value entry.
171      *
172      * <p>Note that the {@code mandatory} flag has no effect and a value is always written
173      * because the whole domain of {@code value} is treated as valid. Use {@link
174      * #writeEntry(String, Integer, boolean)} for integer values that may not be present.
175      *
176      * @param key   the keyword to write
177      * @param value the value to write
178      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored.
179      * @throws IOException if an I/O error occurs.
180      * @see #writeEntry(String, Integer, boolean)
181      */
182     void writeEntry(String key, int value, boolean mandatory) throws IOException;
183 
184     /** Write a single key/value entry.
185      * @param key   the keyword to write
186      * @param value the value to write
187      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
188      * @throws IOException if an I/O error occurs.
189      */
190     default void writeEntry(final String key, final Integer value, final boolean mandatory) throws IOException {
191         writeEntry(key, value == null ? null : value.toString(), null, mandatory);
192     }
193 
194     /** Write a single key/value entry.
195      * <p>
196      * This method is a convenience method that allows to write an optional value
197      * without having to check presence on it.
198      * </p>
199      * @param key   the keyword to write
200      * @param value the optional value to write
201      * @param mandatory if true, null values triggers exception; otherwise they are silently ignored
202      * @throws IOException if an I/O error occurs.
203      */
204     @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // we want to use Optional here to avoid having to call value.isPresent() everywhere
205     default void writeOptionalIntEntry(final String key, final Optional<Integer> value,
206                                        final boolean mandatory) throws IOException {
207         writeEntry(key, value.map(integer -> Integer.toString(integer)).orElse(null), null, mandatory);
208     }
209 
210     /** Write a single key/value entry.
211      * @param key   the keyword to write
212      * @param value the value to write (in SI units)
213      * @param unit output unit
214      * @param mandatory if true, null values triggers exception, otherwise they are silently ignored
215      * @throws IOException if an I/O error occurs.
216      */
217     void writeEntry(String key, double value, Unit unit, boolean mandatory) throws IOException;
218 
219     /** Write a single key/value entry.
220      * @param key   the keyword to write
221      * @param value the value to write (in SI units)
222      * @param unit output unit
223      * @param mandatory if true, null values triggers exception, otherwise they are silently ignored
224      * @throws IOException if an I/O error occurs.
225      */
226     void writeEntry(String key, Double value, Unit unit, boolean mandatory) throws IOException;
227 
228     /** Write a single key/value entry.
229      * <p>
230      * This method is a convenience method that allows to write an optional value
231      * without having to call {@code orElse(Double.NaN)} on it.
232      * </p>
233      * @param key   the keyword to write
234      * @param value the optional value to write
235      * @param unit output unit (may be null)
236      * @param mandatory if true, null values triggers exception, otherwise they are silently ignored
237      * @throws IOException if an I/O error occurs.
238      */
239     @SuppressWarnings("OptionalUsedAsFieldOrParameterType") // we want to use Optional here to avoid having to call orElse(Double.NaN) everywhere
240     default void writeOptionalDoubleEntry(final String key, final Optional<Double> value,
241                                           final Unit unit, final boolean mandatory) throws IOException {
242         writeEntry(key, value.orElse(Double.NaN), unit, mandatory);
243     }
244 
245     /** Finish current line.
246      * @throws IOException if an I/O error occurs.
247      */
248     void newLine() throws IOException;
249 
250     /** Write raw data.
251      * @param data raw data to write
252      * @throws IOException if an I/O error occurs.
253      */
254     void writeRawData(char data) throws IOException;
255 
256     /** Write raw data.
257      * @param data raw data to write
258      * @throws IOException if an I/O error occurs.
259      */
260     void writeRawData(CharSequence data) throws IOException;
261 
262     /** Enter into a new section.
263      * @param name section name
264      * @throws IOException if an I/O error occurs.
265      */
266     void enterSection(String name) throws IOException;
267 
268     /** Exit last section.
269      * @return section name
270      * @throws IOException if an I/O error occurs.
271      */
272     String exitSection() throws IOException;
273 
274     /** Close the generator.
275      * @throws IOException if an I/O error occurs.
276      */
277     void close() throws IOException;
278 
279     /** Convert a date to string value with high precision.
280      * @param converter converter for dates
281      * @param date date to write
282      * @return date as a string (may be either a relative date or a calendar date)
283      */
284     String dateToString(TimeConverter converter, AbsoluteDate date);
285 
286     /** Convert a date to calendar string value with high precision.
287      * @param converter converter for dates
288      * @param date date to write
289      * @return date as a calendar string
290      * @since 12.0
291      */
292     String dateToCalendarString(TimeConverter converter, AbsoluteDate date);
293 
294     /**
295      * Convert a date to string value with high precision.
296      * @param dt date and time components to write
297      * @return date as a string
298      * @since 13.1.6
299      */
300     default String dateToString(final DateTimeComponents dt) {
301         final DateComponents date = dt.getDate();
302         final TimeComponents time = dt.getTime();
303         return dateToString(date.getYear(), date.getMonth(), date.getDay(),
304                             time.getHour(), time.getMinute(), time.getSecond());
305     }
306 
307     /** Convert a date to string value with high precision.
308      * @param year year
309      * @param month month
310      * @param day day
311      * @param hour hour
312      * @param minute minute
313      * @param seconds seconds
314      * @return date as a string
315      */
316     String dateToString(int year, int month, int day, int hour, int minute, double seconds);
317 
318     /** Convert a double to string value with high precision.
319      * <p>
320      * We don't want to loose internal accuracy when writing doubles
321      * but we also don't want to have ugly representations like STEP = 1.25000000000000000
322      * so we try a few simple formats first and fall back to scientific notation
323      * if it doesn't work.
324      * </p>
325      * @param value value to format
326      * @return formatted value, with all original value accuracy preserved, or null
327      * if value is null or {@code Double.NaN}
328      */
329     String doubleToString(double value);
330 
331     /** Convert a list of units to a bracketed string.
332      * @param units lists to output (may be null or empty)
333      * @return bracketed string (null if units list is null or empty)
334      */
335     String unitsListToString(List<Unit> units);
336 
337     /** Convert a SI unit name to a CCSDS name.
338      * @param siName si unit name
339      * @return CCSDS name for the unit
340      */
341     String siToCcsdsName(String siName);
342 
343 }