1   /* Copyright 2002-2016 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     /** Keyword corresponding to the parsed key.
121      * @return keyword corresponding to the parsed key
122      * (null if not recognized)
123      */
124     public Keyword getKeyword() {
125         return keyword;
126     }
127 
128     /** Get the key.
129      * @return key
130      */
131     public String getKey() {
132         return key;
133     }
134 
135     /** Get the value.
136      * @return value
137      */
138     public String getValue() {
139         return value;
140     }
141 
142     /** Get the value as a double number.
143      * @return value
144      * @exception OrekitException if value is not a number
145      */
146     public double getDoubleValue() throws OrekitException {
147         try {
148             return Double.parseDouble(value);
149         } catch (NumberFormatException nfe) {
150             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
151                                       lineNumber, fileName, line);
152         }
153     }
154 
155 }