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.ndm;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.function.Function;
23  import java.util.function.Supplier;
24  
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.files.ccsds.section.CommentsContainer;
28  import org.orekit.files.ccsds.utils.FileFormat;
29  import org.orekit.files.ccsds.utils.lexical.ParseToken;
30  import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
31  import org.orekit.files.ccsds.utils.parsing.AbstractMessageParser;
32  
33  /** A parser for the CCSDS NDM (Navigation Data Message).
34   * @author Luc Maisonobe
35   * @since 11.0
36   */
37  public class NdmParser extends AbstractMessageParser<Ndm> {
38  
39      /** Builder for the constituents parsers. */
40      private final ParserBuilder builder;
41  
42      /** Current constituent parser. */
43      private AbstractMessageParser<? extends NdmConstituent<?, ?>> constituentParser;
44  
45      /** Container for comments. */
46      private CommentsContainer comments;
47  
48      /** Container for constituents. */
49      private List<NdmConstituent<?, ?>> constituents;
50  
51      /** Simple constructor.
52       * <p>
53       * Calling this constructor directly is not recommended. Users should rather use
54       * {@link org.orekit.files.ccsds.ndm.ParserBuilder#buildNdmParser()
55       * parserBuilder.buildNdmParser()}.
56       * </p>
57       * @param builder builder for the constituents parsers
58       * @param filters filters to apply to parse tokens
59       * @since 12.0
60       */
61      public NdmParser(final ParserBuilder builder,
62                       final Function<ParseToken, List<ParseToken>>[] filters) {
63          super(NdmStructureKey.ndm.name(), null, filters);
64          this.builder = builder;
65      }
66  
67      /** {@inheritDoc} */
68      @Override
69      public Map<String, XmlTokenBuilder> getSpecialXmlElementsBuilders() {
70          final Map<String, XmlTokenBuilder> builders = super.getSpecialXmlElementsBuilders();
71  
72          // special handling of root elements for all constituents
73          builders.putAll(builder.buildTdmParser().getSpecialXmlElementsBuilders());
74          builders.putAll(builder.buildOpmParser().getSpecialXmlElementsBuilders());
75          builders.putAll(builder.buildOmmParser().getSpecialXmlElementsBuilders());
76          builders.putAll(builder.buildOemParser().getSpecialXmlElementsBuilders());
77          builders.putAll(builder.buildOcmParser().getSpecialXmlElementsBuilders());
78          builders.putAll(builder.buildApmParser().getSpecialXmlElementsBuilders());
79          builders.putAll(builder.buildAemParser().getSpecialXmlElementsBuilders());
80          builders.putAll(builder.buildAcmParser().getSpecialXmlElementsBuilders());
81          builders.putAll(builder.buildCdmParser().getSpecialXmlElementsBuilders());
82  
83          return builders;
84  
85      }
86  
87      /** {@inheritDoc} */
88      @Override
89      public void reset(final FileFormat fileFormat) {
90          reset(fileFormat, this::processToken);
91          constituentParser = null;
92          comments          = new CommentsContainer();
93          constituents      = new ArrayList<>();
94      }
95  
96      /** {@inheritDoc} */
97      @Override
98      public Ndm build() {
99          // build the file from parsed comments and constituents
100         return new Ndm(comments.getComments(), constituents);
101     }
102 
103     /**
104      * Add comment.
105      * <p>
106      * Comments are accepted only at start. Once
107      * other content is stored in the same section, comments are refused.
108      * </p>
109      * @param comment comment line
110      * @return true if comment was accepted
111      */
112     public boolean addComment(final String comment) {
113         return comments.addComment(comment);
114     }
115 
116     /** Prepare parsing of a TDM constituent.
117      * @return always return true
118      */
119     boolean manageTdmConstituent() {
120         return manageConstituent(builder::buildTdmParser);
121     }
122 
123     /** Prepare parsing of an OPM constituent.
124      * @return always return true
125      */
126     boolean manageOpmConstituent() {
127         return manageConstituent(builder::buildOpmParser);
128     }
129 
130     /** Prepare parsing of an OMM constituent.
131      * @return always return true
132      */
133     boolean manageOmmConstituent() {
134         return manageConstituent(builder::buildOmmParser);
135     }
136 
137     /** Prepare parsing of an OEM constituent.
138      * @return always return true
139      */
140     boolean manageOemConstituent() {
141         return manageConstituent(builder::buildOemParser);
142     }
143 
144     /** Prepare parsing of an OCM constituent.
145      * @return always return true
146      */
147     boolean manageOcmConstituent() {
148         return manageConstituent(builder::buildOcmParser);
149     }
150 
151     /** Prepare parsing of an APM constituent.
152      * @return always return true
153      */
154     boolean manageApmConstituent() {
155         return manageConstituent(builder::buildApmParser);
156     }
157 
158     /** Prepare parsing of a AEM constituent.
159      * @return always return true
160      */
161     boolean manageAemConstituent() {
162         return manageConstituent(builder::buildAemParser);
163     }
164 
165     /** Prepare parsing of a ACM constituent.
166      * @return always return true
167      * @since 12.0
168      */
169     boolean manageAcmConstituent() {
170         return manageConstituent(builder::buildAcmParser);
171     }
172 
173     /** Prepare parsing of a CDM constituent.
174      * @return always return true
175      */
176     boolean manageCdmConstituent() {
177         return manageConstituent(builder::buildCdmParser);
178     }
179 
180     /** Prepare parsing of a constituent.
181      * @param parserSupplier supplier for constituent parser
182      * @return always return true
183      */
184     boolean manageConstituent(final Supplier<AbstractMessageParser<? extends NdmConstituent<?, ?>>> parserSupplier) {
185 
186         // as we have started parsing constituents, we cannot accept any further comments
187         comments.refuseFurtherComments();
188 
189         // create a parser for the constituent
190         constituentParser = parserSupplier.get();
191         constituentParser.reset(getFileFormat());
192 
193         return true;
194 
195     }
196 
197     /** Process one token.
198      * @param token token to process
199      * @return true if token was processed, false otherwise
200      */
201     private boolean processToken(final ParseToken token) {
202 
203         if (getFileFormat() == FileFormat.KVN) {
204             // NDM combined instantiation can only be formatted as XML messages
205             throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, token.getFileName());
206         }
207 
208         if (constituentParser == null) {
209             // we are in the global NDM structure
210             try {
211                 return NdmStructureKey.valueOf(token.getName()).process(token, this);
212             } catch (IllegalArgumentException iae) {
213                 // token has not been recognized
214                 return false;
215             }
216         } else {
217             // we are inside one constituent
218             constituentParser.process(token);
219             if (constituentParser.wasEndTagSeen()) {
220                 // we have seen the end tag, we must go back global structure parsing
221                 constituents.add(constituentParser.build());
222                 constituentParser = null;
223             }
224             return true;
225         }
226 
227     }
228 
229 }