001    /*
002     @license.text@ 
003     */
004    package biz.hammurapi.sql;
005    
006    import java.io.Serializable;
007    import java.lang.reflect.InvocationHandler;
008    import java.lang.reflect.Method;
009    import java.lang.reflect.Proxy;
010    import java.sql.ResultSet;
011    import java.sql.SQLException;
012    import java.util.ArrayList;
013    import java.util.Collections;
014    import java.util.HashMap;
015    import java.util.Iterator;
016    import java.util.List;
017    import java.util.Map;
018    import java.util.Map.Entry;
019    
020    import org.w3c.dom.Element;
021    
022    import biz.hammurapi.convert.CompositeConverter;
023    
024    /**
025     * @author Pavel Vlasov
026     * @version $Revision: 1.5 $
027     */
028    public class InterfaceProjector extends BaseReflectionProjector implements Projector {
029            private Class theInterface;
030            private Object delegate;
031    
032            /**
033             * @param typeMap
034             * @param theInterface Iterface projected object should implement.
035             */
036            public InterfaceProjector(Class theInterface, Map typeMap) {
037                    super(typeMap);
038                    this.theInterface=theInterface;
039            }
040    
041            /**
042             * @param typeMap
043             * @param delegate Object to delegate unmatched calls to.
044             * @param theInterface Iterface projected object should implement.
045             */
046            public InterfaceProjector(Class theInterface, Object delegate, Map typeMap) {
047                    super(typeMap);
048                    this.theInterface=theInterface;
049                    this.delegate=delegate;
050            }
051            
052            private static Map primitivesMap=new HashMap();
053            
054            static {
055                    primitivesMap.put(int.class, java.lang.Integer.class);
056                    primitivesMap.put(boolean.class, java.lang.Boolean.class);
057                    primitivesMap.put(byte.class, java.lang.Byte.class);
058                    primitivesMap.put(char.class, java.lang.Character.class);
059                    primitivesMap.put(double.class, java.lang.Double.class);
060                    primitivesMap.put(float.class, java.lang.Float.class);
061                    primitivesMap.put(long.class, java.lang.Long.class);
062                    primitivesMap.put(short.class, java.lang.Short.class);
063            }
064            
065            class ResultSetInvocationHandler implements InvocationHandler, Serializable {
066                    /**
067                     * Comment for <code>serialVersionUID</code>
068                     */
069                    private static final long serialVersionUID = -971624963614852201L;
070                    Map fieldMap;
071                    
072                    public ResultSetInvocationHandler(Map fieldMap) {
073                            this.fieldMap=fieldMap;
074                    }
075                    
076                    
077                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
078                            if (method.getName().length()>3) {
079                                    String key = method.getName().substring(3);
080                                    if (method.getName().startsWith("get") && method.getParameterTypes().length==0 && fieldMap.containsKey(key)) {
081                                            Class prt=(Class) primitivesMap.get(method.getReturnType());                                                                            
082                                            Object value = fieldMap.get(key);
083                                            if (value==null) {
084                                                    if (prt==null) {
085                                                            return null;
086                                                    }
087                                                    
088                                                    throw new IllegalArgumentException("Cannot convert 'null' to "+method.getReturnType());
089                                            }
090                                            
091                                            Object ret = CompositeConverter.getDefaultConverter().convert(value, method.getReturnType(), false);
092                                            if (ret==null && prt!=null) {
093                                                    ret = CompositeConverter.getDefaultConverter().convert(value, prt, false);
094                                            }
095                                            
096                                            if (ret==null) {
097                                                    throw new IllegalArgumentException(
098                                                                    "Cannot convert " + 
099                                                                    (value==null ? "null" : value.getClass()+" '"+value+"'") + 
100                                                                    " to " + 
101                                                                    method.getReturnType().getName());
102                                            }
103                                            
104                                            return ret;
105                                    } else if (method.getName().startsWith("set") && method.getParameterTypes().length==1 && fieldMap.containsKey(key)) {
106                                            fieldMap.put(key, args[0]);
107                                            return null;
108                                    }
109                            }
110                            
111                            if ("toString".equals(method.getName()) && method.getParameterTypes().length==0) {
112                                    StringBuffer ret=new StringBuffer(theInterface.getName());
113                                    ret.append(" [");
114                                    List fields=new ArrayList(fieldMap.keySet());
115                                    Collections.sort(fields);
116                                    Iterator it=fields.iterator();
117                                    while (it.hasNext()) {
118                                            ret.append(" ");
119                                            String key=(String) it.next();
120                                            ret.append(key);
121                                            ret.append(" = ");
122                                            ret.append(fieldMap.get(key));
123                                            if (it.hasNext()) {
124                                                    ret.append(";");
125                                            }
126                                    }
127                                    return ret.append(" ]").toString();
128                            }
129                            
130                            if ("toDom".equals(method.getName()) && method.getParameterTypes().length==1 && Element.class.isAssignableFrom(method.getParameterTypes()[0])) {
131                                    Element holder=(Element) args[0];
132                                    Iterator it=fieldMap.entrySet().iterator();
133                                    while (it.hasNext()) {
134                                            Map.Entry entry=(Entry) it.next();
135                                            Element fieldElement=holder.getOwnerDocument().createElement((String) entry.getKey());
136                                            holder.appendChild(fieldElement);
137                                            if (entry.getValue()!=null) {
138                                                    fieldElement.setAttribute("type", entry.getValue().getClass().getName());
139                                                    fieldElement.appendChild(fieldElement.getOwnerDocument().createTextNode(entry.getValue().toString()));
140                                            }
141                                    }                               
142                            }
143                            
144                            return (delegate==null ? fieldMap : delegate).getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(delegate, args);
145                    }
146                    
147            }       
148    
149            public Object project(ResultSet rs) throws SQLException {
150                    final Map fieldMap=new HashMap();
151                    for (int i=1, c=rs.getMetaData().getColumnCount(); i<=c; i++) {
152                            fieldMap.put(accessorName(rs.getMetaData().getColumnName(i)).substring(3), rs.getObject(i));
153                    }
154                                    
155                    return Proxy.newProxyInstance(theInterface.getClassLoader(), new Class[] {theInterface}, new ResultSetInvocationHandler(fieldMap));
156            }
157    }