001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     * 
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     * 
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */ 
017    package org.apache.commons.betwixt.strategy;
018    
019    import java.text.ParseException;
020    import java.text.SimpleDateFormat;
021    import java.util.Locale;
022    
023    import org.apache.commons.beanutils.ConversionException;
024    import org.apache.commons.betwixt.expression.Context;
025    
026    /** 
027     * <p>Default string &lt;-&gt; object conversion strategy.</p>
028     * <p>
029     * This delegates to ConvertUtils except when the type 
030     * is assignable from <code>java.util.Date</code>
031     * but not from <code>java.sql.Date</code>.
032     * In this case, the format used is (in SimpleDateFormat terms) 
033     * <code>EEE MMM dd HH:mm:ss zzz yyyy</code>.
034     * This is the same as the output of the toString method on java.util.Date.
035     * </p>
036     * <p>
037     * This should preserve the existing symantic behaviour whilst allowing round tripping of dates
038     * (given the default settings).
039     * </p>
040     * @author Robert Burrell Donkin
041     * @since 0.5
042     */
043    public class DefaultObjectStringConverter extends ConvertUtilsObjectStringConverter {
044        
045        /** Formats Dates to Strings and Strings to Dates */
046        private final SimpleDateFormat formatter 
047            = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.UK);
048        
049        /**
050          * Converts an object to a string representation using ConvertUtils.
051          * If the object is a java.util.Date and the type is java.util.Date 
052          * but not java.sql.Date
053          * then SimpleDateFormat formatting to 
054          * <code>EEE MMM dd HH:mm:ss zzz yyyy</code>
055          * will be used. 
056          * (This is the same as java.util.Date toString would return.)
057          *
058          * @param object the object to be converted, possibly null
059          * @param type the property class of the object, not null
060          * @param flavour a string allow symantic differences in formatting 
061          * to be communicated (ignored)
062          * @param context convert against this context not null
063          * @return a String representation, not null
064          */
065        public String objectToString(Object object, Class type, String flavour, Context context) {
066            if ( object != null ) {
067                if ( object instanceof Class) {
068                    return ((Class) object).getName();
069                }
070                    
071                if ( object instanceof java.util.Date && isUtilDate( type ) ) {
072                    
073                    return formatter.format( (java.util.Date) object );
074                    
075                } else {
076                    // use ConvertUtils implementation
077                    return super.objectToString( object, type, flavour, context );
078                }
079            }
080            return "";
081        }
082        
083        /**
084          * Converts an object to a string representation using ConvertUtils.
085          * 
086          * @param value the String to be converted, not null
087          * @param type the property class to be returned (if possible), not null
088          * @param flavour a string allow symantic differences 
089          * in formatting to be communicated (ignored)
090          * @param context not null
091          * @return an Object converted from the String, not null
092          */
093        public Object stringToObject(String value, Class type, String flavour, Context context) {
094                if ( isUtilDate( type ) ) {
095                    try {
096                        
097                        return formatter.parse( value );
098                        
099                    } catch ( ParseException ex ) { 
100                        handleException( ex );
101                        // this supports any subclasses that do not which to throw exceptions
102                        // probably will result in a problem when the method will be invoked
103                        // but never mind
104                        return value;
105                    }
106                } else {
107                    // use ConvertUtils implementation
108                    return super.stringToObject( value, type, flavour, context );
109                }
110        }
111        
112        /** 
113          * Allow subclasses to use a different exception handling strategy.
114          * This class throws a <code>org.apache.commons.beanutils.ConversionException</code>
115          * when conversion fails.
116          * @param e the Exception to be handled
117          * @throws org.apache.commons.beanutils.ConversionException when conversion fails
118          */
119        protected void handleException(Exception e) {
120            throw new ConversionException( "String to object conversion failed: " + e.getMessage(), e );
121        }
122        
123        /**
124          * Is the given type a java.util.Date but not a java.sql.Date?
125          * @param type test this class type
126          * @return true is this is a until date but not a sql one
127          */
128        private boolean isUtilDate(Class type) {
129            return ( java.util.Date.class.isAssignableFrom(type) 
130                 && !java.sql.Date.class.isAssignableFrom(type)
131                 && !java.sql.Time.class.isAssignableFrom(type) 
132                 && !java.sql.Timestamp.class.isAssignableFrom(type) );
133        }
134    }