1   /* Copyright 2002-2026 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.errors;
18  
19  import java.io.Serial;
20  import java.text.MessageFormat;
21  import java.util.Locale;
22  
23  import org.hipparchus.exception.Localizable;
24  import org.hipparchus.exception.MathRuntimeException;
25  
26  /** This class is the base class for all specific exceptions thrown by
27   * the Orekit classes.
28  
29   * <p>When the Orekit classes throw exceptions that are specific to
30   * the package, these exceptions are always subclasses of
31   * OrekitException. When exceptions that are already covered by the
32   * standard java API should be thrown, like
33   * ArrayIndexOutOfBoundsException or InvalidParameterException, these
34   * standard exceptions are thrown rather than the Hipparchus specific
35   * ones.</p>
36   * <p>This class also provides utility methods to throw some standard
37   * java exceptions with localized messages.</p>
38   *
39   * @author Luc Maisonobe
40  
41   */
42  
43  public class OrekitException extends RuntimeException implements LocalizedException {
44  
45      /** Serializable UID. */
46      @Serial
47      private static final long serialVersionUID = 20150611L;
48  
49      /** Format specifier (to be translated). */
50      private final Localizable specifier;
51  
52      /** Parts to insert in the format (no translation). */
53      private final Object[] parts;
54  
55      /** Simple constructor.
56       * Build an exception with a translated and formatted message
57       * @param specifier format specifier (to be translated)
58       * @param parts parts to insert in the format (no translation)
59       */
60      public OrekitException(final Localizable specifier, final Object... parts) {
61          this.specifier = specifier;
62          this.parts     = (parts == null) ? new Object[0] : parts.clone();
63      }
64  
65      /** Copy constructor.
66       * @param exception exception to copy from
67       * @since 5.1
68       */
69      public OrekitException(final OrekitException exception) {
70          super(exception);
71          this.specifier = exception.specifier;
72          this.parts     = exception.parts.clone();
73      }
74  
75      /** Simple constructor.
76       * Build an exception from a cause and with a specified message
77       * @param message descriptive message
78       * @param cause underlying cause
79       */
80      public OrekitException(final Localizable message, final Throwable cause) {
81          super(cause);
82          this.specifier = message;
83          this.parts     = new Object[0];
84      }
85  
86      /** Simple constructor.
87       * Build an exception from a cause and with a translated and formatted message
88       * @param cause underlying cause
89       * @param specifier format specifier (to be translated)
90       * @param parts parts to insert in the format (no translation)
91       */
92      public OrekitException(final Throwable cause, final Localizable specifier,
93                             final Object... parts) {
94          super(cause);
95          this.specifier = specifier;
96          this.parts     = (parts == null) ? new Object[0] : parts.clone();
97      }
98  
99      /** Simple constructor.
100      * Build an exception from an Hipparchus exception
101      * @param exception underlying Hipparchus exception
102      * @since 6.0
103      */
104     public OrekitException(final MathRuntimeException exception) {
105         super(exception);
106         this.specifier = exception.getSpecifier();
107         this.parts     = exception.getParts();
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     public String getMessage(final Locale locale) {
113         return buildMessage(locale);
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public String getMessage() {
119         return getMessage(Locale.US);
120     }
121 
122     /** {@inheritDoc} */
123     @Override
124     public String getLocalizedMessage() {
125         return getMessage(Locale.getDefault());
126     }
127 
128     /** {@inheritDoc} */
129     @Override
130     public Localizable getSpecifier() {
131         return specifier;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public Object[] getParts() {
137         return parts.clone();
138     }
139 
140     /** Recover a OrekitException, possibly embedded in a {@link MathRuntimeException}.
141      * <p>
142      * If the {@code MathRuntimeException} does not embed a OrekitException, a
143      * new one will be created.
144      * </p>
145      * @param exception MathRuntimeException to analyze
146      * @return a (possibly embedded) OrekitException
147      */
148     public static OrekitException unwrap(final MathRuntimeException exception) {
149 
150         for (Throwable t = exception; t != null; t = t.getCause()) {
151             if (t instanceof OrekitException orekitException) {
152                 return orekitException;
153             }
154         }
155 
156         return new OrekitException(exception);
157 
158     }
159 
160     /**
161      * Builds a message string by from a pattern and its arguments.
162      * @param locale Locale in which the message should be translated
163      * @return a message string
164      */
165     private String buildMessage(final Locale locale) {
166         if (specifier == null) {
167             return "";
168         } else {
169             try {
170                 final String localizedString = specifier.getLocalizedString(locale);
171                 if (localizedString == null) {
172                     return "";
173                 } else {
174                     return new MessageFormat(localizedString, locale).format(parts);
175                 }
176                 //CHECKSTYLE: stop IllegalCatch check
177             } catch (Throwable t) {
178                 //CHECKSTYLE: resume IllegalCatch check
179                 // Message formatting or localization failed
180                 // Catch all exceptions to prevent the stack trace from being lost
181                 // Add the exception as suppressed so the user can fix that bug too
182                 this.addSuppressed(t);
183                 // just use the source string as the message
184                 return specifier.getSourceString();
185             }
186         }
187     }
188 
189 }