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    
034    /**
035     * Utility class allowing to get various kinds of binders. The {@link DefaultBinderConfiguration} uses binders
036     * returned by this class, based on the type of the parameter.
037     * @author JB Nizet
038     */
039    public final class Binders {
040    
041        private static final Binder DEFAULT_BINDER = new DefaultBinder();
042        private static final Binder DATE_BINDER = new DateBinder();
043        private static final Binder TIMESTAMP_BINDER = new TimestampBinder();
044        private static final Binder DECIMAL_BINDER = new DecimalBinder();
045        private static final Binder INTEGER_BINDER = new IntegerBinder();
046        private static final Binder TIME_BINDER = new TimeBinder();
047        private static final Binder STRING_BINDER = new StringBinder();
048    
049        private Binders() {
050        }
051    
052        /**
053         * Returns the default binder, which uses <code>stmt.setObject()</code> to bind the parameter.
054         */
055        public static Binder defaultBinder() {
056            return DEFAULT_BINDER;
057        }
058    
059        /**
060         * Returns a binder suitable for columns of type CHAR and VARCHAR. The returned binder supports values of type
061         * <ul>
062         *   <li><code>String</code></li>
063         *   <li><code>enum</code>: the name of the enum is used as bound value</li>
064         *   <li><code>Object</code>: the <code>toString()</code> of the object is used as bound value</li>
065         * </ul>
066         */
067        public static Binder stringBinder() {
068            return STRING_BINDER;
069        }
070    
071        /**
072         * Returns a binder suitable for columns of type DATE. The returned binder supports values of type
073         * <ul>
074         *   <li><code>java.sql.Date</code></li>
075         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Date</li>
076         *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a java.sql.Date
077         *   </li>
078         *   <li><code>String</code>: the string is transformed to a java.sql.Date using the <code>Date.valueOf()</code>
079         *       method</li>
080         * </ul>
081         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
082         */
083        public static Binder dateBinder() {
084            return DATE_BINDER;
085        }
086    
087        /**
088         * Returns a binder suitable for columns of type TIMESTAMP. The returned binder supports values of type
089         * <ul>
090         *   <li><code>java.sql.Timestamp</code></li>
091         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a java.sql.Timestamp</li>
092         *   <li><code>java.util.Calendar: the milliseconds of the calendar are used to construct a
093         *   java.sql.Timestamp</code></li>
094         *   <li><code>String</code>: the string is transformed to a <code>java.sql.Timestamp</code> using the
095         *       <code>Timestamp.valueOf()</code> method, or using the <code>java.sql.Date.valueOf()</code> method if the
096         *       string has less than 19 characters</li>
097         * </ul>
098         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
099         */
100        public static Binder timestampBinder() {
101            return TIMESTAMP_BINDER;
102        }
103    
104        /**
105         * Returns a binder suitable for columns of type TIME. The returned binder supports values of type
106         * <ul>
107         *   <li><code>java.sql.Time</code></li>
108         *   <li><code>java.util.Date</code>: the milliseconds of the date are used to construct a
109         *      <code>java.sql.Time</code></li>
110         *   <li><code>java.util.Calendar</code>: the milliseconds of the calendar are used to construct a
111         *      <code>java.sql.Time</code>
112         *   </li>
113         *   <li><code>String</code>: the string is transformed to a java.sql.Time using the
114         *       <code>Time.valueOf()</code> method</li>
115         * </ul>
116         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
117         */
118        public static Binder timeBinder() {
119            return TIME_BINDER;
120        }
121    
122        /**
123         * Returns a binder suitable for numeric, decimal columns. The returned binder supports values of type
124         * <ul>
125         *   <li><code>String</code>: the string is transformed to a java.math.BigDecimal using its constructor</li>
126         * </ul>
127         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
128         */
129        public static Binder decimalBinder() {
130            return DECIMAL_BINDER;
131        }
132    
133        /**
134         * Returns a binder suitable for numeric, integer columns. The returned binder supports values of type
135         * <ul>
136         *   <li><code>enum</code>: the enum is transformed into an integer by taking its ordinal</li>
137         *   <li><code>String</code>: the string is transformed to a <code>java.math.BigInteger</code> using its
138         *      constructor</li>
139         * </ul>
140         * If the value is none of these types, <code>stmt.setObject()</code> is used to bind the value.
141         */
142        public static Binder integerBinder() {
143            return INTEGER_BINDER;
144        }
145    
146        /**
147         * The implementation for {@link Binders#stringBinder()}
148         * @author JB Nizet
149         */
150        private static final class StringBinder implements Binder {
151            @Override
152            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
153                if (value instanceof String) {
154                    stmt.setString(param, (String) value);
155                }
156                else if (value instanceof Enum<?>) {
157                    stmt.setString(param, ((Enum<?>) value).name());
158                }
159                else if (value == null) {
160                    stmt.setObject(param, null);
161                }
162                else {
163                    stmt.setString(param, value.toString());
164                }
165            }
166    
167            @Override
168            public String toString() {
169                return "Binders.stringBinder";
170            }
171        }
172    
173        /**
174         * The implementation for {@link Binders#timeBinder()}
175         * @author JB Nizet
176         */
177        private static final class TimeBinder implements Binder {
178            @Override
179            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
180                if (value instanceof Time) {
181                    stmt.setTime(param, (Time) value);
182                }
183                else if (value instanceof java.util.Date) {
184                    stmt.setTime(param, new Time(((java.util.Date) value).getTime()));
185                }
186                else if (value instanceof java.util.Calendar) {
187                    stmt.setTime(param, new Time(((java.util.Calendar) value).getTimeInMillis()));
188                }
189                else if (value instanceof String) {
190                    stmt.setTime(param, Time.valueOf((String) value));
191                }
192                else {
193                    stmt.setObject(param, value);
194                }
195            }
196    
197            @Override
198            public String toString() {
199                return "Binders.timeBinder";
200            }
201        }
202    
203        /**
204         * The implementation for {@link Binders#integerBinder()}
205         * @author JB Nizet
206         */
207        private static final class IntegerBinder implements Binder {
208            @Override
209            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
210                if (value instanceof BigInteger) {
211                    stmt.setObject(param, value, Types.BIGINT);
212                }
213                else if (value instanceof Enum<?>) {
214                    stmt.setInt(param, ((Enum<?>) value).ordinal());
215                }
216                else if (value instanceof String) {
217                    stmt.setObject(param, new BigInteger((String) value), Types.BIGINT);
218                }
219                else {
220                    stmt.setObject(param, value);
221                }
222            }
223    
224            @Override
225            public String toString() {
226                return "Binders.integerBinder";
227            }
228        }
229    
230        /**
231         * The implementation for {@link Binders#decimalBinder()}
232         * @author JB Nizet
233         */
234        private static final class DecimalBinder implements Binder {
235            @Override
236            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
237                if (value instanceof String) {
238                    stmt.setBigDecimal(param, new BigDecimal((String) value));
239                }
240                else {
241                    stmt.setObject(param, value);
242                }
243            }
244    
245            @Override
246            public String toString() {
247                return "Binders.decimalBinder";
248            }
249        }
250    
251        /**
252         * The implementation for {@link Binders#timestampBinder()}
253         * @author JB Nizet
254         */
255        private static final class TimestampBinder implements Binder {
256            // the number of chars in yyyy-mm-dd hh:mm:ss
257            private static final int MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP = 19;
258    
259            @Override
260            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
261                if (value instanceof Timestamp) {
262                    stmt.setTimestamp(param, (Timestamp) value);
263                }
264                else if (value instanceof java.util.Date) {
265                    stmt.setTimestamp(param, new Timestamp(((java.util.Date) value).getTime()));
266                }
267                else if (value instanceof java.util.Calendar) {
268                    stmt.setTimestamp(param, new Timestamp(((java.util.Calendar) value).getTimeInMillis()));
269                }
270                else if (value instanceof String) {
271                    String valueAsString = (String) value;
272                    if (valueAsString.length() >= MIN_NUMBER_OF_CHARS_FOR_TIMESTAMP) {
273                        stmt.setTimestamp(param, Timestamp.valueOf(valueAsString));
274                    }
275                    else {
276                        Date valueAsDate = Date.valueOf(valueAsString);
277                        stmt.setTimestamp(param, new Timestamp(valueAsDate.getTime()));
278                    }
279                }
280                else {
281                    stmt.setObject(param, value);
282                }
283            }
284    
285            @Override
286            public String toString() {
287                return "Binders.timestampBinder";
288            }
289        }
290    
291        /**
292         * The implementation for {@link Binders#dateBinder()}
293         * @author JB Nizet
294         */
295        private static final class DateBinder implements Binder {
296            @Override
297            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
298                if (value instanceof Date) {
299                    stmt.setDate(param, (Date) value);
300                }
301                else if (value instanceof java.util.Date) {
302                    stmt.setDate(param, new Date(((java.util.Date) value).getTime()));
303                }
304                else if (value instanceof java.util.Calendar) {
305                    stmt.setDate(param, new Date(((java.util.Calendar) value).getTimeInMillis()));
306                }
307                else if (value instanceof String) {
308                    stmt.setDate(param, Date.valueOf((String) value));
309                }
310                else {
311                    stmt.setObject(param, value);
312                }
313            }
314    
315            @Override
316            public String toString() {
317                return "Binders.dateBinder";
318            }
319        }
320    
321        /**
322         * The implementation for {@link Binders#defaultBinder()}
323         * @author JB Nizet
324         */
325        private static final class DefaultBinder implements Binder {
326            @Override
327            public void bind(java.sql.PreparedStatement stmt, int param, Object value) throws java.sql.SQLException {
328                stmt.setObject(param, value);
329            }
330    
331            @Override
332            public String toString() {
333                return "Binders.defaultBinder";
334            }
335        }
336    }