1   /* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
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.iirv.terms.base;
18  
19  import org.hipparchus.util.FastMath;
20  import org.orekit.errors.OrekitIllegalArgumentException;
21  import org.orekit.errors.OrekitMessages;
22  
23  import java.util.regex.Pattern;
24  
25  /**
26   * Term in an IIRV Vector representing a Long (or integer) value.
27   *
28   * @author Nick LaFarge
29   * @since 13.0
30   */
31  public class LongValuedIIRVTerm extends IIRVVectorTerm<Long> {
32  
33      /** Space pattern. */
34      private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
35  
36      /** True if negative values are permitted, false if the value is positive. */
37      private final boolean isSigned;
38  
39      /**
40       * Constructs an IIRV Vector Term represented by a long. This representation is used for any numeric terms
41       * in the IIRV Vector that do not contain a decimal point.
42       *
43       * @param pattern  Regular expression pattern that validates the term
44       * @param value    Value of the term, expressed as a long
45       * @param length   LengthC of the term, measured in number of characters in the String representation
46       * @param isSigned True if negative values are permitted, false if the value is positive
47       */
48      public LongValuedIIRVTerm(final String pattern, final long value, final int length, final boolean isSigned) {
49          super(pattern, value, length);
50          this.isSigned = isSigned;
51          validateString(toEncodedString(value));
52          validateNumericValue(this.value());
53      }
54  
55      /**
56       * Constructs an IIRV Vector Term represented by a Long. This representation is used for any numeric terms
57       * in the IIRV Vector that do not contain a decimal point.
58       *
59       * @param pattern  Regular expression pattern that validates the term
60       * @param value    Value of the term, expressed as a String
61       * @param length   Length of the term, measured in number of characters in the String representation
62       * @param isSigned True if negative values are permitted, false if the value is positive
63       */
64      public LongValuedIIRVTerm(final String pattern, final String value, final int length, final boolean isSigned) {
65          super(pattern, LongValuedIIRVTerm.computeValueFromString(value), length);
66          this.isSigned = isSigned;
67          validateString(value);
68          validateNumericValue(this.value());
69      }
70  
71      /**
72       * Parses a string as a long, removing any leading spaces.
73       *
74       * @param value String value of the term.
75       * @return the long represented by the argument
76       */
77      public static long computeValueFromString(final String value) {
78          try {
79              // Remove spaces (for positive values)
80              final String integerString = SPACE_PATTERN.matcher(value).replaceAll("");
81  
82              // Cast String to integer
83              return Long.parseLong(integerString);
84          } catch (NumberFormatException nfe) {
85              throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_TERM_VALUE, value);
86          }
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      public String toEncodedString(final Long termValue) {
92  
93          // Reserve one character for the sign (if applicable)
94          final int signAdjustedStringLength = isSigned ? length() - 1 : length();
95  
96          String signCharacter = "";  // Sign character ('' for unsigned number)
97          if (isSigned) {
98              signCharacter = termValue > 0 ? " " : "-";
99          }
100 
101         // Pad each string with zeros to reach the desired length
102         final String integerString = String.format("%0" + signAdjustedStringLength + "d", FastMath.abs(termValue));
103 
104         return signCharacter + integerString;
105     }
106 
107     /**
108      * Convert the underlying {@link #value()} from long to int.
109      *
110      * @return The value of the term as an int
111      */
112     public int toInt() {
113         return FastMath.toIntExact(value());
114     }
115 
116     /**
117      * Validate a given numerical value to ensure it is not greater than the maximum possible accuracy of this term,
118      * and that it does not contain a negative value for a positive term (when {@link #isSigned} is false).
119      *
120      * @param value long value of this term
121      */
122     protected void validateNumericValue(final long value) {
123         // Compute the number of characters excluding the sign character
124         final int n = isSigned ? length() - 1 : length();
125 
126         // If the value is greater than the maximum possible value, throw an error
127         final double maxPossibleValue = FastMath.pow(10, n);
128         if (FastMath.abs(value) >= maxPossibleValue) {
129             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_VALUE_TOO_LARGE, FastMath.abs(value), maxPossibleValue);
130         }
131 
132         // Throw an error if the signs don't match up
133         if (!isSigned && value < 0) {
134             throw new OrekitIllegalArgumentException(OrekitMessages.NOT_POSITIVE, value);
135         }
136     }
137 }