001    /*
002     * The MIT License
003     *
004     * Copyright (c) 2012, Ninja Squad
005     *
006     * Permission is hereby granted, free of charge, to any person obtaining a copy
007     * of this software and associated documentation files (the "Software"), to deal
008     * in the Software without restriction, including without limitation the rights
009     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010     * copies of the Software, and to permit persons to whom the Software is
011     * furnished to do so, subject to the following conditions:
012     *
013     * The above copyright notice and this permission notice shall be included in
014     * all copies or substantial portions of the Software.
015     *
016     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022     * THE SOFTWARE.
023     */
024    
025    package com.ninja_squad.dbsetup.bind;
026    
027    import java.math.BigDecimal;
028    import java.math.BigInteger;
029    import java.sql.Date;
030    import java.sql.Time;
031    import java.sql.Timestamp;
032    import java.sql.Types;
033    import java.util.Calendar;
034    
035    /**
036     * Utility class allowing to get various kinds of binders. The {@link DefaultBinderConfiguration} uses binders
037     * returned by this class, based on the type of the parameter.
038     * @author JB Nizet
039     */
040    public final class Binders {
041    
042        private static final Binder DEFAULT_BINDER = new DefaultBinder();
043        private static final Binder DATE_BINDER = new DateBinder();
044        private static final Binder TIMESTAMP_BINDER = new TimestampBinder();
045        private static final Binder DECIMAL_BINDER = new DecimalBinder();
046        private static final Binder INTEGER_BINDER = new IntegerBinder();
047        private static final Binder TIME_BINDER = new TimeBinder();
048        private static final Binder STRING_BINDER = new StringBinder();
049    
050        private Binders() {
051        }
052    
053        /**
054         * Returns the default binder. This binder is normally used for columns of a type that is not handled by the other
055         * binders. It is also used when the metadata are not used and the Insert thus doesn't know the type of the column.
056         * It simply uses <code>stmt.setObject()</code> to bind the parameter, except if the value being bound is of some
057         * some well-known type not handled by JDBC:
058         * <ul>
059         *     <li><code>enum</code>: the name of the enum is bound</li>
060         *     <li><code>java.util.Date</code>: the date is transformed to a java.sql.Timestamp</li>
061         *     <li><code>java.util.Calendar</code>: the date is transformed to a java.sql.Timestamp</li>
062         * </ul>
063         */
064        public static Binder defaultBinder() {
065            return DEFAULT_BINDER;
066        }
067    
068        /**
069         * Returns a binder suitable for columns of type CHAR and VARCHAR. The returned binder supports values of type
070         * <ul>
071         *   <li><code>String</code></li>
072         *   <li><code>enum</code>: the name of the enum is used as bound value</li>
073         *   <li><code>Object</code>: the <code>toString()</code> of the object is used as bound value</li>
074         * </ul>
075         */
076        public static Binder stringBinder() {
077            return STRING_BINDER;
078        }
079    
080        /**
081         * Returns a binder suitable for columns of type DATE. The returned binder supports values of type
082         * <ul>
083         *   <li><code>java.sql.Date</code></li>
084         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Date</li>
085         *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a java.sql.Date
086         *   </li>
087         *   <li><code>String</code>: the string is transformed to a java.sql.Date using the <code>Date.valueOf()</code>
088         *       method</li>
089         * </ul>
090         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
091         */
092        public static Binder dateBinder() {
093            return DATE_BINDER;
094        }
095    
096        /**
097         * Returns a binder suitable for columns of type TIMESTAMP. The returned binder supports values of type
098         * <ul>
099         *   <li><code>java.sql.Timestamp</code></li>
100         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Timestamp</li>
101         *   <li><code>java.util.Calendar: the milliseconds of the calendar are used to construct a
102         *   java.sql.Timestamp</code></li>
103         *   <li><code>String</code>: the string is transformed to a <code>java.sql.Timestamp</code> using the
104         *       <code>Timestamp.valueOf()</code> method, or using the <code>java.sql.Date.valueOf()</code> method if the
105         *       string has less than 19 characters</li>
106         * </ul>
107         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
108         */
109        public static Binder timestampBinder() {
110            return TIMESTAMP_BINDER;
111        }
112    
113        /**
114         * Returns a binder suitable for columns of type TIME. The returned binder supports values of type
115         * <ul>
116         *   <li><code>java.sql.Time</code></li>
117         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a
118         *      <code>java.sql.Time</code></li>
119         *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a
120         *      <code>java.sql.Time</code>
121         *   </li>
122         *   <li><code>String</code>: the string is transformed to a java.sql.Time using the
123         *       <code>Time.valueOf()</code> method</li>
124         * </ul>
125         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
126         */
127        public static Binder timeBinder() {
128            return TIME_BINDER;
129        }
130    
131        /**
132         * Returns a binder suitable for numeric, decimal columns. The returned binder supports values of type
133         * <ul>
134         *   <li><code>String</code>: the string is transformed to a java.math.BigDecimal using its constructor</li>
135         * </ul>
136         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
137         */
138        public static Binder decimalBinder() {
139            return DECIMAL_BINDER;
140        }
141    
142        /**
143         * Returns a binder suitable for numeric, integer columns. The returned binder supports values of type
144         * <ul>
145         *   <li><code>enum</code>: the enum is transformed into an integer by taking its ordinal</li>
146         *   <li><code>String</code>: the string is transformed to a <code>java.math.BigInteger</code> using its
147         *      constructor</li>
148         * </ul>
149         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
150         */
151        public static Binder integerBinder() {
152            return INTEGER_BINDER;
153        }
154    
155        /**
156         * The implementation for {@link Binders#stringBinder()}
157         * @author JB Nizet
158         */
159        private static final class StringBinder implements Binder {
160            @Override
161            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
162                if (value instanceof String) {
163                    stmt.setString(param, (String) value);
164                }
165                else if (value instanceof Enum<?>) {
166                    stmt.setString(param, ((Enum<?>) value).name());
167                }
168                else if (value == null) {
169                    stmt.setObject(param, null);
170                }
171                else {
172                    stmt.setString(param, value.toString());
173                }
174            }
175    
176            @Override
177            public String toString() {
178                return "Binders.stringBinder";
179            }
180        }
181    
182        /**
183         * The implementation for {@link Binders#timeBinder()}
184         * @author JB Nizet
185         */
186        private static final class TimeBinder implements Binder {
187            @Override
188            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
189                if (value instanceof Time) {
190                    stmt.setTime(param, (Time) value);
191                }
192                else if (value instanceof java.util.Date) {
193                    stmt.setTime(param, new Time(((java.util.Date) value).getTime()));
194                }
195                else if (value instanceof java.util.Calendar) {
196                    stmt.setTime(param, new Time(((java.util.Calendar) value).getTimeInMillis()));
197                }
198                else if (value instanceof String) {
199                    stmt.setTime(param, Time.valueOf((String) value));
200                }
201                else {
202                    stmt.setObject(param, value);
203                }
204            }
205    
206            @Override
207            public String toString() {
208                return "Binders.timeBinder";
209            }
210        }
211    
212        /**
213         * The implementation for {@link Binders#integerBinder()}
214         * @author JB Nizet
215         */
216        private static final class IntegerBinder implements Binder {
217            @Override
218            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
219                if (value instanceof BigInteger) {
220                    stmt.setObject(param, value, Types.BIGINT);
221                }
222                else if (value instanceof Enum<?>) {
223                    stmt.setInt(param, ((Enum<?>) value).ordinal());
224                }
225                else if (value instanceof String) {
226                    stmt.setObject(param, new BigInteger((String) value), Types.BIGINT);
227                }
228                else {
229                    stmt.setObject(param, value);
230                }
231            }
232    
233            @Override
234            public String toString() {
235                return "Binders.integerBinder";
236            }
237        }
238    
239        /**
240         * The implementation for {@link Binders#decimalBinder()}
241         * @author JB Nizet
242         */
243        private static final class DecimalBinder implements Binder {
244            @Override
245            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
246                if (value instanceof String) {
247                    stmt.setBigDecimal(param, new BigDecimal((String) value));
248                }
249                else {
250                    stmt.setObject(param, value);
251                }
252            }
253    
254            @Override
255            public String toString() {
256                return "Binders.decimalBinder";
257            }
258        }
259    
260        /**
261         * The implementation for {@link Binders#timestampBinder()}
262         * @author JB Nizet
263         */
264        private static final class TimestampBinder implements Binder {
265            // the number of chars in yyyy-mm-dd hh:mm:ss
266            private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
267    
268            @Override
269            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
270                if (value instanceof Timestamp) {
271                    stmt.setTimestamp(param, (Timestamp) value);
272                }
273                else if (value instanceof java.util.Date) {
274                    stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));
275                }
276                else if (value instanceof java.util.Calendar) {
277                    stmt.setTimestamp(param, new Timestamp(((java.util.Calendar) value).getTimeInMillis()));
278                }
279                else if (value instanceof String) {
280                    String valueAsString = (String) value;
281                    if (valueAsString.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
282                        stmt.setTimestamp(param, Timestamp.valueOf(valueAsString));
283                    }
284                    else {
285                        Date valueAsDate = Date.valueOf(valueAsString);
286                        stmt.setTimestamp(param, new Timestamp(valueAsDate.getTime()));
287                    }
288                }
289                else {
290                    stmt.setObject(param, value);
291                }
292            }
293    
294            @Override
295            public String toString() {
296                return "Binders.timestampBinder";
297            }
298        }
299    
300        /**
301         * The implementation for {@link Binders#dateBinder()}
302         * @author JB Nizet
303         */
304        private static final class DateBinder implements Binder {
305            @Override
306            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
307                if (value instanceof Date) {
308                    stmt.setDate(param, (Date) value);
309                }
310                else if (value instanceof java.util.Date) {
311                    stmt.setDate(param, new Date(((java.util.Date) value).getTime()));
312                }
313                else if (value instanceof java.util.Calendar) {
314                    stmt.setDate(param, new Date(((java.util.Calendar) value).getTimeInMillis()));
315                }
316                else if (value instanceof String) {
317                    stmt.setDate(param, Date.valueOf((String) value));
318                }
319                else {
320                    stmt.setObject(param, value);
321                }
322            }
323    
324            @Override
325            public String toString() {
326                return "Binders.dateBinder";
327            }
328        }
329    
330        /**
331         * The implementation for {@link Binders#defaultBinder()}
332         * @author JB Nizet
333         */
334        private static final class DefaultBinder implements Binder {
335            @Override
336            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
337                Object boundValue;
338                if (value instanceof Enum) {
339                    boundValue = ((Enum) value).name();
340                }
341                else if (value instanceof java.util.Date) {
342                    boundValue = new Timestamp(((java.util.Date) value).getTime());
343                }
344                else if (value instanceof Calendar) {
345                    boundValue = new Timestamp(((Calendar) value).getTime().getTime());
346                }
347                else {
348                    boundValue = value;
349                }
350                stmt.setObject(param, boundValue);
351            }
352    
353            @Override
354            public String toString() {
355                return "Binders.defaultBinder";
356            }
357        }
358    }