1 /* Copyright 2002-2019 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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;
18
19 import java.util.Locale;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
23 import org.orekit.errors.OrekitException;
24 import org.orekit.errors.OrekitMessages;
25
26 /** Holder for key-value pair.
27 * <p>
28 * The syntax for key-value lines in CCSDS files is:
29 * </p>
30 * <pre>
31 * KEY = value [unit]
32 * </pre>
33 * <p>
34 * The "[unit]" part (with the square brackets included) is optional.
35 * The COMMENT keyword is an exception and does not have an '=' but directly
36 * the value as free form text. The META_START, META_STOP, COVARIANCE_START
37 * and COVARIANCE_STOP keywords are other exception and do not have anything
38 * else following them on the line.
39 * </p>
40 * @author Luc Maisonobe
41 * @since 6.1
42 */
43 class KeyValue {
44
45 /** Regular expression for splitting lines. */
46 private final Pattern PATTERN =
47 Pattern.compile("\\p{Space}*([A-Z][A-Z_0-9]*)\\p{Space}*=?\\p{Space}*(.*?)\\p{Space}*(?:\\[.*\\])?\\p{Space}*");
48
49 /** Regular expression for user defined keywords. */
50 private final Pattern USER_DEFINED_KEYWORDS =
51 Pattern.compile("USER_DEFINED_[A-Z][A-Z_]*");
52
53 /** Line from which pair is extracted. */
54 private final String line;
55
56 /** Number of the line from which pair is extracted. */
57 private final int lineNumber;
58
59 /** Name of the file. */
60 private final String fileName;
61
62 /** Keyword enum corresponding to parsed key. */
63 private final Keyword keyword;
64
65 /** Key part of the pair. */
66 private final String key;
67
68 /** Value part of the line. */
69 private final String value;
70
71 /** Build a pair by splitting a key-value line.
72 * <p>
73 * The splitting is very basic and only extracts words using a regular
74 * expression ignoring the '=' sign and the optional unit. No attempt
75 * is made to recognize the special keywords. The key and value parts
76 * may be empty if not matched, and the keyword may be null.
77 * </p>
78 * <p> The value part may be upper case or lower case. This constructor
79 * converts all lower case values to upper case.
80 * @param line to split
81 * @param lineNumber number of the line in the CCSDS data message
82 * @param fileName name of the file
83 */
84 KeyValue(final String line, final int lineNumber, final String fileName) {
85
86 this.line = line;
87 this.lineNumber = lineNumber;
88 this.fileName = fileName;
89
90 final Matcher matcher = PATTERN.matcher(line);
91 if (matcher.matches()) {
92 key = matcher.group(1);
93 final String rawValue = matcher.group(2);
94 Keyword recognized;
95 try {
96 recognized = Keyword.valueOf(key);
97 } catch (IllegalArgumentException iae) {
98 if (USER_DEFINED_KEYWORDS.matcher(key).matches()) {
99 recognized = Keyword.USER_DEFINED_X;
100 } else {
101 recognized = null;
102 }
103 }
104 keyword = recognized;
105 if (recognized == Keyword.COMMENT) {
106 value = rawValue;
107 } else {
108 value = rawValue.
109 toUpperCase(Locale.US).
110 replace('_', ' ').
111 replaceAll("\\p{Space}+", " ");
112 }
113 } else {
114 key = "";
115 value = key;
116 keyword = null;
117 }
118 }
119
120 /** Build a pair by giving the input arguments.
121 * This is essentially used while parsing XML files.
122 * It is made to be allow the use of the class KeyValue for both Keyvalue and XML file formats.
123 * Thus common functions can be used for the parsing.
124 * <p>
125 * The splitting is very basic and only extracts words using a regular
126 * expression ignoring the '=' sign and the optional unit. No attempt
127 * is made to recognize the special keywords. The key and value parts
128 * may be empty if not matched, and the keyword may be null.
129 * </p>
130 * <p> The value part may be upper case or lower case. This constructor
131 * converts all lower case values to upper case.
132 * @param keyword the keyword
133 * @param value the value attached to the keyword
134 * @param line the line where the keyword was found
135 * @param lineNumber number of the line in the CCSDS data message
136 * @param fileName name of the file
137 */
138 KeyValue(final Keyword keyword, final String value,
139 final String line, final int lineNumber,
140 final String fileName) {
141 this.keyword = keyword;
142 this.key = keyword.name();
143 this.value = value;
144 this.lineNumber = lineNumber;
145 this.line = line;
146 this.fileName = fileName;
147 }
148
149 /** Keyword corresponding to the parsed key.
150 * @return keyword corresponding to the parsed key
151 * (null if not recognized)
152 */
153 public Keyword getKeyword() {
154 return keyword;
155 }
156
157 /** Get the key.
158 * @return key
159 */
160 public String getKey() {
161 return key;
162 }
163
164 /** Get the value.
165 * @return value
166 */
167 public String getValue() {
168 return value;
169 }
170
171 /** Get the value as a double number.
172 * @return value
173 */
174 public double getDoubleValue() {
175 try {
176 return Double.parseDouble(value);
177 } catch (NumberFormatException nfe) {
178 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
179 lineNumber, fileName, line);
180 }
181 }
182
183 /** Get the value as an integer number.
184 * @return value
185 */
186 public int getIntegerValue() {
187 try {
188 return Integer.parseInt(value);
189 } catch (NumberFormatException nfe) {
190 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
191 lineNumber, fileName, line);
192 }
193 }
194
195 }