ParseToken.java

  1. /* Copyright 2002-2022 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.Vector3D;
  26. import org.orekit.bodies.CelestialBodies;
  27. import org.orekit.bodies.CelestialBody;
  28. import org.orekit.errors.OrekitException;
  29. import org.orekit.errors.OrekitMessages;
  30. import org.orekit.files.ccsds.definitions.BodyFacade;
  31. import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
  32. import org.orekit.files.ccsds.definitions.CenterName;
  33. import org.orekit.files.ccsds.definitions.FrameFacade;
  34. import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
  35. import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
  36. import org.orekit.files.ccsds.definitions.TimeSystem;
  37. import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior;
  38. import org.orekit.files.ccsds.ndm.cdm.Maneuvrable;
  39. import org.orekit.files.ccsds.utils.ContextBinding;
  40. import org.orekit.time.AbsoluteDate;
  41. import org.orekit.utils.units.Unit;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  127.     /** Get the content of the entry as a list of free-text strings.
  128.      * <p>
  129.      * Free-text strings are normalized by replacing all occurrences
  130.      * of '_' with space, and collapsing several spaces as one space only.
  131.      * </p>
  132.      * @return content of the entry as a list of free-test strings
  133.      */
  134.     public List<String> getContentAsNormalizedList() {
  135.         return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsNormalizedString()));
  136.     }

  137.     /** Get the content of the entry as normalized and uppercased.
  138.      * @return entry normalized and uppercased content
  139.      */
  140.     public String getContentAsUppercaseString() {
  141.         return getContentAsNormalizedString().toUpperCase(Locale.US);
  142.     }

  143.     /** Get the content of the entry as a list of normalized and uppercased strings.
  144.      * @return content of the entry as a list of normalized and uppercased strings
  145.      */
  146.     public List<String> getContentAsUppercaseList() {
  147.         return Arrays.asList(SPLIT_AT_COMMAS.split(getContentAsUppercaseString()));
  148.     }

  149.     /** Get the content of the entry as an enum.
  150.      * @param cls enum class
  151.      * @param <T> type of the enum
  152.      * @return entry content
  153.      */
  154.     public <T extends Enum<T>> T getContentAsEnum(final Class<T> cls) {
  155.         return toEnum(cls, getRawContent());
  156.     }

  157.     /** Get the content of the entry as a list of enum.
  158.      * @param cls enum class
  159.      * @param <T> type of the enum
  160.      * @return entry content
  161.      */
  162.     public <T extends Enum<T>> List<T> getContentAsEnumList(final Class<T> cls) {
  163.         final String[] elements = SPLIT_AT_COMMAS.split(getRawContent());
  164.         final List<T> list = new ArrayList<>(elements.length);
  165.         for (int i = 0; i < elements.length; ++i) {
  166.             list.add(toEnum(cls, elements[i]));
  167.         }
  168.         return list;
  169.     }

  170.     /** Get the content of the entry as a double.
  171.      * @return content as a double
  172.      */
  173.     public double getContentAsDouble() {
  174.         try {
  175.             return Double.parseDouble(content);
  176.         } catch (NumberFormatException nfe) {
  177.             throw generateException(nfe);
  178.         }
  179.     }

  180.     /** Get the content of the entry as a vector.
  181.      * @return content as a vector
  182.      */
  183.     public Vector3D getContentAsVector() {
  184.         try {
  185.             final String[] fields = SPACE.split(content);
  186.             if (fields.length == 3) {
  187.                 return new Vector3D(Double.parseDouble(fields[0]),
  188.                                     Double.parseDouble(fields[1]),
  189.                                     Double.parseDouble(fields[2]));
  190.             }
  191.         } catch (NumberFormatException nfe) {
  192.             // ignored, error handled below, together with wrong number of fields
  193.         }
  194.         throw generateException(null);
  195.     }

  196.     /** Get the content of the entry as a boolean.
  197.      * @return content as a boolean
  198.      */
  199.     public boolean getContentAsBoolean() {
  200.         if (BOOLEAN_TRUE.matcher(content).matches()) {
  201.             return true;
  202.         } else if (BOOLEAN_FALSE.matcher(content).matches()) {
  203.             return false;
  204.         } else {
  205.             throw generateException(null);
  206.         }
  207.     }

  208.     /** Get the content of the entry as an integer.
  209.      * @return content as an integer
  210.      */
  211.     public int getContentAsInt() {
  212.         try {
  213.             return Integer.parseInt(content);
  214.         } catch (NumberFormatException nfe) {
  215.             throw generateException(nfe);
  216.         }
  217.     }

  218.     /** Get the content of the entry as an uppercase character.
  219.      * @return content as an uppercase character
  220.      */
  221.     public char getContentAsUppercaseCharacter() {
  222.         try {
  223.             return getContentAsUppercaseString().charAt(0);
  224.         } catch (NumberFormatException nfe) {
  225.             throw generateException(nfe);
  226.         }
  227.     }

  228.     /** Get the units.
  229.      * @return units of the entry (may be null)
  230.      */
  231.     public Unit getUnits() {
  232.         return units;
  233.     }

  234.     /** Get the number of the line in the CCSDS data message.
  235.      * @return number of the line in the CCSDS data message
  236.      */
  237.     public int getLineNumber() {
  238.         return lineNumber;
  239.     }

  240.     /** Get the name of the file.
  241.      * @return name of the file
  242.      */
  243.     public String getFileName() {
  244.         return fileName;
  245.     }

  246.     /** Process the content as a normalized string.
  247.      * @param consumer consumer of the normalized string
  248.      * @return always returns {@code true}
  249.      * @see #processAsUppercaseString(StringConsumer)
  250.      */
  251.     public boolean processAsNormalizedString(final StringConsumer consumer) {
  252.         if (type == TokenType.ENTRY) {
  253.             consumer.accept(getContentAsNormalizedString());
  254.         }
  255.         return true;
  256.     }

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

  268.     /** Process the content as an indexed normalized string.
  269.      * @param index index
  270.      * @param consumer consumer of the indexed normalized string
  271.      * @return always returns {@code true}
  272.      */
  273.     public boolean processAsIndexedNormalizedString(final int index, final IndexedStringConsumer consumer) {
  274.         if (type == TokenType.ENTRY) {
  275.             consumer.accept(index, getContentAsNormalizedString());
  276.         }
  277.         return true;
  278.     }

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

  290.     /** Process the content as a list of normalized strings.
  291.      * @param consumer consumer of the normalized strings list
  292.      * @return always returns {@code true}
  293.      */
  294.     public boolean processAsNormalizedList(final StringListConsumer consumer) {
  295.         if (type == TokenType.ENTRY) {
  296.             consumer.accept(getContentAsNormalizedList());
  297.         }
  298.         return true;
  299.     }

  300.     /** Process the content as a list of normalized uppercase strings.
  301.      * @param consumer consumer of the normalized uppercase strings list
  302.      * @return always returns {@code true}
  303.      */
  304.     public boolean processAsUppercaseList(final StringListConsumer consumer) {
  305.         if (type == TokenType.ENTRY) {
  306.             consumer.accept(getContentAsUppercaseList());
  307.         }
  308.         return true;
  309.     }

  310.     /** Process the content as an enum.
  311.      * @param cls enum class
  312.      * @param consumer consumer of the enum
  313.      * @param <T> type of the enum
  314.      * @return always returns {@code true}
  315.      */
  316.     public <T extends Enum<T>> boolean processAsEnum(final Class<T> cls, final EnumConsumer<T> consumer) {
  317.         if (type == TokenType.ENTRY) {
  318.             consumer.accept(getContentAsEnum(cls));
  319.         }
  320.         return true;
  321.     }

  322.     /** Process the content as a list of enums.
  323.      * @param cls enum class
  324.      * @param consumer consumer of the enums list
  325.      * @param <T> type of the enum
  326.      * @return always returns {@code true}
  327.      */
  328.     public <T extends Enum<T>> boolean processAsEnumsList(final Class<T> cls, final EnumListConsumer<T> consumer) {
  329.         if (type == TokenType.ENTRY) {
  330.             consumer.accept(getContentAsEnumList(cls));
  331.         }
  332.         return true;
  333.     }

  334.     /** Process the content as a boolean.
  335.      * @param consumer consumer of the boolean
  336.      * @return always returns {@code true}
  337.      */
  338.     public boolean processAsBoolean(final BooleanConsumer consumer) {
  339.         if (type == TokenType.ENTRY) {
  340.             consumer.accept(getContentAsBoolean());
  341.         }
  342.         return true;
  343.     }

  344.     /** Process the content as an integer.
  345.      * @param consumer consumer of the integer
  346.      * @return always returns {@code true}
  347.      */
  348.     public boolean processAsInteger(final IntConsumer consumer) {
  349.         if (type == TokenType.ENTRY) {
  350.             consumer.accept(getContentAsInt());
  351.         }
  352.         return true;
  353.     }

  354.     /** Process the content as an array of integers. No spaces between commas are allowed.
  355.      * @param consumer consumer of the array
  356.      * @return always returns {@code true}
  357.      */
  358.     public boolean processAsIntegerArrayNoSpace(final IntegerArrayConsumer consumer) {
  359.         try {
  360.             if (type == TokenType.ENTRY) {
  361.                 // Do not allow spaces
  362.                 final String[] fields = SPLIT_AT_COMMAS_NO_SPACE.split(getRawContent());
  363.                 final int[] integers = new int[fields.length];
  364.                 for (int i = 0; i < fields.length; ++i) {
  365.                     integers[i] = Integer.parseInt(fields[i]);
  366.                 }
  367.                 consumer.accept(integers);
  368.             }
  369.             return true;
  370.         } catch (NumberFormatException nfe) {
  371.             throw generateException(nfe);
  372.         }
  373.     }

  374.     /** Process the content as an array of integers. Spaces are replaced by commas.
  375.      * @param consumer consumer of the array
  376.      * @return always returns {@code true}
  377.      */
  378.     public boolean processAsIntegerArray(final IntegerArrayConsumer consumer) {
  379.         try {
  380.             if (type == TokenType.ENTRY) {
  381.                 final String[] fields = SPLIT_AT_COMMAS.split(getRawContent().replace(" ", ","));
  382.                 final int[] integers = new int[fields.length];
  383.                 for (int i = 0; i < fields.length; ++i) {
  384.                     integers[i] = Integer.parseInt(fields[i]);
  385.                 }
  386.                 consumer.accept(integers);
  387.             }
  388.             return true;
  389.         } catch (NumberFormatException nfe) {
  390.             throw generateException(nfe);
  391.         }
  392.     }

  393.     /** Process the content as a normalized character.
  394.      * @param consumer consumer of the normalized character
  395.      * @return always returns {@code true}
  396.      */
  397.     public boolean processAsNormalizedCharacter(final CharConsumer consumer) {
  398.         if (type == TokenType.ENTRY) {
  399.             consumer.accept(getContentAsUppercaseCharacter());
  400.         }
  401.         return true;
  402.     }

  403.     /** Process the content as a double.
  404.      * @param standard units of parsed content as specified by CCSDS standard
  405.      * @param behavior behavior to adopt for parsed unit
  406.      * @param consumer consumer of the double
  407.      * @return always returns {@code true}
  408.      */
  409.     public boolean processAsDouble(final Unit standard, final ParsedUnitsBehavior behavior,
  410.                                    final DoubleConsumer consumer) {
  411.         if (type == TokenType.ENTRY) {
  412.             consumer.accept(behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  413.         }
  414.         return true;
  415.     }

  416.     /** Process the content as an labeled double.
  417.      * @param label label
  418.      * @param standard units of parsed content as specified by CCSDS standard
  419.      * @param behavior behavior to adopt for parsed unit
  420.      * @param consumer consumer of the indexed double
  421.      * @return always returns {@code true}
  422.      */
  423.     public boolean processAsLabeledDouble(final char label,
  424.                                           final Unit standard, final ParsedUnitsBehavior behavior,
  425.                                           final LabeledDoubleConsumer consumer) {
  426.         if (type == TokenType.ENTRY) {
  427.             consumer.accept(label, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  428.         }
  429.         return true;
  430.     }

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

  446.     /** Process the content as a doubly-indexed double.
  447.      * @param i first index
  448.      * @param j second index
  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 doubly-indexed double
  452.      * @return always returns {@code true}
  453.      */
  454.     public boolean processAsDoublyIndexedDouble(final int i, final int j,
  455.                                                 final Unit standard, final ParsedUnitsBehavior behavior,
  456.                                                 final DoublyIndexedDoubleConsumer consumer) {
  457.         if (type == TokenType.ENTRY) {
  458.             consumer.accept(i, j, behavior.select(getUnits(), standard).toSI(getContentAsDouble()));
  459.         }
  460.         return true;
  461.     }

  462.     /** Process the content as a vector.
  463.      * @param consumer consumer of the vector
  464.      * @return always returns {@code true} (or throws an exception)
  465.      */
  466.     public boolean processAsVector(final VectorConsumer consumer) {
  467.         if (type == TokenType.ENTRY) {
  468.             consumer.accept(getContentAsVector());
  469.         }
  470.         return true;
  471.     }

  472.     /** Process the content as a date.
  473.      * @param consumer consumer of the date
  474.      * @param context context binding
  475.      * @return always returns {@code true} (or throws an exception)
  476.      */
  477.     public boolean processAsDate(final DateConsumer consumer, final ContextBinding context) {
  478.         if (type == TokenType.ENTRY) {
  479.             if (context.getTimeSystem() == null) {
  480.                 throw new OrekitException(OrekitMessages.CCSDS_TIME_SYSTEM_NOT_READ_YET,
  481.                                           getLineNumber(), getFileName());
  482.             }
  483.             consumer.accept(context.getTimeSystem().getConverter(context).parse(content));
  484.         }
  485.         return true;
  486.     }

  487.     /** Process the content as a time system.
  488.      * @param consumer consumer of the time system
  489.      * @return always returns {@code true} (or throws an exception)
  490.      */
  491.     public boolean processAsTimeSystem(final TimeSystemConsumer consumer) {
  492.         if (type == TokenType.ENTRY) {
  493.             consumer.accept(TimeSystem.parse(getContentAsUppercaseString()));
  494.         }
  495.         return true;
  496.     }

  497.     /** Process the content as a frame.
  498.      * @param consumer consumer of the frame
  499.      * @param context context binding
  500.      * @param allowCelestial if true, {@link CelestialBodyFrame} are allowed
  501.      * @param allowOrbit if true, {@link OrbitRelativeFrame} are allowed
  502.      * @param allowSpacecraft if true, {@link SpacecraftBodyFrame} are allowed
  503.      * @return always returns {@code true}
  504.      */
  505.     public boolean processAsFrame(final FrameConsumer consumer, final ContextBinding context,
  506.                                   final boolean allowCelestial, final boolean allowOrbit,
  507.                                   final boolean allowSpacecraft) {
  508.         if (type == TokenType.ENTRY) {
  509.             try {
  510.                 consumer.accept(FrameFacade.parse(DASH.
  511.                                                   matcher(getContentAsUppercaseString()).
  512.                                                   replaceAll("").
  513.                                                   replace(' ', '_'),
  514.                                                   context.getConventions(),
  515.                                                   context.isSimpleEOP(), context.getDataContext(),
  516.                                                   allowCelestial, allowOrbit, allowSpacecraft));
  517.             } catch (OrekitException oe) {
  518.                 throw generateException(oe);
  519.             }
  520.         }
  521.         return true;
  522.     }

  523.     /** Process the content as a body center.
  524.      * @param consumer consumer of the body center
  525.      * @param celestialBodies factory for celestial bodies
  526.      * @return always returns {@code true}
  527.      */
  528.     public boolean processAsCenter(final CenterConsumer consumer, final CelestialBodies celestialBodies) {
  529.         if (type == TokenType.ENTRY) {
  530.             final String centerName = getContentAsUppercaseString();
  531.             consumer.accept(new BodyFacade(centerName, body(centerName, celestialBodies)));
  532.         }
  533.         return true;
  534.     }

  535.     /** Process the content as a body center list.
  536.      * @param consumer consumer of the body center list
  537.      * @param celestialBodies factory for celestial bodies
  538.      * @return always returns {@code true}
  539.      */
  540.     public boolean processAsCenterList(final CenterListConsumer consumer, final CelestialBodies celestialBodies) {
  541.         if (type == TokenType.ENTRY) {
  542.             final List<BodyFacade> facades = new ArrayList<>();
  543.             for (final String centerName : SPLIT_AT_COMMAS.split(getContentAsUppercaseString())) {
  544.                 facades.add(new BodyFacade(centerName, body(centerName, celestialBodies)));
  545.             }
  546.             consumer.accept(facades);
  547.         }
  548.         return true;
  549.     }

  550.     /** Process the content as a list of units.
  551.      * @param consumer consumer of the time scale
  552.      * @return always returns {@code true} (or throws an exception)
  553.      */
  554.     public boolean processAsUnitList(final UnitListConsumer consumer) {
  555.         if (type == TokenType.ENTRY) {
  556.             final String bracketed = getContentAsNormalizedString();
  557.             if (bracketed.charAt(0) != '[' || bracketed.charAt(bracketed.length() - 1) != ']') {
  558.                 throw generateException(null);
  559.             }
  560.             final String unbracketed = bracketed.substring(1, bracketed.length() - 1).trim();
  561.             try {
  562.                 consumer.accept(Stream.of(SPLIT_AT_COMMAS.split(unbracketed)).
  563.                                 map(s -> Unit.parse(s)).
  564.                                 collect(Collectors.toList()));
  565.             } catch (OrekitException oe) {
  566.                 // one unit is unknown
  567.                 throw generateException(oe);
  568.             }
  569.         }
  570.         return true;
  571.     }

  572.      /** Process the content as free text string.
  573.      * @param consumer consumer of the string
  574.      * @return always returns {@code true}
  575.      */
  576.     public boolean processAsFreeTextString(final StringConsumer consumer) {
  577.         if (type == TokenType.ENTRY) {
  578.             consumer.accept(getRawContent());
  579.         }
  580.         return true;
  581.     }

  582.     /** Process the content as an array of doubles.
  583.      * @param consumer consumer of the array
  584.      * @return always returns {@code true}
  585.      */
  586.     public boolean processAsDoubleArray(final DoubleArrayConsumer consumer) {
  587.         try {
  588.             if (type == TokenType.ENTRY) {
  589.                 final String[] fields = SPLIT_AT_COMMAS.split(getRawContent().replace(" ", ","));
  590.                 final double[] doubles = new double[fields.length];
  591.                 for (int i = 0; i < fields.length; ++i) {
  592.                     doubles[i] = Double.parseDouble(fields[i]);
  593.                 }
  594.                 consumer.accept(doubles);
  595.             }
  596.             return true;
  597.         } catch (NumberFormatException nfe) {
  598.             throw generateException(nfe);
  599.         }
  600.     }

  601.     /** Process the content of the Maneuvrable enum.
  602.      * @param consumer consumer of the enum
  603.      * @return always returns {@code true}
  604.      */
  605.     public boolean processAsManeuvrableEnum(final ManeuvrableConsumer consumer) {
  606.         if (type == TokenType.ENTRY) {
  607.             consumer.accept(Maneuvrable.getEnum(getRawContent()));
  608.         }
  609.         return true;
  610.     }

  611.     /** Generate a parse exception for this entry.
  612.      * @param cause underlying cause exception (may be null)
  613.      * @return exception for this entry
  614.      */
  615.     public OrekitException generateException(final Exception cause) {
  616.         return new OrekitException(cause, OrekitMessages.UNABLE_TO_PARSE_ELEMENT_IN_FILE,
  617.                                    getName(), getLineNumber(), getFileName());
  618.     }

  619.     /** Get the body corresponding to a center name.
  620.      * @param centerName name of the center
  621.      * @param celestialBodies factory for celestial bodies
  622.      * @return celestial body corresponding to name, or null
  623.      */
  624.     private CelestialBody body(final String centerName, final CelestialBodies celestialBodies) {

  625.         // convert some known names
  626.         final String canonicalValue;
  627.         if (centerName.equals("SOLAR SYSTEM BARYCENTER") || centerName.equals("SSB")) {
  628.             canonicalValue = "SOLAR_SYSTEM_BARYCENTER";
  629.         } else if (centerName.equals("EARTH MOON BARYCENTER") || centerName.equals("EARTH-MOON BARYCENTER") ||
  630.                         centerName.equals("EARTH BARYCENTER") || centerName.equals("EMB")) {
  631.             canonicalValue = "EARTH_MOON";
  632.         } else {
  633.             canonicalValue = centerName;
  634.         }

  635.         try {
  636.             return CenterName.valueOf(canonicalValue).getCelestialBody(celestialBodies);
  637.         } catch (IllegalArgumentException iae) {
  638.             // ignored, we just let body set to null
  639.             return null;
  640.         }

  641.     }

  642.     /** Convert a value to an enum.
  643.      * @param cls enum class
  644.      * @param value value to convert to an enum
  645.      * @param <T> type of the enum
  646.      * @return enumerate corresponding to the value
  647.      */
  648.     private <T extends Enum<T>> T toEnum(final Class<T> cls, final String value) {
  649.         // first replace space characters
  650.         final String noSpace = value.replace(' ', '_');
  651.         try {
  652.             // first try without changing case, as some CCSDS enums are mixed case (like RangeUnits for TDM)
  653.             return Enum.valueOf(cls, noSpace);
  654.         } catch (IllegalArgumentException iae1) {
  655.             try {
  656.                 // second try, using more standard uppercase
  657.                 return Enum.valueOf(cls, noSpace.toUpperCase(Locale.US));
  658.             } catch (IllegalArgumentException iae2) {
  659.                 // use the first exception for the message
  660.                 throw generateException(iae1);
  661.             }
  662.         }
  663.     }

  664.     /** Interface representing instance methods that consume string values. */
  665.     public interface StringConsumer {
  666.         /** Consume a string.
  667.          * @param value value to consume
  668.          */
  669.         void accept(String value);
  670.     }

  671.     /** Interface representing instance methods that consume indexed string values. */
  672.     public interface IndexedStringConsumer {
  673.         /** Consume an indexed string.
  674.          * @param index index
  675.          * @param value value to consume
  676.          */
  677.         void accept(int index, String value);
  678.     }

  679.     /** Interface representing instance methods that consume lists of strings values. */
  680.     public interface StringListConsumer {
  681.         /** Consume a list of strings.
  682.          * @param value value to consume
  683.          */
  684.         void accept(List<String> value);
  685.     }

  686.     /** Interface representing instance methods that consume enum values.
  687.      * @param <T> type of the enum
  688.      */
  689.     public interface EnumConsumer<T extends Enum<T>> {
  690.         /** Consume an enum.
  691.          * @param value value to consume
  692.          */
  693.         void accept(T value);
  694.     }

  695.     /** Interface representing instance methods that consume lists of enum values.
  696.      * @param <T> type of the enum
  697.      */
  698.     public interface EnumListConsumer<T extends Enum<T>> {
  699.         /** Consume an enum.
  700.          * @param value value to consume
  701.          */
  702.         void accept(List<T> value);
  703.     }

  704.     /** Interface representing instance methods that consume boolean values. */
  705.     public interface BooleanConsumer {
  706.         /** Consume a boolean.
  707.          * @param value value to consume
  708.          */
  709.         void accept(boolean value);
  710.     }

  711.     /** Interface representing instance methods that consume integer values. */
  712.     public interface IntConsumer {
  713.         /** Consume an integer.
  714.          * @param value value to consume
  715.          */
  716.         void accept(int value);
  717.     }

  718.     /** Interface representing instance methods that consume integer array. */
  719.     public interface IntegerArrayConsumer {
  720.         /** Consume an array of integers.
  721.          * @param integers array of integers
  722.          */
  723.         void accept(int[] integers);
  724.     }

  725.     /** Interface representing instance methods that consume character values. */
  726.     public interface CharConsumer {
  727.         /** Consume a character.
  728.          * @param value value to consume
  729.          */
  730.         void accept(char value);
  731.     }

  732.     /** Interface representing instance methods that consume double values. */
  733.     public interface DoubleConsumer {
  734.         /** Consume a double.
  735.          * @param value value to consume
  736.          */
  737.         void accept(double value);
  738.     }

  739.     /** Interface representing instance methods that consume labeled double values. */
  740.     public interface LabeledDoubleConsumer {
  741.         /** Consume an indexed double.
  742.          * @param label label
  743.          * @param value value to consume
  744.          */
  745.         void accept(char label, double value);
  746.     }

  747.     /** Interface representing instance methods that consume indexed double values. */
  748.     public interface IndexedDoubleConsumer {
  749.         /** Consume an indexed double.
  750.          * @param i index
  751.          * @param value value to consume
  752.          */
  753.         void accept(int i, double value);
  754.     }

  755.     /** Interface representing instance methods that consume doubly-indexed double values. */
  756.     public interface DoublyIndexedDoubleConsumer {
  757.         /** Consume a doubly indexed double.
  758.          * @param i first index
  759.          * @param j second index
  760.          * @param value value to consume
  761.          */
  762.         void accept(int i, int j, double value);
  763.     }

  764.     /** Interface representing instance methods that consume vector values. */
  765.     public interface VectorConsumer {
  766.         /** Consume a vector.
  767.          * @param value value to consume
  768.          */
  769.         void accept(Vector3D value);
  770.     }

  771.     /** Interface representing instance methods that consume date values. */
  772.     public interface DateConsumer {
  773.         /** Consume a date.
  774.          * @param value value to consume
  775.          */
  776.         void accept(AbsoluteDate value);
  777.     }

  778.     /** Interface representing instance methods that consume time systems values. */
  779.     public interface TimeSystemConsumer {
  780.         /** Consume a time system.
  781.          * @param value value to consume
  782.          */
  783.         void accept(TimeSystem value);
  784.     }

  785.     /** Interface representing instance methods that consume frame values. */
  786.     public interface FrameConsumer {
  787.         /** Consume a frame.
  788.          * @param frameFacade facade in front of several frames types
  789.          */
  790.         void accept(FrameFacade frameFacade);
  791.     }

  792.     /** Interface representing instance methods that consume center values. */
  793.     public interface CenterConsumer {
  794.         /** Consume a body center.
  795.          * @param bodyFacade facade for celestial body name and body
  796.          */
  797.         void accept(BodyFacade bodyFacade);
  798.     }

  799.     /** Interface representing instance methods that consume center lists. */
  800.     public interface CenterListConsumer {
  801.         /** Consume a body center.
  802.          * @param bodyFacades facades for celestial bodies name and bodies
  803.          */
  804.         void accept(List<BodyFacade> bodyFacades);
  805.     }

  806.     /** Interface representing instance methods that consume units lists values. */
  807.     public interface UnitListConsumer {
  808.         /** Consume a list of units.
  809.          * @param value value to consume
  810.          */
  811.         void accept(List<Unit> value);
  812.     }

  813.     /** Interface representing instance methods that consume double array. */
  814.     public interface DoubleArrayConsumer {
  815.         /** Consume an array of doubles.
  816.          * @param doubles array of doubles
  817.          */
  818.         void accept(double[] doubles);
  819.     }

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