1 /* Copyright (c) 2001-2016, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 package org.hsqldb.error;
33 
34 import java.lang.reflect.Field;
35 
36 import org.hsqldb.HsqlException;
37 import org.hsqldb.lib.StringUtil;
38 import org.hsqldb.resources.ResourceBundleHandler;
39 import org.hsqldb.result.Result;
40 
41 /**
42  * Contains static factory methods to produce instances of HsqlException
43  *
44  * @author Loic Lefevre
45  * @author Fred Toussi (fredt@users dot sourceforge.net)
46  * @version 2.2.7
47  * @since 1.9.0
48  */
49 public class Error {
50 
51     //
52     public static boolean TRACE          = false;
53     public static boolean TRACESYSTEMOUT = false;
54 
55     //
56     private static final String defaultMessage = "S1000 General error";
57     private static final String errPropsName   = "sql-state-messages";
58     private static final int bundleHandle =
59         ResourceBundleHandler.getBundleHandle(errPropsName, null);
60     private static final String MESSAGE_TAG      = "$$";
61     private static final int    SQL_STATE_DIGITS = 5;
62     private static final int    SQL_CODE_DIGITS  = 4;
63     private static final int    ERROR_CODE_BASE  = 11;
64 
runtimeError(int code, String add)65     public static RuntimeException runtimeError(int code, String add) {
66 
67         HsqlException e = error(code, add);
68 
69         return new RuntimeException(e.getMessage());
70     }
71 
error(int code, String add)72     public static HsqlException error(int code, String add) {
73         return error((Throwable) null, code, add);
74     }
75 
error(Throwable t, int code, String add)76     public static HsqlException error(Throwable t, int code, String add) {
77 
78         String s = getMessage(code);
79 
80         if (add != null) {
81             s += ": " + add;
82         }
83 
84         return new HsqlException(t, s.substring(SQL_STATE_DIGITS + 1),
85                                  s.substring(0, SQL_STATE_DIGITS), -code);
86     }
87 
parseError(int code, String add, int lineNumber)88     public static HsqlException parseError(int code, String add,
89                                            int lineNumber) {
90 
91         String s = getMessage(code);
92 
93         if (add != null) {
94             s = s + ": " + add;
95         }
96 
97         if (lineNumber > 1) {
98             add = getMessage(ErrorCode.M_parse_line);
99             s   = s + " :" + add + String.valueOf(lineNumber);
100         }
101 
102         return new HsqlException(null, s.substring(SQL_STATE_DIGITS + 1),
103                                  s.substring(0, SQL_STATE_DIGITS), -code);
104     }
105 
error(int code)106     public static HsqlException error(int code) {
107         return error(null, code, 0, null);
108     }
109 
error(int code, Throwable t)110     public static HsqlException error(int code, Throwable t) {
111 
112         String message = getMessage(code, 0, null);
113 
114         return new HsqlException(t, message.substring(0, SQL_STATE_DIGITS),
115                                  -code);
116     }
117 
118     /**
119      * Compose error message by inserting the strings in the add parameters
120      * in placeholders within the error message. The message string contains
121      * $$ markers for each context variable. Context variables are supplied in
122      * the add parameters.
123      *
124      * @param code      main error code
125      * @param subCode   sub error code (if 0 => no subMessage!)
126      * @param   add     optional parameters
127      *
128      * @return an <code>HsqlException</code>
129      */
error(Throwable t, int code, int subCode, final Object[] add)130     public static HsqlException error(Throwable t, int code, int subCode,
131                                       final Object[] add) {
132 
133         String message = getMessage(code, subCode, add);
134         int    sqlCode = subCode < ERROR_CODE_BASE ? code
135                                                    : subCode;
136 
137         return new HsqlException(t, message.substring(SQL_STATE_DIGITS + 1),
138                                  message.substring(0, SQL_STATE_DIGITS),
139                                  -sqlCode);
140     }
141 
142     public static HsqlException parseError(int code, int subCode,
143                                            int lineNumber,
144                                            final Object[] add) {
145 
146         String message = getMessage(code, subCode, add);
147 
148         if (lineNumber > 1) {
149             String sub = getMessage(ErrorCode.M_parse_line);
150 
151             message = message + " :" + sub + String.valueOf(lineNumber);
152         }
153 
154         int sqlCode = subCode < ERROR_CODE_BASE ? code
155                                                 : subCode;
156 
157         return new HsqlException(null,
158                                  message.substring(SQL_STATE_DIGITS + 1),
159                                  message.substring(0, SQL_STATE_DIGITS),
160                                  -sqlCode);
161     }
162 
163     public static HsqlException error(int code, int code2) {
164         return error(code, getMessage(code2));
165     }
166 
167     /**
168      * For SIGNAL and RESIGNAL
169      * @see HsqlException#HsqlException(Throwable,String, String, int)
170      * @return an <code>HsqlException</code>
171      */
172     public static HsqlException error(String message, String sqlState) {
173 
174         int code = getCode(sqlState);
175 
176         if (code < 1000) {
177             code = ErrorCode.X_45000;
178         }
179 
180         if (message == null) {
181             message = getMessage(code);
182         }
183 
184         return new HsqlException(null, message, sqlState, code);
185     }
186 
187     /**
188      * Compose error message by inserting the strings in the add variables
189      * in placeholders within the error message. The message string contains
190      * $$ markers for each context variable. Context variables are supplied in
191      * the add parameter. (by Loic Lefevre)
192      *
193      * @param message  message string
194      * @param add      optional parameters
195      *
196      * @return an <code>HsqlException</code>
197      */
198     private static String insertStrings(String message, Object[] add) {
199 
200         StringBuffer sb        = new StringBuffer(message.length() + 32);
201         int          lastIndex = 0;
202         int          escIndex  = message.length();
203 
204         // removed test: i < add.length
205         // because if mainErrorMessage is equal to "blabla $$"
206         // then the statement escIndex = mainErrorMessage.length();
207         // is never reached!  ???
208         for (int i = 0; i < add.length; i++) {
209             escIndex = message.indexOf(MESSAGE_TAG, lastIndex);
210 
211             if (escIndex == -1) {
212                 break;
213             }
214 
215             sb.append(message.substring(lastIndex, escIndex));
216             sb.append(add[i] == null ? "null exception message"
217                                      : add[i].toString());
218 
219             lastIndex = escIndex + MESSAGE_TAG.length();
220         }
221 
222         escIndex = message.length();
223 
224         sb.append(message.substring(lastIndex, escIndex));
225 
226         return sb.toString();
227     }
228 
229     /**
230      * Returns the error message given the error code.<br/>
231      * This method is be used when throwing exception other
232      * than <code>HsqlException</code>.
233      *
234      * @param errorCode    the error code associated to the error message
235      * @return  the error message associated with the error code
236      */
237     public static String getMessage(final int errorCode) {
238         return getMessage(errorCode, 0, null);
239     }
240 
241     /**
242      * Returns the error SQL STATE sting given the error code.<br/>
243      * This method is be used when throwing exception based on other exceptions.
244      *
245      * @param errorCode    the error code associated to the error message
246      * @return  the error message associated with the error code
247      */
248     public static String getStateString(final int errorCode) {
249         return getMessage(errorCode, 0, null).substring(0, SQL_STATE_DIGITS);
250     }
251 
252     /**
253      * Returns the error message given the error code.<br/> This method is used
254      * when throwing exception other than <code>HsqlException</code>.
255      *
256      * @param code the code for the error message
257      * @param subCode the code for the addon message
258      * @param add value(s) to use to replace the placeholer(s)
259      * @return the error message associated with the error code
260      */
261     public static String getMessage(final int code, int subCode,
262                                     final Object[] add) {
263 
264         String message = getResourceString(code);
265 
266         if (subCode != 0) {
267             message += getResourceString(subCode);
268         }
269 
270         if (add != null) {
271             message = insertStrings(message, add);
272         }
273 
274         return message;
275     }
276 
277     private static String getResourceString(int code) {
278 
279         String key = StringUtil.toZeroPaddedString(code, SQL_CODE_DIGITS,
280             SQL_CODE_DIGITS);
281         String string = ResourceBundleHandler.getString(bundleHandle, key);
282 
283         if (string == null) {
284             string = defaultMessage;
285         }
286 
287         return string;
288     }
289 
290     public static HsqlException error(final Result result) {
291         return new HsqlException(result);
292     }
293 
294     /**
295      * Used to print messages to System.out
296      *
297      *
298      * @param message message to print
299      */
300     public static void printSystemOut(String message) {
301 
302         if (TRACESYSTEMOUT) {
303             System.out.println(message);
304         }
305     }
306 
307     public static int getCode(String sqlState) {
308 
309         try {
310             Field[] fields = ErrorCode.class.getDeclaredFields();
311 
312             for (int i = 0; i < fields.length; i++) {
313                 String name = fields[i].getName();
314 
315                 if (name.length() == 7 && name.endsWith(sqlState)) {
316                     return fields[i].getInt(ErrorCode.class);
317                 }
318             }
319         } catch (IllegalAccessException e) {}
320 
321         return -1;
322     }
323 }
324