1 /* Copyright 2002-2021 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.parsing;
18
19 import java.io.IOException;
20 import java.util.HashMap;
21 import java.util.Map;
22
23 import org.hipparchus.exception.LocalizedCoreFormats;
24 import org.orekit.data.DataSource;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitInternalError;
27 import org.orekit.files.ccsds.utils.FileFormat;
28 import org.orekit.files.ccsds.utils.lexical.LexicalAnalyzerSelector;
29 import org.orekit.files.ccsds.utils.lexical.MessageParser;
30 import org.orekit.files.ccsds.utils.lexical.MessageVersionXmlTokenBuilder;
31 import org.orekit.files.ccsds.utils.lexical.ParseToken;
32 import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
33
34 /** Parser for CCSDS messages.
35 * <p>
36 * Note than starting with Orekit 11.0, CCSDS message parsers are
37 * mutable objects that gather the data being parsed, until the
38 * message is complete and the {@link #parseMessage(org.orekit.data.DataSource)
39 * parseMessage} method has returned. This implies that parsers
40 * should <em>not</em> be used in a multi-thread context. The recommended
41 * way to use parsers is to either dedicate one parser for each message
42 * and drop it afterwards, or to use a single-thread loop.
43 * </p>
44 * @param <T> type of the file
45 * @author Luc Maisonobe
46 * @since 11.0
47 */
48 public abstract class AbstractMessageParser<T> implements MessageParser<T> {
49
50 /** Safety limit for loop over processing states. */
51 private static final int MAX_LOOP = 100;
52
53 /** Root element for XML files. */
54 private final String root;
55
56 /** Key for format version. */
57 private final String formatVersionKey;
58
59 /** Anticipated next processing state. */
60 private ProcessingState next;
61
62 /** Current processing state. */
63 private ProcessingState current;
64
65 /** Fallback processing state. */
66 private ProcessingState fallback;
67
68 /** Format of the file ready to be parsed. */
69 private FileFormat format;
70
71 /** Flag for XML end tag. */
72 private boolean endTagSeen;
73
74 /** Simple constructor.
75 * @param root root element for XML files
76 * @param formatVersionKey key for format version
77 */
78 protected AbstractMessageParser(final String root, final String formatVersionKey) {
79 this.root = root;
80 this.formatVersionKey = formatVersionKey;
81 this.current = null;
82 setFallback(new ErrorState());
83 }
84
85 /** Set fallback processing state.
86 * <p>
87 * The fallback processing state is used if anticipated state fails
88 * to parse the token.
89 * </p>
90 * @param fallback processing state to use if anticipated state does not work
91 */
92 public void setFallback(final ProcessingState fallback) {
93 this.fallback = fallback;
94 }
95
96 /** Reset parser to initial state before parsing.
97 * @param fileFormat format of the file ready to be parsed
98 * @param initialState initial processing state
99 */
100 protected void reset(final FileFormat fileFormat, final ProcessingState initialState) {
101 format = fileFormat;
102 current = initialState;
103 endTagSeen = false;
104 anticipateNext(fallback);
105 }
106
107 /** Set the flag for XML end tag.
108 * @param endTagSeen if true, the XML end tag has been seen
109 */
110 public void setEndTagSeen(final boolean endTagSeen) {
111 this.endTagSeen = endTagSeen;
112 }
113
114 /** Check if XML end tag has been seen.
115 * @return true if XML end tag has been seen
116 */
117 public boolean wasEndTagSeen() {
118 return endTagSeen;
119 }
120
121 /** Get the current processing state.
122 * @return current processing state
123 */
124 public ProcessingState getCurrent() {
125 return current;
126 }
127
128 /** Get the file format.
129 * @return file format
130 */
131 protected FileFormat getFileFormat() {
132 return format;
133 }
134
135 /** {@inheritDoc} */
136 @Override
137 public T parseMessage(final DataSource source) {
138 try {
139 return LexicalAnalyzerSelector.select(source).accept(this);
140 } catch (IOException ioe) {
141 throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE,
142 ioe.getLocalizedMessage());
143 }
144 }
145
146 /** {@inheritDoc} */
147 @Override
148 public String getFormatVersionKey() {
149 return formatVersionKey;
150 }
151
152 /** {@inheritDoc} */
153 @Override
154 public Map<String, XmlTokenBuilder> getSpecialXmlElementsBuilders() {
155
156 final HashMap<String, XmlTokenBuilder> builders = new HashMap<>();
157
158 if (formatVersionKey != null) {
159 // special handling of root tag that contains the format version
160 builders.put(root, new MessageVersionXmlTokenBuilder());
161 }
162
163 return builders;
164
165 }
166
167 /** Anticipate what next processing state should be.
168 * @param anticipated anticipated next processing state
169 */
170 public void anticipateNext(final ProcessingState anticipated) {
171 this.next = anticipated;
172 }
173
174 /** {@inheritDoc} */
175 @Override
176 public void process(final ParseToken token) {
177
178 // loop over the various states until one really processes the token
179 for (int i = 0; i < MAX_LOOP; ++i) {
180 if (current.processToken(token)) {
181 return;
182 }
183 current = next;
184 next = fallback;
185 }
186
187 // this should never happen
188 throw new OrekitInternalError(null);
189
190 }
191
192 }