1 /* Copyright 2002-2025 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.lexical;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.regex.Pattern;
24 import java.util.stream.Collectors;
25 import java.util.stream.Stream;
26
27 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
28 import org.hipparchus.geometry.euclidean.threed.Vector3D;
29 import org.orekit.bodies.CelestialBodies;
30 import org.orekit.bodies.CelestialBody;
31 import org.orekit.errors.OrekitException;
32 import org.orekit.errors.OrekitMessages;
33 import org.orekit.files.ccsds.definitions.BodyFacade;
34 import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
35 import org.orekit.files.ccsds.definitions.CenterName;
36 import org.orekit.files.ccsds.definitions.FrameFacade;
37 import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
38 import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
39 import org.orekit.files.ccsds.definitions.TimeSystem;
40 import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
41 import org.orekit.files.ccsds.ndm.cdm.Maneuvrable;
42 import org.orekit.files.ccsds.utils.ContextBinding;
43 import org.orekit.time.AbsoluteDate;
44 import org.orekit.utils.units.Unit;
45
46 /** Token occurring during CCSDS file parsing.
47 * <p>
48 * Parse tokens correspond to:
49 * <ul>
50 * <li>bloc or entry start</li>
51 * <li>entry content</li>
52 * <li>bloc or entry end</li>
53 * <li>raw lines</li>
54 * </ul>
55 * @see MessageParser
56 * @author Luc Maisonobe
57 * @since 11.0
58 */
59 public class ParseToken {
60
61 /** Pattern for dash. */
62 private static final Pattern DASH = Pattern.compile("-");
63
64 /** Pattern for spaces. */
65 private static final Pattern SPACE = Pattern.compile("\\p{Space}+");
66
67 /** Pattern for splitting comma-separated lists. */
68 private static final Pattern SPLIT_AT_COMMAS = Pattern.compile("\\p{Space}*,\\p{Space}*");
69
70 /** Pattern for splitting comma-separated lists with no space in between. */
71 private static final Pattern SPLIT_AT_COMMAS_NO_SPACE = Pattern.compile(",");
72
73 /** Pattern for true boolean value. */
74 private static final Pattern BOOLEAN_TRUE = Pattern.compile("(?:yes)|(?:true)",
75 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
76
77 /** Pattern for false boolean value. */
78 private static final Pattern BOOLEAN_FALSE = Pattern.compile("(?:no)|(?:false)",
79 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
80
81 /** Type of the token. */
82 private TokenType type;
83
84 /** Name of the entry. */
85 private final String name;
86
87 /** Entry content. */
88 private final String content;
89
90 /** Units of the entry. */
91 private final Unit units;
92
93 /** Number of the line from which pair is extracted. */
94 private final int lineNumber;
95
96 /** Name of the file. */
97 private final String fileName;
98
99 /** Simple constructor.
100 * @param type type of the token
101 * @param name name of the block or entry
102 * @param content entry content
103 * @param units units of the entry
104 * @param lineNumber number of the line in the CCSDS data message
105 * @param fileName name of the file
106 */
107 public ParseToken(final TokenType type, final String name, final String content, final Unit units,
108 final int lineNumber, final String fileName) {
109 this.type = type;
110 this.name = name;
111 this.content = content;
112 this.units = units;
113 this.lineNumber = lineNumber;
114 this.fileName = fileName;
115 }
116
117 /** Get the type of the token.
118 * @return type of the token
119 */
120 public TokenType getType() {
121 return type;
122 }
123
124 /** Get the name of the block or entry.
125 * @return name of the block or entry
126 */
127 public String getName() {
128 return name;
129 }
130
131 /** Get the raw content of the entry.
132 * @return entry raw content
133 */
134 public String getRawContent() {
135 return content;
136 }
137
138 /** Get the content of the entry.
139 * <p>
140 * Free-text strings are normalized by replacing all occurrences
141 * of '_' with space, and collapsing several spaces as one space only.
142 * </p>
143 * @return entry content
144 */
145 public String getContentAsNormalizedString() {
146 return SPACE.matcher(content.replace('_', ' ')).replaceAll(" ").trim();
147 }
148
149 /** Get the content of the entry as a list of free-text strings.
150 * @return content of the entry as a list of free-test strings
151 * @since 12.0
152 */
153 public List<String> getContentAsFreeTextList() {
154 return Arrays.asList(SPLIT_AT_COMMAS.split(getRawContent()));
155 }
156
157 /** Get the content of the entry as a list of normalized strings.
158 * <p>
159 * Normalization is performed by replacing all occurrences
160 * of '_' with space, and collapsing several spaces as one space only.
161 * </p>
162 * @return content of the entry as a list of free-test strings
163 */
164 public List<String> getContentAsNormalizedList() {
165 return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsNormalizedString()));
166 }
167
168 /** Get the content of the entry as normalized and uppercased.
169 * @return entry normalized and uppercased content
170 */
171 public String getContentAsUppercaseString() {
172 return getContentAsNormalizedString().toUpperCase(Locale.US);
173 }
174
175 /** Get the content of the entry as a list of normalized and uppercased strings.
176 * @return content of the entry as a list of normalized and uppercased strings
177 */
178 public List<String> getContentAsUppercaseList() {
179 return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsUppercaseString()));
180 }
181
182 /** Get the content of the entry as an enum.
183 * @param cls enum class
184 * @param <T> type of the enum
185 * @return entry content
186 */
187 public <T extends Enum<T>> T getContentAsEnum(final Class<T> cls) {
188 return toEnum(cls, getRawContent());
189 }
190
191 /** Get the content of the entry as a list of enum.
192 * @param cls enum class
193 * @param <T> type of the enum
194 * @return entry content
195 */
196 public <T extends Enum<T>> List<T> getContentAsEnumList(final Class<T> cls) {
197 final String[] elements = SPLIT_AT_COMMAS.split(getRawContent());
198 final List<T> list = new ArrayList<>(elements.length);
199 for (String element : elements) {
200 list.add(toEnum(cls, element));
201 }
202 return list;
203 }
204
205 /** Get the content of the entry as a double.
206 * @return content as a double
207 */
208 public double getContentAsDouble() {
209 try {
210 return Double.parseDouble(content);
211 } catch (NumberFormatException nfe) {
212 throw generateException(nfe);
213 }
214 }
215
216 /** Get the content of the entry as a vector.
217 * @return content as a vector
218 */
219 public Vector3D getContentAsVector() {
220 try {
221 final String[] fields = SPACE.split(content);
222 if (fields.length == 3) {
223 return new Vector3D(Double.parseDouble(fields[0]),
224 Double.parseDouble(fields[1]),
225 Double.parseDouble(fields[2]));
226 }
227 } catch (NumberFormatException nfe) {
228 // ignored, error handled below, together with wrong number of fields
229 }
230 throw generateException(null);
231 }
232
233 /** Get the content of the entry as a boolean.
234 * @return content as a boolean
235 */
236 public boolean getContentAsBoolean() {
237 if (BOOLEAN_TRUE.matcher(content).matches()) {
238 return true;
239 } else if (BOOLEAN_FALSE.matcher(content).matches()) {
240 return false;
241 } else {
242 throw generateException(null);
243 }
244 }
245
246 /** Get the content of the entry as an integer.
247 * @return content as an integer
248 */
249 public int getContentAsInt() {
250 try {
251 return Integer.parseInt(content);
252 } catch (NumberFormatException nfe) {
253 throw generateException(nfe);
254 }
255 }
256
257 /** Get the content of the entry as an uppercase character.
258 * @return content as an uppercase character
259 */
260 public char getContentAsUppercaseCharacter() {
261 try {
262 return getContentAsUppercaseString().charAt(0);
263 } catch (NumberFormatException nfe) {
264 throw generateException(nfe);
265 }
266 }
267
268 /** Get the units.
269 * @return units of the entry (may be null)
270 */
271 public Unit getUnits() {
272 return units;
273 }
274
275 /** Get the number of the line in the CCSDS data message.
276 * @return number of the line in the CCSDS data message
277 */
278 public int getLineNumber() {
279 return lineNumber;
280 }
281
282 /** Get the name of the file.
283 * @return name of the file
284 */
285 public String getFileName() {
286 return fileName;
287 }
288
289 /** Process the content as a normalized string.
290 * @param consumer consumer of the normalized string
291 * @return always returns {@code true}
292 * @see #processAsUppercaseString(StringConsumer)
293 */
294 public boolean processAsNormalizedString(final StringConsumer consumer) {
295 if (type == TokenType.ENTRY) {
296 consumer.accept(getContentAsNormalizedString());
297 }
298 return true;
299 }
300
301 /** Process the content as a normalized uppercase string.
302 * @param consumer consumer of the normalized uppercase string
303 * @return always returns {@code true}
304 * @see #processAsNormalizedString(StringConsumer)
305 */
306 public boolean processAsUppercaseString(final StringConsumer consumer) {
307 if (type == TokenType.ENTRY) {
308 consumer.accept(getContentAsUppercaseString());
309 }
310 return true;
311 }
312
313 /** Process the content as an indexed normalized string.
314 * @param index index
315 * @param consumer consumer of the indexed normalized string
316 * @return always returns {@code true}
317 */
318 public boolean processAsIndexedNormalizedString(final int index, final IndexedStringConsumer consumer) {
319 if (type == TokenType.ENTRY) {
320 consumer.accept(index, getContentAsNormalizedString());
321 }
322 return true;
323 }
324
325 /** Process the content as an indexed normalized uppercase string.
326 * @param index index
327 * @param consumer consumer of the indexed normalized uppercase string
328 * @return always returns {@code true}
329 */
330 public boolean processAsIndexedUppercaseString(final int index, final IndexedStringConsumer consumer) {
331 if (type == TokenType.ENTRY) {
332 consumer.accept(index, getContentAsUppercaseString());
333 }
334 return true;
335 }
336
337 /** Process the content as a list of free-text strings.
338 * @param consumer consumer of the free-text strings list
339 * @return always returns {@code true}
340 * @since 12.0
341 */
342 public boolean processAsFreeTextList(final StringListConsumer consumer) {
343 if (type == TokenType.ENTRY) {
344 consumer.accept(getContentAsFreeTextList());
345 }
346 return true;
347 }
348
349 /** Process the content as a list of normalized strings.
350 * @param consumer consumer of the normalized strings list
351 * @return always returns {@code true}
352 */
353 public boolean processAsNormalizedList(final StringListConsumer consumer) {
354 if (type == TokenType.ENTRY) {
355 consumer.accept(getContentAsNormalizedList());
356 }
357 return true;
358 }
359
360 /** Process the content as a list of normalized uppercase strings.
361 * @param consumer consumer of the normalized uppercase strings list
362 * @return always returns {@code true}
363 */
364 public boolean processAsUppercaseList(final StringListConsumer consumer) {
365 if (type == TokenType.ENTRY) {
366 consumer.accept(getContentAsUppercaseList());
367 }
368 return true;
369 }
370
371 /** Process the content as an enum.
372 * @param cls enum class
373 * @param consumer consumer of the enum
374 * @param <T> type of the enum
375 * @return always returns {@code true}
376 */
377 public <T extends Enum<T>> boolean processAsEnum(final Class<T> cls, final EnumConsumer<T> consumer) {
378 if (type == TokenType.ENTRY) {
379 consumer.accept(getContentAsEnum(cls));
380 }
381 return true;
382 }
383
384 /** Process the content as a list of enums.
385 * @param cls enum class
386 * @param consumer consumer of the enums list
387 * @param <T> type of the enum
388 * @return always returns {@code true}
389 */
390 public <T extends Enum<T>> boolean processAsEnumsList(final Class<T> cls, final EnumListConsumer<T> consumer) {
391 if (type == TokenType.ENTRY) {
392 consumer.accept(getContentAsEnumList(cls));
393 }
394 return true;
395 }
396
397 /** Process the content as a boolean.
398 * @param consumer consumer of the boolean
399 * @return always returns {@code true}
400 */
401 public boolean processAsBoolean(final BooleanConsumer consumer) {
402 if (type == TokenType.ENTRY) {
403 consumer.accept(getContentAsBoolean());
404 }
405 return true;
406 }
407
408 /** Process the content as an integer.
409 * @param consumer consumer of the integer
410 * @return always returns {@code true}
411 */
412 public boolean processAsInteger(final IntConsumer consumer) {
413 if (type == TokenType.ENTRY) {
414 consumer.accept(getContentAsInt());
415 }
416 return true;
417 }
418
419 /** Process the content as an indexed integer.
420 * @param index index
421 * @param consumer consumer of the integer
422 * @return always returns {@code true}
423 * @since 12.0
424 */
425 public boolean processAsIndexedInteger(final int index, final IndexedIntConsumer consumer) {
426 if (type == TokenType.ENTRY) {
427 consumer.accept(index, getContentAsInt());
428 }
429 return true;
430 }
431
432 /** Process the content as an array of integers. No spaces between commas are allowed.
433 * @param consumer consumer of the array
434 * @return always returns {@code true}
435 */
436 public boolean processAsIntegerArrayNoSpace(final IntegerArrayConsumer consumer) {
437 try {
438 if (type == TokenType.ENTRY) {
439 // Do not allow spaces
440 final String[] fields = SPLIT_AT_COMMAS_NO_SPACE.split(getRawContent());
441 final int[] integers = new int[fields.length];
442 for (int i = 0; i < fields.length; ++i) {
443 integers[i] = Integer.parseInt(fields[i]);
444 }
445 consumer.accept(integers);
446 }
447 return true;
448 } catch (NumberFormatException nfe) {
449 throw generateException(nfe);
450 }
451 }
452
453 /** Process the content as an array of integers. Spaces are replaced by commas.
454 * @param consumer consumer of the array
455 * @return always returns {@code true}
456 */
457 public boolean processAsIntegerArray(final IntegerArrayConsumer consumer) {
458 try {
459 if (type == TokenType.ENTRY) {
460 final String[] fields = SPACE.split(getRawContent());
461 final int[] integers = new int[fields.length];
462 for (int i = 0; i < fields.length; ++i) {
463 integers[i] = Integer.parseInt(fields[i]);
464 }
465 consumer.accept(integers);
466 }
467 return true;
468 } catch (NumberFormatException nfe) {
469 throw generateException(nfe);
470 }
471 }
472
473 /** Process the content as a normalized character.
474 * @param consumer consumer of the normalized character
475 * @return always returns {@code true}
476 */
477 public boolean processAsNormalizedCharacter(final CharConsumer consumer) {
478 if (type == TokenType.ENTRY) {
479 consumer.accept(getContentAsUppercaseCharacter());
480 }
481 return true;
482 }
483
484 /** Process the content as a double.
485 * @param standard units of parsed content as specified by CCSDS standard
486 * @param behavior behavior to adopt for parsed unit
487 * @param consumer consumer of the double
488 * @return always returns {@code true}
489 */
490 public boolean processAsDouble(final Unit standard, final ParsedUnitsBehavior behavior,
491 final DoubleConsumer consumer) {
492 if (type == TokenType.ENTRY) {
493 consumer.accept(behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
494 }
495 return true;
496 }
497
498 /** Process the content as a labeled double.
499 * @param label label
500 * @param standard units of parsed content as specified by CCSDS standard
501 * @param behavior behavior to adopt for parsed unit
502 * @param consumer consumer of the indexed double
503 * @return always returns {@code true}
504 */
505 public boolean processAsLabeledDouble(final char label,
506 final Unit standard, final ParsedUnitsBehavior behavior,
507 final LabeledDoubleConsumer consumer) {
508 if (type == TokenType.ENTRY) {
509 consumer.accept(label, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
510 }
511 return true;
512 }
513
514 /** Process the content as an indexed double.
515 * @param i index
516 * @param standard units of parsed content as specified by CCSDS standard
517 * @param behavior behavior to adopt for parsed unit
518 * @param consumer consumer of the indexed double
519 * @return always returns {@code true}
520 */
521 public boolean processAsIndexedDouble(final int i,
522 final Unit standard, final ParsedUnitsBehavior behavior,
523 final IndexedDoubleConsumer consumer) {
524 if (type == TokenType.ENTRY) {
525 consumer.accept(i, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
526 }
527 return true;
528 }
529
530 /** Process the content as a doubly-indexed double.
531 * @param i first index
532 * @param j second index
533 * @param standard units of parsed content as specified by CCSDS standard
534 * @param behavior behavior to adopt for parsed unit
535 * @param consumer consumer of the doubly-indexed double
536 * @return always returns {@code true}
537 */
538 public boolean processAsDoublyIndexedDouble(final int i, final int j,
539 final Unit standard, final ParsedUnitsBehavior behavior,
540 final DoublyIndexedDoubleConsumer consumer) {
541 if (type == TokenType.ENTRY) {
542 consumer.accept(i, j, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
543 }
544 return true;
545 }
546
547 /** Process the content as an array of doubles.
548 * @param standard units of parsed content as specified by CCSDS standard
549 * @param behavior behavior to adopt for parsed unit
550 * @param consumer consumer of the array
551 * @return always returns {@code true}
552 * @since 12.0
553 */
554 public boolean processAsDoubleArray(final Unit standard, final ParsedUnitsBehavior behavior,
555 final DoubleArrayConsumer consumer) {
556 try {
557 if (type == TokenType.ENTRY) {
558 final String[] fields = SPACE.split(getRawContent());
559 final double[] doubles = new double[fields.length];
560 for (int i = 0; i < fields.length; ++i) {
561 doubles[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i]));
562 }
563 consumer.accept(doubles);
564 }
565 return true;
566 } catch (NumberFormatException nfe) {
567 throw generateException(nfe);
568 }
569 }
570
571 /** Process the content as an indexed double array.
572 * @param index index
573 * @param standard units of parsed content as specified by CCSDS standard
574 * @param behavior behavior to adopt for parsed unit
575 * @param consumer consumer of the indexed double array
576 * @return always returns {@code true}
577 * @since 12.0
578 */
579 public boolean processAsIndexedDoubleArray(final int index,
580 final Unit standard, final ParsedUnitsBehavior behavior,
581 final IndexedDoubleArrayConsumer consumer) {
582 if (type == TokenType.ENTRY) {
583 final String[] fields = SPACE.split(content);
584 final double[] values = new double[fields.length];
585 for (int i = 0; i < fields.length; ++i) {
586 values[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i]));
587 }
588 consumer.accept(index, values);
589 }
590 return true;
591 }
592
593 /** Process the content as a vector.
594 * @param standard units of parsed content as specified by CCSDS standard
595 * @param behavior behavior to adopt for parsed unit
596 * @param consumer consumer of the vector
597 * @return always returns {@code true} (or throws an exception)
598 */
599 public boolean processAsVector(final Unit standard, final ParsedUnitsBehavior behavior,
600 final VectorConsumer consumer) {
601 if (type == TokenType.ENTRY) {
602 final double scale = behavior.select(getUnits(), standard).getScale();
603 consumer.accept(getContentAsVector().scalarMultiply(scale));
604 }
605 return true;
606 }
607
608 /** Process the content as a date.
609 * @param consumer consumer of the date
610 * @param context context binding
611 * @return always returns {@code true} (or throws an exception)
612 */
613 public boolean processAsDate(final DateConsumer consumer, final ContextBinding context) {
614 if (type == TokenType.ENTRY) {
615 if (context.getTimeSystem() == null) {
616 throw new OrekitException(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_READ_YET,
617 getLineNumber(), getFileName());
618 }
619 consumer.accept(context.getTimeSystem().getConverter(context).parse(content));
620 }
621 return true;
622 }
623
624 /** Process the content as a time system.
625 * @param consumer consumer of the time system
626 * @return always returns {@code true} (or throws an exception)
627 */
628 public boolean processAsTimeSystem(final TimeSystemConsumer consumer) {
629 if (type == TokenType.ENTRY) {
630 consumer.accept(TimeSystem.parse(getContentAsUppercaseString()));
631 }
632 return true;
633 }
634
635 /** Process the content as a frame.
636 * @param consumer consumer of the frame
637 * @param context context binding
638 * @param allowCelestial if true, {@link CelestialBodyFrame} are allowed
639 * @param allowOrbit if true, {@link OrbitRelativeFrame} are allowed
640 * @param allowSpacecraft if true, {@link SpacecraftBodyFrame} are allowed
641 * @return always returns {@code true}
642 */
643 public boolean processAsFrame(final FrameConsumer consumer, final ContextBinding context,
644 final boolean allowCelestial, final boolean allowOrbit,
645 final boolean allowSpacecraft) {
646 if (type == TokenType.ENTRY) {
647 try {
648 consumer.accept(FrameFacade.parse(DASH.
649 matcher(getContentAsUppercaseString()).
650 replaceAll("").
651 replace(' ', '_'),
652 context.getConventions(),
653 context.isSimpleEOP(), context.getDataContext(),
654 allowCelestial, allowOrbit, allowSpacecraft));
655 } catch (OrekitException oe) {
656 throw generateException(oe);
657 }
658 }
659 return true;
660 }
661
662 /** Process the content as a body center.
663 * @param consumer consumer of the body center
664 * @param celestialBodies factory for celestial bodies
665 * @return always returns {@code true}
666 */
667 public boolean processAsCenter(final CenterConsumer consumer, final CelestialBodies celestialBodies) {
668 if (type == TokenType.ENTRY) {
669 final String centerName = getContentAsUppercaseString();
670 consumer.accept(new BodyFacade(centerName, body(centerName, celestialBodies)));
671 }
672 return true;
673 }
674
675 /** Process the content as a body center list.
676 * @param consumer consumer of the body center list
677 * @param celestialBodies factory for celestial bodies
678 * @return always returns {@code true}
679 */
680 public boolean processAsCenterList(final CenterListConsumer consumer, final CelestialBodies celestialBodies) {
681 if (type == TokenType.ENTRY) {
682 final List<BodyFacade> facades = new ArrayList<>();
683 for (final String centerName : SPLIT_AT_COMMAS.split(getContentAsUppercaseString())) {
684 facades.add(new BodyFacade(centerName, body(centerName, celestialBodies)));
685 }
686 consumer.accept(facades);
687 }
688 return true;
689 }
690
691 /** Process the content as a rotation sequence.
692 * @param consumer consumer of the rotation sequence
693 * @return always returns {@code true}
694 * @since 12.0
695 */
696 public boolean processAsRotationOrder(final RotationOrderConsumer consumer) {
697 if (type == TokenType.ENTRY) {
698 try {
699 consumer.accept(RotationOrder.valueOf(getContentAsUppercaseString().
700 replace('1', 'X').
701 replace('2', 'Y').
702 replace('3', 'Z')));
703 } catch (IllegalArgumentException iae) {
704 throw new OrekitException(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE,
705 getContentAsUppercaseString(), getLineNumber(), getFileName());
706 }
707 }
708 return true;
709 }
710
711 /** Process the content as a list of units.
712 * @param consumer consumer of the time scale
713 * @return always returns {@code true} (or throws an exception)
714 */
715 public boolean processAsUnitList(final UnitListConsumer consumer) {
716 if (type == TokenType.ENTRY) {
717 final String bracketed = getContentAsNormalizedString();
718 if (bracketed.charAt(0) != '[' || bracketed.charAt(bracketed.length() - 1) != ']') {
719 throw generateException(null);
720 }
721 final String unbracketed = bracketed.substring(1, bracketed.length() - 1).trim();
722 try {
723 consumer.accept(Stream.of(SPLIT_AT_COMMAS.split(unbracketed)).
724 map(Unit::parse).
725 collect(Collectors.toList()));
726 } catch (OrekitException oe) {
727 // one unit is unknown
728 throw generateException(oe);
729 }
730 }
731 return true;
732 }
733
734 /** Process the content as free text string.
735 * @param consumer consumer of the string
736 * @return always returns {@code true}
737 */
738 public boolean processAsFreeTextString(final StringConsumer consumer) {
739 if (type == TokenType.ENTRY) {
740 consumer.accept(getRawContent());
741 }
742 return true;
743 }
744
745 /** Process the content of the Maneuvrable enum.
746 * @param consumer consumer of the enum
747 * @return always returns {@code true}
748 */
749 public boolean processAsManeuvrableEnum(final ManeuvrableConsumer consumer) {
750 if (type == TokenType.ENTRY) {
751 consumer.accept(Maneuvrable.getEnum(getRawContent()));
752 }
753 return true;
754 }
755
756 /** Generate a parse exception for this entry.
757 * @param cause underlying cause exception (may be null)
758 * @return exception for this entry
759 */
760 public OrekitException generateException(final Exception cause) {
761 return new OrekitException(cause, OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE,
762 getName(), getLineNumber(), getFileName());
763 }
764
765 /** Get the body corresponding to a center name.
766 * @param centerName name of the center
767 * @param celestialBodies factory for celestial bodies
768 * @return celestial body corresponding to name, or null
769 */
770 private CelestialBody body(final String centerName, final CelestialBodies celestialBodies) {
771
772 // convert some known names
773 final String canonicalValue;
774 if (centerName.equals("SOLAR SYSTEM BARYCENTER") || centerName.equals("SSB")) {
775 canonicalValue = "SOLAR_SYSTEM_BARYCENTER";
776 } else if (centerName.equals("EARTH MOON BARYCENTER") || centerName.equals("EARTH-MOON BARYCENTER") ||
777 centerName.equals("EARTH BARYCENTER") || centerName.equals("EMB")) {
778 canonicalValue = "EARTH_MOON";
779 } else {
780 canonicalValue = centerName;
781 }
782
783 try {
784 return CenterName.valueOf(canonicalValue).getCelestialBody(celestialBodies);
785 } catch (IllegalArgumentException iae) {
786 // ignored, we just let body set to null
787 return null;
788 }
789
790 }
791
792 /** Convert a value to an enum.
793 * @param cls enum class
794 * @param value value to convert to an enum
795 * @param <T> type of the enum
796 * @return enumerate corresponding to the value
797 */
798 private <T extends Enum<T>> T toEnum(final Class<T> cls, final String value) {
799 // first replace space characters
800 final String noSpace = value.replace(' ', '_');
801 try {
802 // first try without changing case, as some CCSDS enums are mixed case (like RangeUnits for TDM)
803 return Enum.valueOf(cls, noSpace);
804 } catch (IllegalArgumentException iae1) {
805 try {
806 // second try, using more standard uppercase
807 return Enum.valueOf(cls, noSpace.toUpperCase(Locale.US));
808 } catch (IllegalArgumentException iae2) {
809 // use the first exception for the message
810 throw generateException(iae1);
811 }
812 }
813 }
814
815 /** Interface representing instance methods that consume string values. */
816 public interface StringConsumer {
817 /** Consume a string.
818 * @param value value to consume
819 */
820 void accept(String value);
821 }
822
823 /** Interface representing instance methods that consume indexed string values. */
824 public interface IndexedStringConsumer {
825 /** Consume an indexed string.
826 * @param index index
827 * @param value value to consume
828 */
829 void accept(int index, String value);
830 }
831
832 /** Interface representing instance methods that consume lists of strings values. */
833 public interface StringListConsumer {
834 /** Consume a list of strings.
835 * @param value value to consume
836 */
837 void accept(List<String> value);
838 }
839
840 /** Interface representing instance methods that consume enum values.
841 * @param <T> type of the enum
842 */
843 public interface EnumConsumer<T extends Enum<T>> {
844 /** Consume an enum.
845 * @param value value to consume
846 */
847 void accept(T value);
848 }
849
850 /** Interface representing instance methods that consume lists of enum values.
851 * @param <T> type of the enum
852 */
853 public interface EnumListConsumer<T extends Enum<T>> {
854 /** Consume an enum.
855 * @param value value to consume
856 */
857 void accept(List<T> value);
858 }
859
860 /** Interface representing instance methods that consume boolean values. */
861 public interface BooleanConsumer {
862 /** Consume a boolean.
863 * @param value value to consume
864 */
865 void accept(boolean value);
866 }
867
868 /** Interface representing instance methods that consume integer values. */
869 public interface IntConsumer {
870 /** Consume an integer.
871 * @param value value to consume
872 */
873 void accept(int value);
874 }
875
876 /** Interface representing instance methods that consume indexed integer values.
877 * @since 12.0
878 */
879 public interface IndexedIntConsumer {
880 /** Consume an integer.
881 * @param index index
882 * @param value value to consume
883 */
884 void accept(int index, int value);
885 }
886
887 /** Interface representing instance methods that consume integer array. */
888 public interface IntegerArrayConsumer {
889 /** Consume an array of integers.
890 * @param integers array of integers
891 */
892 void accept(int[] integers);
893 }
894
895 /** Interface representing instance methods that consume character values. */
896 public interface CharConsumer {
897 /** Consume a character.
898 * @param value value to consume
899 */
900 void accept(char value);
901 }
902
903 /** Interface representing instance methods that consume double values. */
904 public interface DoubleConsumer {
905 /** Consume a double.
906 * @param value value to consume
907 */
908 void accept(double value);
909 }
910
911 /** Interface representing instance methods that consume labeled double values. */
912 public interface LabeledDoubleConsumer {
913 /** Consume an indexed double.
914 * @param label label
915 * @param value value to consume
916 */
917 void accept(char label, double value);
918 }
919
920 /** Interface representing instance methods that consume indexed double values. */
921 public interface IndexedDoubleConsumer {
922 /** Consume an indexed double.
923 * @param i index
924 * @param value value to consume
925 */
926 void accept(int i, double value);
927 }
928
929 /** Interface representing instance methods that consume doubly-indexed double values. */
930 public interface DoublyIndexedDoubleConsumer {
931 /** Consume a doubly indexed double.
932 * @param i first index
933 * @param j second index
934 * @param value value to consume
935 */
936 void accept(int i, int j, double value);
937 }
938
939 /** Interface representing instance methods that consume double array. */
940 public interface DoubleArrayConsumer {
941 /** Consume an array of doubles.
942 * @param doubles array of doubles
943 */
944 void accept(double[] doubles);
945 }
946
947 /** Interface representing instance methods that consume indexed double array values.
948 * @since 12.0
949 */
950 public interface IndexedDoubleArrayConsumer {
951 /** Consume an indexed double array.
952 * @param index index
953 * @param value array value to consume
954 */
955 void accept(int index, double[] value);
956 }
957
958 /** Interface representing instance methods that consume vector values. */
959 public interface VectorConsumer {
960 /** Consume a vector.
961 * @param value value to consume
962 */
963 void accept(Vector3D value);
964 }
965
966 /** Interface representing instance methods that consume date values. */
967 public interface DateConsumer {
968 /** Consume a date.
969 * @param value value to consume
970 */
971 void accept(AbsoluteDate value);
972 }
973
974 /** Interface representing instance methods that consume time systems values. */
975 public interface TimeSystemConsumer {
976 /** Consume a time system.
977 * @param value value to consume
978 */
979 void accept(TimeSystem value);
980 }
981
982 /** Interface representing instance methods that consume frame values. */
983 public interface FrameConsumer {
984 /** Consume a frame.
985 * @param frameFacade facade in front of several frames types
986 */
987 void accept(FrameFacade frameFacade);
988 }
989
990 /** Interface representing instance methods that consume center values. */
991 public interface CenterConsumer {
992 /** Consume a body center.
993 * @param bodyFacade facade for celestial body name and body
994 */
995 void accept(BodyFacade bodyFacade);
996 }
997
998 /** Interface representing instance methods that consume center lists. */
999 public interface CenterListConsumer {
1000 /** Consume a body center.
1001 * @param bodyFacades facades for celestial bodies name and bodies
1002 */
1003 void accept(List<BodyFacade> bodyFacades);
1004 }
1005
1006 /** Interface representing instance methods that consume otation order values.
1007 * @since 12.0
1008 */
1009 public interface RotationOrderConsumer {
1010 /** Consume a data.
1011 * @param value value to consume
1012 */
1013 void accept(RotationOrder value);
1014 }
1015
1016 /** Interface representing instance methods that consume units lists values. */
1017 public interface UnitListConsumer {
1018 /** Consume a list of units.
1019 * @param value value to consume
1020 */
1021 void accept(List<Unit> value);
1022 }
1023
1024 /** Interface representing instance methods that consume Maneuvrable values. */
1025 public interface ManeuvrableConsumer {
1026 /** Consume a Maneuvrable.
1027 * @param value value to consume
1028 */
1029 void accept(Maneuvrable value);
1030 }
1031 }