/* Copyright 2002-2017 CS Systèmes d'Information * Licensed to CS Systèmes d'Information (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * CS licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package fr.cs.examples; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.hipparchus.exception.DummyLocalizable; import org.hipparchus.exception.Localizable; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.Predefined; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; /** Simple parser for key/value files. * @param Key type of the parameter keys */ public class KeyValueFileParser<Key extends Enum<Key>> { /** Error message for unknown frame. */ private static final Localizable UNKNOWN_FRAME = new DummyLocalizable("unknown frame {0}"); /** Error message for not Earth frame. */ private static final Localizable NOT_EARTH_FRAME = new DummyLocalizable("frame {0} is not an Earth frame"); /** Enum type. */ private final Class<Key> enumType; /** Key/scalar value map. */ private final Map<Key, String> scalarMap; /** Key/array value map. */ private final Map<Key, List<String>> arrayMap; /** Simple constructor. * @param enumType type of the parameters keys enumerate */ public KeyValueFileParser(Class<Key> enumType) { this.enumType = enumType; this.scalarMap = new HashMap<Key, String>(); this.arrayMap = new HashMap<Key, List<String>>(); } /** Parse an input file. * <p> * The input file syntax is a set of {@code key=value} lines or * {@code key[i]=value} lines. Blank lines and lines starting with '#' * (after whitespace trimming) are silently ignored. The equal sign may * be surrounded by space characters. Keys must correspond to the * {@link Key} enumerate constants, given that matching is not case * sensitive and that '_' characters may appear as '.' characters in * the file. This means that the lines: * <pre> * # this is the semi-major axis * orbit.circular.a = 7231582 * </pre> * are perfectly right and correspond to key {@code Key#ORBIT_CIRCULAR_A} if * such a constant exists in the enumerate. * </p> * <p> * When the key[i] notation is used, all the values extracted from lines * with the same key will be put in a general array that will be retrieved * using one of the {@code getXxxArray(key)} methods. They will <em>not</em> * be available with the {@code getXxx(key)} methods without the {@code Array}. * </p> * @param input input stream * @exception IOException if input file cannot be read * @exception OrekitException if a line cannot be read properly */ public void parseInput(final String name, final InputStream input) throws IOException, OrekitException { final Pattern arrayPattern = Pattern.compile("([\\w\\.]+)\\s*\\[([0-9]+)\\]"); try (final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) { int lineNumber = 0; for (String line = reader.readLine(); line != null; line = reader.readLine()) { ++lineNumber; line = line.trim(); // we ignore blank lines and line starting with '#' if ((line.length() > 0) && !line.startsWith("#")) { String[] fields = line.split("\\s*=\\s*"); if (fields.length != 2) { throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, lineNumber, name, line); } final Matcher matcher = arrayPattern.matcher(fields[0]); if (matcher.matches()) { // this is a key[i]=value line String canonicalized = matcher.group(1).toUpperCase().replaceAll("\\.", "_"); Key key = Key.valueOf(enumType, canonicalized); Integer index = Integer.valueOf(matcher.group(2)); List<String> list = arrayMap.get(key); if (list == null) { list = new ArrayList<String>(); arrayMap.put(key, list); } while (index >= list.size()) { // insert empty strings for missing elements list.add(""); } list.set(index, fields[1]); } else { // this is a key=value line String canonicalized = fields[0].toUpperCase().replaceAll("\\.", "_"); Key key = Key.valueOf(enumType, canonicalized); scalarMap.put(key, fields[1]); } } } } } /** Check if a key is contained in the map. * @param key parameter key * @return true if the key is contained in the map */ public boolean containsKey(final Key key) { return scalarMap.containsKey(key) || arrayMap.containsKey(key); } /** Get a raw string value from a parameters map. * @param key parameter key * @return string value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public String getString(final Key key) throws NoSuchElementException { final String value = scalarMap.get(key); if (value == null) { throw new NoSuchElementException(key.toString()); } return value.trim(); } /** Get a raw string values array from a parameters map. * @param key parameter key * @return string values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public String[] getStringArray(final Key key) throws NoSuchElementException { final List<String> list = arrayMap.get(key); if (list == null) { throw new NoSuchElementException(key.toString()); } String[] array = new String[list.size()]; for (int i = 0; i < array.length; ++i) { array[i] = list.get(i).trim(); } return array; } /** Get a raw double value from a parameters map. * @param key parameter key * @return double value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public double getDouble(final Key key) throws NoSuchElementException { return Double.parseDouble(getString(key)); } /** Get a raw double values array from a parameters map. * @param key parameter key * @return double values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public double[] getDoubleArray(final Key key) throws NoSuchElementException { String[] strings = getStringArray(key); double[] array = new double[strings.length]; for (int i = 0; i < array.length; ++i) { if (!strings[i].isEmpty()) array[i] = Double.parseDouble(strings[i]); else { array[i] = 0.; } } return array; } /** Get a raw int value from a parameters map. * @param key parameter key * @return int value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public int getInt(final Key key) throws NoSuchElementException { return Integer.parseInt(getString(key)); } /** Get a raw int values array from a parameters map. * @param key parameter key * @return int values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public int[] getIntArray(final Key key) throws NoSuchElementException { String[] strings = getStringArray(key); int[] array = new int[strings.length]; for (int i = 0; i < array.length; ++i) { array[i] = Integer.parseInt(strings[i]); } return array; } /** Get a raw boolean value from a parameters map. * @param key parameter key * @return boolean value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public boolean getBoolean(final Key key) throws NoSuchElementException { return Boolean.parseBoolean(getString(key)); } /** Get a raw boolean values array from a parameters map. * @param key parameter key * @return boolean values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public boolean[] getBooleanArray(final Key key) throws NoSuchElementException { String[] strings = getStringArray(key); boolean[] array = new boolean[strings.length]; for (int i = 0; i < array.length; ++i) { array[i] = Boolean.parseBoolean(strings[i]); } return array; } /** Get an angle value from a parameters map. * <p> * The angle is considered to be in degrees in the file, it will be returned in radians * </p> * @param key parameter key * @return angular value corresponding to the key, in radians * @exception NoSuchElementException if key is not in the map */ public double getAngle(final Key key) throws NoSuchElementException { return FastMath.toRadians(getDouble(key)); } /** Get an angle values array from a parameters map. * @param key parameter key * @return angle values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public double[] getAngleArray(final Key key) throws NoSuchElementException { double[] array = getDoubleArray(key); for (int i = 0; i < array.length; ++i) { array[i] = FastMath.toRadians(array[i]); } return array; } /** Get a date value from a parameters map. * @param key parameter key * @param scale time scale in which the date is to be parsed * @return date value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public AbsoluteDate getDate(final Key key, TimeScale scale) throws NoSuchElementException { return new AbsoluteDate(getString(key), scale); } /** Get a date values array from a parameters map. * @param key parameter key * @param scale time scale in which the date is to be parsed * @return date values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public AbsoluteDate[] getDateArray(final Key key, TimeScale scale) throws NoSuchElementException { String[] strings = getStringArray(key); AbsoluteDate[] array = new AbsoluteDate[strings.length]; for (int i = 0; i < array.length; ++i) { array[i] = new AbsoluteDate(strings[i], scale); } return array; } /** Get a time value from a parameters map. * @param key parameter key * @return time value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public TimeComponents getTime(final Key key) throws NoSuchElementException { return TimeComponents.parseTime(getString(key)); } /** Get a time values array from a parameters map. * @param key parameter key * @return time values array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public TimeComponents[] getTimeArray(final Key key) throws NoSuchElementException { String[] strings = getStringArray(key); TimeComponents[] array = new TimeComponents[strings.length]; for (int i = 0; i < array.length; ++i) { array[i] = TimeComponents.parseTime(strings[i]); } return array; } /** Get a vector value from a parameters map. * @param xKey parameter key for abscissa * @param yKey parameter key for ordinate * @param zKey parameter key for height * @param scale time scale in which the date is to be parsed * @return date value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public Vector3D getVector(final Key xKey, final Key yKey, final Key zKey) throws NoSuchElementException { return new Vector3D(getDouble(xKey), getDouble(yKey), getDouble(zKey)); } /** Get a vector values array from a parameters map. * @param xKey parameter key for abscissa * @param yKey parameter key for ordinate * @param zKey parameter key for height * @param scale time scale in which the date is to be parsed * @return date value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public Vector3D[] getVectorArray(final Key xKey, final Key yKey, final Key zKey) throws NoSuchElementException { String[] xStrings = getStringArray(xKey); String[] yStrings = getStringArray(yKey); String[] zStrings = getStringArray(zKey); Vector3D[] array = new Vector3D[xStrings.length]; for (int i = 0; i < array.length; ++i) { array[i] = new Vector3D(Double.parseDouble(xStrings[i]), Double.parseDouble(yStrings[i]), Double.parseDouble(zStrings[i])); } return array; } /** Get a strings list from a parameters map. * @param key parameter key * @param separator elements separator * @return strings list value corresponding to the key * @exception NoSuchElementException if key is not in the map */ public List<String> getStringsList(final Key key, final char separator) throws NoSuchElementException { final String value = scalarMap.get(key); if (value == null) { throw new NoSuchElementException(key.toString()); } return Arrays.asList(value.trim().split("\\s*" + separator + "\\s*")); } /** Get a strings list array from a parameters map. * @param key parameter key * @param separator elements separator * @return strings list array corresponding to the key * @exception NoSuchElementException if key is not in the map */ public List<String>[] getStringsListArray(final Key key, final char separator) throws NoSuchElementException { final String[] strings = getStringArray(key); @SuppressWarnings("unchecked") final List<String>[] array = (List<String>[]) Array.newInstance(List.class, strings.length); for (int i = 0; i < array.length; ++i) { array[i] = Arrays.asList(strings[i].trim().split("\\s*" + separator + "\\s*")); } return array; } /** Get an inertial frame from a parameters map. * @param key parameter key * @return inertial frame corresponding to the key * @exception NoSuchElementException if key is not in the map * @exception OrekitException if frame cannot be built */ public Frame getInertialFrame(final Key key) throws NoSuchElementException, OrekitException { // get the name of the desired frame final String frameName = getString(key); // check the name against predefined frames for (Predefined predefined : Predefined.values()) { if (frameName.equals(predefined.getName())) { if (FramesFactory.getFrame(predefined).isPseudoInertial()) { return FramesFactory.getFrame(predefined); } else { throw new OrekitException(OrekitMessages.NON_PSEUDO_INERTIAL_FRAME, frameName); } } } // none of the frames match the name throw new OrekitException(UNKNOWN_FRAME, frameName); } /** Get an Earth frame from a parameters map. * <p> * We consider Earth frames are the frames with name starting with "ITRF". * </p> * @param key parameter key * @param parameters key/value map containing the parameters * @return Earth frame corresponding to the key * @exception NoSuchElementException if key is not in the map * @exception OrekitException if frame cannot be built */ public Frame getEarthFrame(final Key key) throws NoSuchElementException, OrekitException { // get the name of the desired frame final String frameName = getString(key); // check the name against predefined frames for (Predefined predefined : Predefined.values()) { if (frameName.equals(predefined.getName())) { if (predefined.toString().startsWith("ITRF") || predefined.toString().startsWith("GTOD")) { return FramesFactory.getFrame(predefined); } else { throw new OrekitException(NOT_EARTH_FRAME, frameName); } } } // none of the frames match the name throw new OrekitException(UNKNOWN_FRAME, frameName); } }