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