ParseToken.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.lexical;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.List;
  21. import java.util.Locale;
  22. import java.util.regex.Pattern;
  23. import java.util.stream.Collectors;
  24. import java.util.stream.Stream;

  25. import org.hipparchus.geometry.euclidean.threed.RotationOrder;
  26. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  27. import org.orekit.bodies.CelestialBodies;
  28. import org.orekit.bodies.CelestialBody;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.files.ccsds.definitions.BodyFacade;
  32. import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
  33. import org.orekit.files.ccsds.definitions.CenterName;
  34. import org.orekit.files.ccsds.definitions.FrameFacade;
  35. import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
  36. import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
  37. import org.orekit.files.ccsds.definitions.TimeSystem;
  38. import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
  39. import org.orekit.files.ccsds.ndm.cdm.Maneuvrable;
  40. import org.orekit.files.ccsds.utils.ContextBinding;
  41. import org.orekit.time.AbsoluteDate;
  42. import org.orekit.utils.units.Unit;

  43. /** Token occurring during CCSDS file parsing.
  44.  * <p>
  45.  * Parse tokens correspond to:
  46.  * <ul>
  47.  *   <li>bloc or entry start</li>
  48.  *   <li>entry content</li>
  49.  *   <li>bloc or entry end</li>
  50.  *   <li>raw lines</li>
  51.  * </ul>
  52.  * @see MessageParser
  53.  * @author Luc Maisonobe
  54.  * @since 11.0
  55.  */
  56. public class ParseToken {

  57.     /** Pattern for dash. */
  58.     private static final Pattern DASH = Pattern.compile("-");

  59.     /** Pattern for spaces. */
  60.     private static final Pattern SPACE = Pattern.compile("\\p{Space}+");

  61.     /** Pattern for splitting comma-separated lists. */
  62.     private static final Pattern SPLIT_AT_COMMAS = Pattern.compile("\\p{Space}*,\\p{Space}*");

  63.     /** Pattern for splitting comma-separated lists with no space in between. */
  64.     private static final Pattern SPLIT_AT_COMMAS_NO_SPACE = Pattern.compile(",");

  65.     /** Pattern for true boolean value. */
  66.     private static final Pattern BOOLEAN_TRUE = Pattern.compile("(?:yes)|(?:true)",
  67.                                                                 Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

  68.     /** Pattern for false boolean value. */
  69.     private static final Pattern BOOLEAN_FALSE = Pattern.compile("(?:no)|(?:false)",
  70.                                                                  Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

  71.     /** Type of the token. */
  72.     private TokenType type;

  73.     /** Name of the entry. */
  74.     private final String name;

  75.     /** Entry content. */
  76.     private final String content;

  77.     /** Units of the entry. */
  78.     private final Unit units;

  79.     /** Number of the line from which pair is extracted. */
  80.     private final int lineNumber;

  81.     /** Name of the file. */
  82.     private final String fileName;

  83.     /** Simple constructor.
  84.      * @param type type of the token
  85.      * @param name name of the block or entry
  86.      * @param content entry content
  87.      * @param units units of the entry
  88.      * @param lineNumber number of the line in the CCSDS data message
  89.      * @param fileName name of the file
  90.      */
  91.     public ParseToken(final TokenType type, final String name, final String content, final Unit units,
  92.                       final int lineNumber, final String fileName) {
  93.         this.type       = type;
  94.         this.name       = name;
  95.         this.content    = content;
  96.         this.units      = units;
  97.         this.lineNumber = lineNumber;
  98.         this.fileName   = fileName;
  99.     }

  100.     /** Get the type of the token.
  101.      * @return type of the token
  102.      */
  103.     public TokenType getType() {
  104.         return type;
  105.     }

  106.     /** Get the name of the block or entry.
  107.      * @return name of the block or entry
  108.      */
  109.     public String getName() {
  110.         return name;
  111.     }

  112.     /** Get the raw content of the entry.
  113.      * @return entry raw content
  114.      */
  115.     public String getRawContent() {
  116.         return content;
  117.     }

  118.     /** Get the content of the entry.
  119.      * <p>
  120.      * Free-text strings are normalized by replacing all occurrences
  121.      * of '_' with space, and collapsing several spaces as one space only.
  122.      * </p>
  123.      * @return entry content
  124.      */
  125.     public String getContentAsNormalizedString() {
  126.         return SPACE.matcher(content.replace('_', ' ')).replaceAll(" ").trim();
  127.     }

  128.     /** Get the content of the entry as a list of free-text strings.
  129.      * @return content of the entry as a list of free-test strings
  130.      * @since 12.0
  131.      */
  132.     public List<String> getContentAsFreeTextList() {
  133.         return Arrays.asList(SPLIT_AT_COMMAS.split(getRawContent()));
  134.     }

  135.     /** Get the content of the entry as a list of normalized strings.
  136.      * <p>
  137.      * Normalization is performed by replacing all occurrences
  138.      * of '_' with space, and collapsing several spaces as one space only.
  139.      * </p>
  140.      * @return content of the entry as a list of free-test strings
  141.      */
  142.     public List<String> getContentAsNormalizedList() {
  143.         return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsNormalizedString()));
  144.     }

  145.     /** Get the content of the entry as normalized and uppercased.
  146.      * @return entry normalized and uppercased content
  147.      */
  148.     public String getContentAsUppercaseString() {
  149.         return getContentAsNormalizedString().toUpperCase(Locale.US);
  150.     }

  151.     /** Get the content of the entry as a list of normalized and uppercased strings.
  152.      * @return content of the entry as a list of normalized and uppercased strings
  153.      */
  154.     public List<String> getContentAsUppercaseList() {
  155.         return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsUppercaseString()));
  156.     }

  157.     /** Get the content of the entry as an enum.
  158.      * @param cls enum class
  159.      * @param <T> type of the enum
  160.      * @return entry content
  161.      */
  162.     public <T extends Enum<T>> T getContentAsEnum(final Class<T> cls) {
  163.         return toEnum(cls, getRawContent());
  164.     }

  165.     /** Get the content of the entry as a list of enum.
  166.      * @param cls enum class
  167.      * @param <T> type of the enum
  168.      * @return entry content
  169.      */
  170.     public <T extends Enum<T>> List<T> getContentAsEnumList(final Class<T> cls) {
  171.         final String[] elements = SPLIT_AT_COMMAS.split(getRawContent());
  172.         final List<T> list = new ArrayList<>(elements.length);
  173.         for (int i = 0; i < elements.length; ++i) {
  174.             list.add(toEnum(cls, elements[i]));
  175.         }
  176.         return list;
  177.     }

  178.     /** Get the content of the entry as a double.
  179.      * @return content as a double
  180.      */
  181.     public double getContentAsDouble() {
  182.         try {
  183.             return Double.parseDouble(content);
  184.         } catch (NumberFormatException nfe) {
  185.             throw generateException(nfe);
  186.         }
  187.     }

  188.     /** Get the content of the entry as a vector.
  189.      * @return content as a vector
  190.      */
  191.     public Vector3D getContentAsVector() {
  192.         try {
  193.             final String[] fields = SPACE.split(content);
  194.             if (fields.length == 3) {
  195.                 return new Vector3D(Double.parseDouble(fields[0]),
  196.                                     Double.parseDouble(fields[1]),
  197.                                     Double.parseDouble(fields[2]));
  198.             }
  199.         } catch (NumberFormatException nfe) {
  200.             // ignored, error handled below, together with wrong number of fields
  201.         }
  202.         throw generateException(null);
  203.     }

  204.     /** Get the content of the entry as a boolean.
  205.      * @return content as a boolean
  206.      */
  207.     public boolean getContentAsBoolean() {
  208.         if (BOOLEAN_TRUE.matcher(content).matches()) {
  209.             return true;
  210.         } else if (BOOLEAN_FALSE.matcher(content).matches()) {
  211.             return false;
  212.         } else {
  213.             throw generateException(null);
  214.         }
  215.     }

  216.     /** Get the content of the entry as an integer.
  217.      * @return content as an integer
  218.      */
  219.     public int getContentAsInt() {
  220.         try {
  221.             return Integer.parseInt(content);
  222.         } catch (NumberFormatException nfe) {
  223.             throw generateException(nfe);
  224.         }
  225.     }

  226.     /** Get the content of the entry as an uppercase character.
  227.      * @return content as an uppercase character
  228.      */
  229.     public char getContentAsUppercaseCharacter() {
  230.         try {
  231.             return getContentAsUppercaseString().charAt(0);
  232.         } catch (NumberFormatException nfe) {
  233.             throw generateException(nfe);
  234.         }
  235.     }

  236.     /** Get the units.
  237.      * @return units of the entry (may be null)
  238.      */
  239.     public Unit getUnits() {
  240.         return units;
  241.     }

  242.     /** Get the number of the line in the CCSDS data message.
  243.      * @return number of the line in the CCSDS data message
  244.      */
  245.     public int getLineNumber() {
  246.         return lineNumber;
  247.     }

  248.     /** Get the name of the file.
  249.      * @return name of the file
  250.      */
  251.     public String getFileName() {
  252.         return fileName;
  253.     }

  254.     /** Process the content as a normalized string.
  255.      * @param consumer consumer of the normalized string
  256.      * @return always returns {@code true}
  257.      * @see #processAsUppercaseString(StringConsumer)
  258.      */
  259.     public boolean processAsNormalizedString(final StringConsumer consumer) {
  260.         if (type == TokenType.ENTRY) {
  261.             consumer.accept(getContentAsNormalizedString());
  262.         }
  263.         return true;
  264.     }

  265.     /** Process the content as a normalized uppercase string.
  266.      * @param consumer consumer of the normalized uppercase string
  267.      * @return always returns {@code true}
  268.      * @see #processAsNormalizedString(StringConsumer)
  269.      */
  270.     public boolean processAsUppercaseString(final StringConsumer consumer) {
  271.         if (type == TokenType.ENTRY) {
  272.             consumer.accept(getContentAsUppercaseString());
  273.         }
  274.         return true;
  275.     }

  276.     /** Process the content as an indexed normalized string.
  277.      * @param index index
  278.      * @param consumer consumer of the indexed normalized string
  279.      * @return always returns {@code true}
  280.      */
  281.     public boolean processAsIndexedNormalizedString(final int index, final IndexedStringConsumer consumer) {
  282.         if (type == TokenType.ENTRY) {
  283.             consumer.accept(index, getContentAsNormalizedString());
  284.         }
  285.         return true;
  286.     }

  287.     /** Process the content as an indexed normalized uppercase string.
  288.      * @param index index
  289.      * @param consumer consumer of the indexed normalized uppercase string
  290.      * @return always returns {@code true}
  291.      */
  292.     public boolean processAsIndexedUppercaseString(final int index, final IndexedStringConsumer consumer) {
  293.         if (type == TokenType.ENTRY) {
  294.             consumer.accept(index, getContentAsUppercaseString());
  295.         }
  296.         return true;
  297.     }

  298.     /** Process the content as a list of free-text strings.
  299.      * @param consumer consumer of the free-text strings list
  300.      * @return always returns {@code true}
  301.      * @since 12.0
  302.      */
  303.     public boolean processAsFreeTextList(final StringListConsumer consumer) {
  304.         if (type == TokenType.ENTRY) {
  305.             consumer.accept(getContentAsFreeTextList());
  306.         }
  307.         return true;
  308.     }

  309.     /** Process the content as a list of normalized strings.
  310.      * @param consumer consumer of the normalized strings list
  311.      * @return always returns {@code true}
  312.      */
  313.     public boolean processAsNormalizedList(final StringListConsumer consumer) {
  314.         if (type == TokenType.ENTRY) {
  315.             consumer.accept(getContentAsNormalizedList());
  316.         }
  317.         return true;
  318.     }

  319.     /** Process the content as a list of normalized uppercase strings.
  320.      * @param consumer consumer of the normalized uppercase strings list
  321.      * @return always returns {@code true}
  322.      */
  323.     public boolean processAsUppercaseList(final StringListConsumer consumer) {
  324.         if (type == TokenType.ENTRY) {
  325.             consumer.accept(getContentAsUppercaseList());
  326.         }
  327.         return true;
  328.     }

  329.     /** Process the content as an enum.
  330.      * @param cls enum class
  331.      * @param consumer consumer of the enum
  332.      * @param <T> type of the enum
  333.      * @return always returns {@code true}
  334.      */
  335.     public <T extends Enum<T>> boolean processAsEnum(final Class<T> cls, final EnumConsumer<T> consumer) {
  336.         if (type == TokenType.ENTRY) {
  337.             consumer.accept(getContentAsEnum(cls));
  338.         }
  339.         return true;
  340.     }

  341.     /** Process the content as a list of enums.
  342.      * @param cls enum class
  343.      * @param consumer consumer of the enums list
  344.      * @param <T> type of the enum
  345.      * @return always returns {@code true}
  346.      */
  347.     public <T extends Enum<T>> boolean processAsEnumsList(final Class<T> cls, final EnumListConsumer<T> consumer) {
  348.         if (type == TokenType.ENTRY) {
  349.             consumer.accept(getContentAsEnumList(cls));
  350.         }
  351.         return true;
  352.     }

  353.     /** Process the content as a boolean.
  354.      * @param consumer consumer of the boolean
  355.      * @return always returns {@code true}
  356.      */
  357.     public boolean processAsBoolean(final BooleanConsumer consumer) {
  358.         if (type == TokenType.ENTRY) {
  359.             consumer.accept(getContentAsBoolean());
  360.         }
  361.         return true;
  362.     }

  363.     /** Process the content as an integer.
  364.      * @param consumer consumer of the integer
  365.      * @return always returns {@code true}
  366.      */
  367.     public boolean processAsInteger(final IntConsumer consumer) {
  368.         if (type == TokenType.ENTRY) {
  369.             consumer.accept(getContentAsInt());
  370.         }
  371.         return true;
  372.     }

  373.     /** Process the content as an indexed integer.
  374.      * @param index index
  375.      * @param consumer consumer of the integer
  376.      * @return always returns {@code true}
  377.      * @since 12.0
  378.      */
  379.     public boolean processAsIndexedInteger(final int index, final IndexedIntConsumer consumer) {
  380.         if (type == TokenType.ENTRY) {
  381.             consumer.accept(index, getContentAsInt());
  382.         }
  383.         return true;
  384.     }

  385.     /** Process the content as an array of integers. No spaces between commas are allowed.
  386.      * @param consumer consumer of the array
  387.      * @return always returns {@code true}
  388.      */
  389.     public boolean processAsIntegerArrayNoSpace(final IntegerArrayConsumer consumer) {
  390.         try {
  391.             if (type == TokenType.ENTRY) {
  392.                 // Do not allow spaces
  393.                 final String[] fields = SPLIT_AT_COMMAS_NO_SPACE.split(getRawContent());
  394.                 final int[] integers = new int[fields.length];
  395.                 for (int i = 0; i < fields.length; ++i) {
  396.                     integers[i] = Integer.parseInt(fields[i]);
  397.                 }
  398.                 consumer.accept(integers);
  399.             }
  400.             return true;
  401.         } catch (NumberFormatException nfe) {
  402.             throw generateException(nfe);
  403.         }
  404.     }

  405.     /** Process the content as an array of integers. Spaces are replaced by commas.
  406.      * @param consumer consumer of the array
  407.      * @return always returns {@code true}
  408.      */
  409.     public boolean processAsIntegerArray(final IntegerArrayConsumer consumer) {
  410.         try {
  411.             if (type == TokenType.ENTRY) {
  412.                 final String[] fields = SPACE.split(getRawContent());
  413.                 final int[] integers = new int[fields.length];
  414.                 for (int i = 0; i < fields.length; ++i) {
  415.                     integers[i] = Integer.parseInt(fields[i]);
  416.                 }
  417.                 consumer.accept(integers);
  418.             }
  419.             return true;
  420.         } catch (NumberFormatException nfe) {
  421.             throw generateException(nfe);
  422.         }
  423.     }

  424.     /** Process the content as a normalized character.
  425.      * @param consumer consumer of the normalized character
  426.      * @return always returns {@code true}
  427.      */
  428.     public boolean processAsNormalizedCharacter(final CharConsumer consumer) {
  429.         if (type == TokenType.ENTRY) {
  430.             consumer.accept(getContentAsUppercaseCharacter());
  431.         }
  432.         return true;
  433.     }

  434.     /** Process the content as a double.
  435.      * @param standard units of parsed content as specified by CCSDS standard
  436.      * @param behavior behavior to adopt for parsed unit
  437.      * @param consumer consumer of the double
  438.      * @return always returns {@code true}
  439.      */
  440.     public boolean processAsDouble(final Unit standard, final ParsedUnitsBehavior behavior,
  441.                                    final DoubleConsumer consumer) {
  442.         if (type == TokenType.ENTRY) {
  443.             consumer.accept(behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  444.         }
  445.         return true;
  446.     }

  447.     /** Process the content as a labeled double.
  448.      * @param label label
  449.      * @param standard units of parsed content as specified by CCSDS standard
  450.      * @param behavior behavior to adopt for parsed unit
  451.      * @param consumer consumer of the indexed double
  452.      * @return always returns {@code true}
  453.      */
  454.     public boolean processAsLabeledDouble(final char label,
  455.                                           final Unit standard, final ParsedUnitsBehavior behavior,
  456.                                           final LabeledDoubleConsumer consumer) {
  457.         if (type == TokenType.ENTRY) {
  458.             consumer.accept(label, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  459.         }
  460.         return true;
  461.     }

  462.     /** Process the content as an indexed double.
  463.      * @param i index
  464.      * @param standard units of parsed content as specified by CCSDS standard
  465.      * @param behavior behavior to adopt for parsed unit
  466.      * @param consumer consumer of the indexed double
  467.      * @return always returns {@code true}
  468.      */
  469.     public boolean processAsIndexedDouble(final int i,
  470.                                           final Unit standard, final ParsedUnitsBehavior behavior,
  471.                                           final IndexedDoubleConsumer consumer) {
  472.         if (type == TokenType.ENTRY) {
  473.             consumer.accept(i, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  474.         }
  475.         return true;
  476.     }

  477.     /** Process the content as a doubly-indexed double.
  478.      * @param i first index
  479.      * @param j second index
  480.      * @param standard units of parsed content as specified by CCSDS standard
  481.      * @param behavior behavior to adopt for parsed unit
  482.      * @param consumer consumer of the doubly-indexed double
  483.      * @return always returns {@code true}
  484.      */
  485.     public boolean processAsDoublyIndexedDouble(final int i, final int j,
  486.                                                 final Unit standard, final ParsedUnitsBehavior behavior,
  487.                                                 final DoublyIndexedDoubleConsumer consumer) {
  488.         if (type == TokenType.ENTRY) {
  489.             consumer.accept(i, j, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  490.         }
  491.         return true;
  492.     }

  493.     /** Process the content as an array of doubles.
  494.      * @param standard units of parsed content as specified by CCSDS standard
  495.      * @param behavior behavior to adopt for parsed unit
  496.      * @param consumer consumer of the array
  497.      * @return always returns {@code true}
  498.      * @since 12.0
  499.      */
  500.     public boolean processAsDoubleArray(final Unit standard, final ParsedUnitsBehavior behavior,
  501.                                         final DoubleArrayConsumer consumer) {
  502.         try {
  503.             if (type == TokenType.ENTRY) {
  504.                 final String[] fields = SPACE.split(getRawContent());
  505.                 final double[] doubles = new double[fields.length];
  506.                 for (int i = 0; i < fields.length; ++i) {
  507.                     doubles[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i]));
  508.                 }
  509.                 consumer.accept(doubles);
  510.             }
  511.             return true;
  512.         } catch (NumberFormatException nfe) {
  513.             throw generateException(nfe);
  514.         }
  515.     }

  516.     /** Process the content as an indexed double array.
  517.      * @param index index
  518.      * @param standard units of parsed content as specified by CCSDS standard
  519.      * @param behavior behavior to adopt for parsed unit
  520.      * @param consumer consumer of the indexed double array
  521.      * @return always returns {@code true}
  522.      * @since 12.0
  523.      */
  524.     public boolean processAsIndexedDoubleArray(final int index,
  525.                                                final Unit standard, final ParsedUnitsBehavior behavior,
  526.                                                final IndexedDoubleArrayConsumer consumer) {
  527.         if (type == TokenType.ENTRY) {
  528.             final String[] fields = SPACE.split(content);
  529.             final double[] values = new double[fields.length];
  530.             for (int i = 0; i < fields.length; ++i) {
  531.                 values[i] = behavior.select(getUnits(), standard).toSI(Double.parseDouble(fields[i]));
  532.             }
  533.             consumer.accept(index, values);
  534.         }
  535.         return true;
  536.     }

  537.     /** Process the content as a vector.
  538.      * @param standard units of parsed content as specified by CCSDS standard
  539.      * @param behavior behavior to adopt for parsed unit
  540.      * @param consumer consumer of the vector
  541.      * @return always returns {@code true} (or throws an exception)
  542.      */
  543.     public boolean processAsVector(final Unit standard, final ParsedUnitsBehavior behavior,
  544.                                    final VectorConsumer consumer) {
  545.         if (type == TokenType.ENTRY) {
  546.             final double scale = behavior.select(getUnits(), standard).getScale();
  547.             consumer.accept(getContentAsVector().scalarMultiply(scale));
  548.         }
  549.         return true;
  550.     }

  551.     /** Process the content as a date.
  552.      * @param consumer consumer of the date
  553.      * @param context context binding
  554.      * @return always returns {@code true} (or throws an exception)
  555.      */
  556.     public boolean processAsDate(final DateConsumer consumer, final ContextBinding context) {
  557.         if (type == TokenType.ENTRY) {
  558.             if (context.getTimeSystem() == null) {
  559.                 throw new OrekitException(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_READ_YET,
  560.                                           getLineNumber(), getFileName());
  561.             }
  562.             consumer.accept(context.getTimeSystem().getConverter(context).parse(content));
  563.         }
  564.         return true;
  565.     }

  566.     /** Process the content as a time system.
  567.      * @param consumer consumer of the time system
  568.      * @return always returns {@code true} (or throws an exception)
  569.      */
  570.     public boolean processAsTimeSystem(final TimeSystemConsumer consumer) {
  571.         if (type == TokenType.ENTRY) {
  572.             consumer.accept(TimeSystem.parse(getContentAsUppercaseString()));
  573.         }
  574.         return true;
  575.     }

  576.     /** Process the content as a frame.
  577.      * @param consumer consumer of the frame
  578.      * @param context context binding
  579.      * @param allowCelestial if true, {@link CelestialBodyFrame} are allowed
  580.      * @param allowOrbit if true, {@link OrbitRelativeFrame} are allowed
  581.      * @param allowSpacecraft if true, {@link SpacecraftBodyFrame} are allowed
  582.      * @return always returns {@code true}
  583.      */
  584.     public boolean processAsFrame(final FrameConsumer consumer, final ContextBinding context,
  585.                                   final boolean allowCelestial, final boolean allowOrbit,
  586.                                   final boolean allowSpacecraft) {
  587.         if (type == TokenType.ENTRY) {
  588.             try {
  589.                 consumer.accept(FrameFacade.parse(DASH.
  590.                                                   matcher(getContentAsUppercaseString()).
  591.                                                   replaceAll("").
  592.                                                   replace(' ', '_'),
  593.                                                   context.getConventions(),
  594.                                                   context.isSimpleEOP(), context.getDataContext(),
  595.                                                   allowCelestial, allowOrbit, allowSpacecraft));
  596.             } catch (OrekitException oe) {
  597.                 throw generateException(oe);
  598.             }
  599.         }
  600.         return true;
  601.     }

  602.     /** Process the content as a body center.
  603.      * @param consumer consumer of the body center
  604.      * @param celestialBodies factory for celestial bodies
  605.      * @return always returns {@code true}
  606.      */
  607.     public boolean processAsCenter(final CenterConsumer consumer, final CelestialBodies celestialBodies) {
  608.         if (type == TokenType.ENTRY) {
  609.             final String centerName = getContentAsUppercaseString();
  610.             consumer.accept(new BodyFacade(centerName, body(centerName, celestialBodies)));
  611.         }
  612.         return true;
  613.     }

  614.     /** Process the content as a body center list.
  615.      * @param consumer consumer of the body center list
  616.      * @param celestialBodies factory for celestial bodies
  617.      * @return always returns {@code true}
  618.      */
  619.     public boolean processAsCenterList(final CenterListConsumer consumer, final CelestialBodies celestialBodies) {
  620.         if (type == TokenType.ENTRY) {
  621.             final List<BodyFacade> facades = new ArrayList<>();
  622.             for (final String centerName : SPLIT_AT_COMMAS.split(getContentAsUppercaseString())) {
  623.                 facades.add(new BodyFacade(centerName, body(centerName, celestialBodies)));
  624.             }
  625.             consumer.accept(facades);
  626.         }
  627.         return true;
  628.     }

  629.     /** Process the content as a rotation sequence.
  630.      * @param consumer consumer of the rotation sequence
  631.      * @return always returns {@code true}
  632.      * @since 12.0
  633.      */
  634.     public boolean processAsRotationOrder(final RotationOrderConsumer consumer) {
  635.         if (type == TokenType.ENTRY) {
  636.             try {
  637.                 consumer.accept(RotationOrder.valueOf(getContentAsUppercaseString().
  638.                                                       replace('1', 'X').
  639.                                                       replace('2', 'Y').
  640.                                                       replace('3', 'Z')));
  641.             } catch (IllegalArgumentException iae) {
  642.                 throw new OrekitException(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE,
  643.                                           getContentAsUppercaseString(), getLineNumber(), getFileName());
  644.             }
  645.         }
  646.         return true;
  647.     }

  648.     /** Process the content as a list of units.
  649.      * @param consumer consumer of the time scale
  650.      * @return always returns {@code true} (or throws an exception)
  651.      */
  652.     public boolean processAsUnitList(final UnitListConsumer consumer) {
  653.         if (type == TokenType.ENTRY) {
  654.             final String bracketed = getContentAsNormalizedString();
  655.             if (bracketed.charAt(0) != '[' || bracketed.charAt(bracketed.length() - 1) != ']') {
  656.                 throw generateException(null);
  657.             }
  658.             final String unbracketed = bracketed.substring(1, bracketed.length() - 1).trim();
  659.             try {
  660.                 consumer.accept(Stream.of(SPLIT_AT_COMMAS.split(unbracketed)).
  661.                                 map(s -> Unit.parse(s)).
  662.                                 collect(Collectors.toList()));
  663.             } catch (OrekitException oe) {
  664.                 // one unit is unknown
  665.                 throw generateException(oe);
  666.             }
  667.         }
  668.         return true;
  669.     }

  670.      /** Process the content as free text string.
  671.      * @param consumer consumer of the string
  672.      * @return always returns {@code true}
  673.      */
  674.     public boolean processAsFreeTextString(final StringConsumer consumer) {
  675.         if (type == TokenType.ENTRY) {
  676.             consumer.accept(getRawContent());
  677.         }
  678.         return true;
  679.     }

  680.     /** Process the content of the Maneuvrable enum.
  681.      * @param consumer consumer of the enum
  682.      * @return always returns {@code true}
  683.      */
  684.     public boolean processAsManeuvrableEnum(final ManeuvrableConsumer consumer) {
  685.         if (type == TokenType.ENTRY) {
  686.             consumer.accept(Maneuvrable.getEnum(getRawContent()));
  687.         }
  688.         return true;
  689.     }

  690.     /** Generate a parse exception for this entry.
  691.      * @param cause underlying cause exception (may be null)
  692.      * @return exception for this entry
  693.      */
  694.     public OrekitException generateException(final Exception cause) {
  695.         return new OrekitException(cause, OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE,
  696.                                    getName(), getLineNumber(), getFileName());
  697.     }

  698.     /** Get the body corresponding to a center name.
  699.      * @param centerName name of the center
  700.      * @param celestialBodies factory for celestial bodies
  701.      * @return celestial body corresponding to name, or null
  702.      */
  703.     private CelestialBody body(final String centerName, final CelestialBodies celestialBodies) {

  704.         // convert some known names
  705.         final String canonicalValue;
  706.         if (centerName.equals("SOLAR SYSTEM BARYCENTER") || centerName.equals("SSB")) {
  707.             canonicalValue = "SOLAR_SYSTEM_BARYCENTER";
  708.         } else if (centerName.equals("EARTH MOON BARYCENTER") || centerName.equals("EARTH-MOON BARYCENTER") ||
  709.                         centerName.equals("EARTH BARYCENTER") || centerName.equals("EMB")) {
  710.             canonicalValue = "EARTH_MOON";
  711.         } else {
  712.             canonicalValue = centerName;
  713.         }

  714.         try {
  715.             return CenterName.valueOf(canonicalValue).getCelestialBody(celestialBodies);
  716.         } catch (IllegalArgumentException iae) {
  717.             // ignored, we just let body set to null
  718.             return null;
  719.         }

  720.     }

  721.     /** Convert a value to an enum.
  722.      * @param cls enum class
  723.      * @param value value to convert to an enum
  724.      * @param <T> type of the enum
  725.      * @return enumerate corresponding to the value
  726.      */
  727.     private <T extends Enum<T>> T toEnum(final Class<T> cls, final String value) {
  728.         // first replace space characters
  729.         final String noSpace = value.replace(' ', '_');
  730.         try {
  731.             // first try without changing case, as some CCSDS enums are mixed case (like RangeUnits for TDM)
  732.             return Enum.valueOf(cls, noSpace);
  733.         } catch (IllegalArgumentException iae1) {
  734.             try {
  735.                 // second try, using more standard uppercase
  736.                 return Enum.valueOf(cls, noSpace.toUpperCase(Locale.US));
  737.             } catch (IllegalArgumentException iae2) {
  738.                 // use the first exception for the message
  739.                 throw generateException(iae1);
  740.             }
  741.         }
  742.     }

  743.     /** Interface representing instance methods that consume string values. */
  744.     public interface StringConsumer {
  745.         /** Consume a string.
  746.          * @param value value to consume
  747.          */
  748.         void accept(String value);
  749.     }

  750.     /** Interface representing instance methods that consume indexed string values. */
  751.     public interface IndexedStringConsumer {
  752.         /** Consume an indexed string.
  753.          * @param index index
  754.          * @param value value to consume
  755.          */
  756.         void accept(int index, String value);
  757.     }

  758.     /** Interface representing instance methods that consume lists of strings values. */
  759.     public interface StringListConsumer {
  760.         /** Consume a list of strings.
  761.          * @param value value to consume
  762.          */
  763.         void accept(List<String> value);
  764.     }

  765.     /** Interface representing instance methods that consume enum values.
  766.      * @param <T> type of the enum
  767.      */
  768.     public interface EnumConsumer<T extends Enum<T>> {
  769.         /** Consume an enum.
  770.          * @param value value to consume
  771.          */
  772.         void accept(T value);
  773.     }

  774.     /** Interface representing instance methods that consume lists of enum values.
  775.      * @param <T> type of the enum
  776.      */
  777.     public interface EnumListConsumer<T extends Enum<T>> {
  778.         /** Consume an enum.
  779.          * @param value value to consume
  780.          */
  781.         void accept(List<T> value);
  782.     }

  783.     /** Interface representing instance methods that consume boolean values. */
  784.     public interface BooleanConsumer {
  785.         /** Consume a boolean.
  786.          * @param value value to consume
  787.          */
  788.         void accept(boolean value);
  789.     }

  790.     /** Interface representing instance methods that consume integer values. */
  791.     public interface IntConsumer {
  792.         /** Consume an integer.
  793.          * @param value value to consume
  794.          */
  795.         void accept(int value);
  796.     }

  797.     /** Interface representing instance methods that consume indexed integer values.
  798.      * @since 12.0
  799.      */
  800.     public interface IndexedIntConsumer {
  801.         /** Consume an integer.
  802.          * @param index index
  803.          * @param value value to consume
  804.          */
  805.         void accept(int index, int value);
  806.     }

  807.     /** Interface representing instance methods that consume integer array. */
  808.     public interface IntegerArrayConsumer {
  809.         /** Consume an array of integers.
  810.          * @param integers array of integers
  811.          */
  812.         void accept(int[] integers);
  813.     }

  814.     /** Interface representing instance methods that consume character values. */
  815.     public interface CharConsumer {
  816.         /** Consume a character.
  817.          * @param value value to consume
  818.          */
  819.         void accept(char value);
  820.     }

  821.     /** Interface representing instance methods that consume double values. */
  822.     public interface DoubleConsumer {
  823.         /** Consume a double.
  824.          * @param value value to consume
  825.          */
  826.         void accept(double value);
  827.     }

  828.     /** Interface representing instance methods that consume labeled double values. */
  829.     public interface LabeledDoubleConsumer {
  830.         /** Consume an indexed double.
  831.          * @param label label
  832.          * @param value value to consume
  833.          */
  834.         void accept(char label, double value);
  835.     }

  836.     /** Interface representing instance methods that consume indexed double values. */
  837.     public interface IndexedDoubleConsumer {
  838.         /** Consume an indexed double.
  839.          * @param i index
  840.          * @param value value to consume
  841.          */
  842.         void accept(int i, double value);
  843.     }

  844.     /** Interface representing instance methods that consume doubly-indexed double values. */
  845.     public interface DoublyIndexedDoubleConsumer {
  846.         /** Consume a doubly indexed double.
  847.          * @param i first index
  848.          * @param j second index
  849.          * @param value value to consume
  850.          */
  851.         void accept(int i, int j, double value);
  852.     }

  853.     /** Interface representing instance methods that consume double array. */
  854.     public interface DoubleArrayConsumer {
  855.         /** Consume an array of doubles.
  856.          * @param doubles array of doubles
  857.          */
  858.         void accept(double[] doubles);
  859.     }

  860.     /** Interface representing instance methods that consume indexed double array values.
  861.      * @since 12.0
  862.      */
  863.     public interface IndexedDoubleArrayConsumer {
  864.         /** Consume an indexed double array.
  865.          * @param index index
  866.          * @param value array value to consume
  867.          */
  868.         void accept(int index, double[] value);
  869.     }

  870.     /** Interface representing instance methods that consume vector values. */
  871.     public interface VectorConsumer {
  872.         /** Consume a vector.
  873.          * @param value value to consume
  874.          */
  875.         void accept(Vector3D value);
  876.     }

  877.     /** Interface representing instance methods that consume date values. */
  878.     public interface DateConsumer {
  879.         /** Consume a date.
  880.          * @param value value to consume
  881.          */
  882.         void accept(AbsoluteDate value);
  883.     }

  884.     /** Interface representing instance methods that consume time systems values. */
  885.     public interface TimeSystemConsumer {
  886.         /** Consume a time system.
  887.          * @param value value to consume
  888.          */
  889.         void accept(TimeSystem value);
  890.     }

  891.     /** Interface representing instance methods that consume frame values. */
  892.     public interface FrameConsumer {
  893.         /** Consume a frame.
  894.          * @param frameFacade facade in front of several frames types
  895.          */
  896.         void accept(FrameFacade frameFacade);
  897.     }

  898.     /** Interface representing instance methods that consume center values. */
  899.     public interface CenterConsumer {
  900.         /** Consume a body center.
  901.          * @param bodyFacade facade for celestial body name and body
  902.          */
  903.         void accept(BodyFacade bodyFacade);
  904.     }

  905.     /** Interface representing instance methods that consume center lists. */
  906.     public interface CenterListConsumer {
  907.         /** Consume a body center.
  908.          * @param bodyFacades facades for celestial bodies name and bodies
  909.          */
  910.         void accept(List<BodyFacade> bodyFacades);
  911.     }

  912.     /** Interface representing instance methods that consume otation order values.
  913.      * @since 12.0
  914.      */
  915.     public interface RotationOrderConsumer {
  916.         /** Consume a data.
  917.          * @param value value to consume
  918.          */
  919.         void accept(RotationOrder value);
  920.     }

  921.     /** Interface representing instance methods that consume units lists values. */
  922.     public interface UnitListConsumer {
  923.         /** Consume a list of units.
  924.          * @param value value to consume
  925.          */
  926.         void accept(List<Unit> value);
  927.     }

  928.     /** Interface representing instance methods that consume Maneuvrable values. */
  929.     public interface ManeuvrableConsumer {
  930.         /** Consume a Maneuvrable.
  931.          * @param value value to consume
  932.          */
  933.         void accept(Maneuvrable value);
  934.     }
  935. }