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.io.Serializable;
020    import java.util.Date;
021    
022    /**
023     * Determines the way that a type (of object) should be bound
024     * by Betwixt.
025     * 
026     * @author <a href='http://commons.apache.org'>Apache Commons Team</a>, <a href='http://www.apache.org'>Apache Software Foundation</a>
027     */
028    public abstract class TypeBindingStrategy {
029        
030        /**
031         * The default Betwixt <code>TypeBindingStrategy</code> implementation.
032         * Since the default implementation has no state, 
033         * a singleton instance can be provided.
034         */
035        public static final TypeBindingStrategy DEFAULT = new Default();
036        
037        /**
038         * Gets the binding type to be used for the given Java type.
039         * @param type <code>Class</code> for which the binding type is to be determined,
040         * not null
041         * @return <code>BindingType</code> enumeration indicating the type of binding,
042         * not null
043         */
044        public abstract BindingType bindingType(Class type);
045        
046    
047        /**
048         * Enumerates the possible general ways that Betwixt can map a Java type to an XML type.
049         * @author <a href='http://commons.apache.org'>Apache Commons Team</a>, <a href='http://www.apache.org'>Apache Software Foundation</a>
050         */
051        public static final class BindingType implements Serializable {
052            
053            private static final int COMPLEX_INDICATOR = 1;
054            private static final int PRIMITIVE_INDICATOR = 2;
055            
056            /**
057             * Indicates that the java type should be bound to a complex xml type.
058             * A complex xml type may have child elements and attributes.
059             * Betwixt determines the mapping for a java bean bound to a complex type.  
060             */
061            public static final BindingType COMPLEX = new BindingType(COMPLEX_INDICATOR);
062            
063            /**
064             * Indicates that the type should be bound as a Java primitive.
065             * Betwixt may bind this to an attribute or a simple xml type.
066             * Which is determined by the configuration for binding primitives.
067             */
068            public static final BindingType PRIMITIVE = new BindingType(PRIMITIVE_INDICATOR);
069            
070            private int type;
071            
072            private BindingType(int type) {
073                this.type = type;
074            }
075            
076            
077            /**
078             * @see java.lang.Object#equals(java.lang.Object)
079             */
080            public boolean equals(Object object) {
081                boolean result = false;
082                if (object instanceof BindingType) {
083                    BindingType bindingType = (BindingType) object;
084                    result = (type == bindingType.type);
085                }
086                return result;
087            }
088            
089            /**
090             * @see java.lang.Object#hashCode()
091             */
092            public int hashCode() {
093                return type;
094            }
095            
096            /**
097             * @see java.lang.Object#toString()
098             */
099            public String toString() {
100                StringBuffer buffer = new StringBuffer();
101                buffer.append("BindingType: ");
102                switch (type) {
103                            case (COMPLEX_INDICATOR):
104                                    buffer.append("COMPLEX");
105                                            break;
106                
107                            case (PRIMITIVE_INDICATOR):
108                                buffer.append("PRIMITIVE");
109                                break;
110                }
111                
112                return buffer.toString();
113            }
114    }
115        
116        /**
117         * The default <code>TypeBindingStrategy</code> used by Betwixt.
118         * This implementation recognizes all the usual Java primitive wrappers 
119         * (plus a few more that will in most typical use cases be regarded in the same way). 
120         * @author <a href='http://commons.apache.org'>Apache Commons Team</a>, <a href='http://www.apache.org'>Apache Software Foundation</a>
121         */
122        public static final class Default extends TypeBindingStrategy {
123    
124            /**
125             * Class who are simple and whose subclass are also simple
126             */
127            private static final Class[] INHERITED_SIMPLE = {
128                                                            Number.class, 
129                                                            String.class,
130                                                            Date.class,
131                                                            java.sql.Date.class,
132                                                            java.sql.Time.class,
133                                                            java.sql.Timestamp.class,
134                                                            java.math.BigDecimal.class,
135                                                            java.math.BigInteger.class};
136                                                            
137            /**
138             * Classes who are complex and whose subclasses are also complex
139             */
140            private static final Class[] INHERITED_COMPLEX = {
141                                                                                                    Throwable.class
142                                                                            };
143    
144            /**
145             * Gets the binding type to be used for the given Java type.
146             * This implementation recognizes all the usual Java primitive wrappers 
147             * (plus a few more that will in most typical use cases be regarded in the same way). 
148             * @param type <code>Class</code> for which the binding type is to be determined,
149             * not null
150             * @return <code>BindingType</code> enumeration indicating the type of binding,
151             * not null
152             */
153            public BindingType bindingType(Class type) {
154                BindingType result =  BindingType.COMPLEX;
155                if (isStandardPrimitive(type)) {
156                    result = BindingType.PRIMITIVE;
157                }
158           
159                return result;
160            }
161            
162            /**
163             * is the given type one of the standard Betwixt primitives?
164             * @param type <code>Class</code>, not null
165             * @return true if the type is one of the standard Betwixt primitives
166             */
167            protected boolean isStandardPrimitive(Class type) {
168                if ( type == null ) {
169                    return false;
170                    
171                } else if ( type.isPrimitive() ) {
172                    return true;
173                    
174                } else if ( type.equals( Object.class ) ) {
175                    return false;
176                }
177                for ( int i=0, size=INHERITED_SIMPLE.length; i<size; i++ ) {
178                    if ( INHERITED_SIMPLE[i].equals( type ) ) {
179                        return true;
180                    }
181                }
182        
183                for ( int i=0, size=INHERITED_COMPLEX.length; i<size; i++ ) {
184                    if ( INHERITED_COMPLEX[i].equals( type ) ) {
185                        return false;
186                    }
187                }
188                
189                for ( int i=0, size=INHERITED_COMPLEX.length; i<size; i++ ) {
190                    if ( INHERITED_COMPLEX[i].isAssignableFrom( type ) ) {
191                        return false;
192                    }
193                }     
194                
195                if (type.getName().startsWith( "java.lang." )) {
196                    return true;
197                }
198                
199                for ( int i=0, size=INHERITED_SIMPLE.length; i<size; i++ ) {
200                    if ( INHERITED_SIMPLE[i].isAssignableFrom( type ) ) {
201                        return true;
202                    }
203                }            
204                return false;
205            }
206        }
207    }