001package gu.sql2java; 002 003import java.io.PrintWriter; 004import java.io.StringWriter; 005import java.lang.reflect.Method; 006import java.nio.ByteBuffer; 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Date; 010import java.util.Hashtable; 011import java.util.List; 012import java.util.Map; 013 014import com.google.common.primitives.Primitives; 015 016/** 017 * implementation of {@link IBeanConverter} by reflect<br> 018 * generic type converter between L and R <br> 019 * @author guyadong 020 * @param <L> left type extends BaseBean 021 * @param <R> right type 022 * 023 */ 024public class BeanConverter<L extends BaseBean,R> extends IBeanConverter.AbstractHandle<L,R> implements Constant{ 025 static final String GET_INITIALIZED = "getInitialized"; 026 static final String SET_INITIALIZED = "setInitialized"; 027 static final String GET_MODIFIED = "getModified"; 028 static final String SET_MODIFIED = "setModified"; 029 static final String SET_NEW = "setNew"; 030 static final String IS_NEW = "isNew"; 031 private final Map<String,Method> methods = new Hashtable<String,Method>(); 032 private final Map<String,Integer> rightIndexs = new Hashtable<String,Integer>(); 033 private final Map<String, Class<?>> setterParams = new Hashtable<String,Class<?>>(); 034 private final RowMetaData metaData; 035 private boolean bitCheck(String name,int...bits){ 036 Integer id = rightIndexs.get(name); 037 return (null == id)?false:bitCheck(id.intValue(),bits); 038 } 039 private int[] bitOR(String name,int... bits){ 040 return bitOR(rightIndexs.get(name),bits); 041 } 042 private void getGetter(String name){ 043 try{ 044 methods.put(name,rightType.getMethod(name)); 045 }catch(NoSuchMethodException e){} 046 } 047 private void getSetter(String name, Class<?>...types) throws NoSuchMethodException{ 048 for(Class<?>paramType:types){ 049 try{ 050 methods.put(name,rightType.getMethod(name,paramType)); 051 setterParams.put(name, paramType); 052 return; 053 }catch(NoSuchMethodException e){ 054 continue; 055 } 056 } 057 throw new NoSuchMethodException(); 058 } 059 private void getSetterNoThrow(String name, Class<?>...types){ 060 try{ 061 getSetter(name,types); 062 }catch(NoSuchMethodException e){} 063 } 064 /** 065 * constructor 066 * @param leftClass 067 * @param rightClass 068 * @param javaFields a comma splice string,including all field name of R,<br> 069 * if null or empty, use default string:{@link RowMetaData#columnJavaNames} 070 */ 071 public BeanConverter (Class<L> leftClass, Class<R> rightClass,String javaFields){ 072 super(leftClass,rightClass); 073 this.metaData = RowMetaData.getMetaData(leftClass); 074 init(javaFields); 075 } 076 /** @see #BeanConverter(Class,Class,String) */ 077 public BeanConverter (Class<L> leftClass, Class<R> rightClass){ 078 this(leftClass,rightClass,null); 079 } 080 private void init(String javaFields){ 081 List<String>rightFields; 082 if(null == javaFields || javaFields.isEmpty()){ 083 rightFields = metaData.columnJavaNames; 084 }else{ 085 rightFields = Arrays.asList(javaFields.split(",")); 086 } 087 for(int i = 0 ; i < rightFields.size(); ++i){ 088 String field = rightFields.get(i).trim(); 089 if(!field.matches("\\w+")){ 090 throw new IllegalArgumentException("invalid 'javaFields':" + javaFields); 091 } 092 rightIndexs.put(field,i); 093 } 094 try{ 095 methods.put(IS_NEW,rightType.getMethod(IS_NEW)); 096 methods.put(GET_INITIALIZED,rightType.getMethod(GET_INITIALIZED)); 097 getSetter(SET_NEW,boolean.class); 098 if(rightIndexs.size() > STATE_BIT_NUM){ 099 getSetter(SET_INITIALIZED,int[].class,List.class); 100 }else{ 101 getSetter(SET_INITIALIZED,int.class); 102 } 103 getGetter(GET_MODIFIED); 104 if(rightIndexs.size() > STATE_BIT_NUM){ 105 getSetter(SET_MODIFIED,int[].class,List.class); 106 }else{ 107 getSetter(SET_MODIFIED,int.class); 108 } 109 }catch(NoSuchMethodException e){ 110 throw new RuntimeException(e); 111 } 112 113 for(int i =0; i<metaData.columnCount; ++i){ 114 getGetter(metaData.getterMethods.get(i).getName()); 115 Method setter = metaData.setterMethods.get(i); 116 Class<?> type = setter.getParameterTypes()[0]; 117 if(type.isPrimitive()){ 118 if(Date.class.isAssignableFrom(type)){ 119 getSetterNoThrow(setter.getName(),type,long.class); 120 } 121 else{ 122 getSetterNoThrow(setter.getName(),type,Primitives.unwrap(type)); 123 } 124 }else if(ByteBuffer.class.isAssignableFrom(type) || byte[].class.isAssignableFrom(type)){ 125 getSetterNoThrow(setter.getName(),type,ByteBuffer.class,byte[].class); 126 } else { 127 getSetterNoThrow(setter.getName(),type); 128 } 129 } 130 } 131 @Override 132 protected void doFromRight(L left, R right) { 133 try{ 134 Method getterMethod; 135 left.resetIsModified(); 136 int selfModified = 0; 137 int[] initialized; 138 int[] modified; 139 if(rightIndexs.size() > STATE_BIT_NUM){ 140 initialized = (int[])methods.get(GET_INITIALIZED).invoke(right); 141 modified = (int[])methods.get(GET_MODIFIED).invoke(right); 142 }else{ 143 initialized = new int[]{(Integer)methods.get(GET_INITIALIZED).invoke(right)}; 144 modified = new int[]{(Integer)methods.get(GET_MODIFIED).invoke(right)}; 145 } 146 147 for(int columnId = 0; columnId < metaData.columnCount; ++columnId){ 148 String columnName = metaData.columnNameOf(columnId); 149 if(bitCheck(columnName,initialized)){ 150 String getterName = metaData.getterMethods.get(columnId).getName(); 151 if(null != (getterMethod = methods.get(getterName))){ 152 left.setValue(columnId, getterMethod.invoke(right)); 153 if(bitCheck(columnName,modified)){ 154 selfModified |= (1<<columnId); 155 } 156 } 157 } 158 } 159 left.setNew((Boolean)methods.get(IS_NEW).invoke(right)); 160 left.setModified(selfModified); 161 }catch(RuntimeException e){ 162 throw e; 163 }catch(Exception e){ 164 throw new RuntimeException(e); 165 } 166 } 167 168 @Override 169 protected void doToRight(L left, R right) { 170 try{ 171 Method setterMethod; 172 int[] initialized = new int[(rightIndexs.size() + STATE_BIT_NUM - 1)>>STATE_BIT_SHIFT]; 173 int[] modified = new int[(rightIndexs.size() + STATE_BIT_NUM - 1)>>STATE_BIT_SHIFT]; 174 Arrays.fill(initialized, 0); 175 Arrays.fill(modified, 0); 176 for(int columnId = 0; columnId < metaData.columnCount; ++columnId){ 177 String columnName = metaData.columnNameOf(columnId); 178 String setterName = metaData.setterMethods.get(columnId).getName(); 179 if(null != (setterMethod = methods.get(setterName)) && left.isInitialized(columnId)){ 180 try{ 181 setterMethod.invoke(right,cast(setterParams.get(setterName),left.getValue(columnId))); 182 bitOR(columnName,initialized); 183 if(left.isModified(columnId)){ 184 bitOR(columnName,modified); 185 } 186 }catch(NullCastPrimitiveException e){} 187 } 188 } 189 // IGNORE field fl_device.create_time , controlled by 'general.beanconverter.tonative.ignore' in properties file 190 /* 191 if(null != (setterMethod = methods.get(Column.createTime.setter)) && left.checkCreateTimeInitialized()){ 192 try{ 193 setterMethod.invoke(right,cast(setterParams.get(Column.createTime.setter),left.getCreateTime())); 194 bitOR(Column.createTime.name(),initialized); 195 if(left.checkCreateTimeModified()){ 196 bitOR(Column.createTime.name(),modified); 197 } 198 }catch(NullCastPrimitiveException e){} 199 } 200 */ 201 // IGNORE field fl_device.update_time , controlled by 'general.beanconverter.tonative.ignore' in properties file 202 /* 203 if(null != (setterMethod = methods.get(Column.updateTime.setter)) && left.checkUpdateTimeInitialized()){ 204 try{ 205 setterMethod.invoke(right,cast(setterParams.get(Column.updateTime.setter),left.getUpdateTime())); 206 bitOR(Column.updateTime.name(),initialized); 207 if(left.checkUpdateTimeModified()){ 208 bitOR(Column.updateTime.name(),modified); 209 } 210 }catch(NullCastPrimitiveException e){} 211 } 212 */ 213 if(null != (setterMethod = methods.get(SET_MODIFIED))){ 214 if( initialized.length > 1){ 215 setterMethod.invoke(right,cast(setterParams.get(SET_MODIFIED),initialized)); 216 }else{ 217 setterMethod.invoke(right,initialized[0]); 218 } 219 } 220 methods.get(SET_NEW).invoke(right,left.isNew()); 221 if( initialized.length > 1){ 222 methods.get(SET_INITIALIZED).invoke(right,cast(setterParams.get(SET_INITIALIZED),initialized)); 223 methods.get(SET_MODIFIED).invoke(right,cast(setterParams.get(SET_MODIFIED),modified)); 224 }else{ 225 methods.get(SET_INITIALIZED).invoke(right,initialized[0]); 226 methods.get(SET_MODIFIED).invoke(right,modified[0]); 227 } 228 }catch(RuntimeException e){ 229 throw e; 230 }catch(Exception e){ 231 throw new RuntimeException(e); 232 } 233 } 234 /** 235 * 返回buffer中所有字节(position~limit),不改变buffer状态 236 * @param buffer 237 * @return byte array 238 */ 239 private static final byte[] getBytesInBuffer(ByteBuffer buffer){ 240 int pos = buffer.position(); 241 try{ 242 byte[] bytes = new byte[buffer.remaining()]; 243 buffer.get(bytes); 244 return bytes; 245 }finally{ 246 buffer.position(pos); 247 } 248 } 249 private static final List<Long> toList(long[] array) { 250 ArrayList<Long> result = new ArrayList<Long>(array.length); 251 for (int i = 0; i < array.length; i++) { 252 result.add(new Long(array[i])); 253 } 254 return result; 255 } 256 private static final long[] toPrimitive(List<Long> list) { 257 long[] dst = new long[list.size()]; 258 Long element; 259 for (int i = 0; i < dst.length; i++) { 260 if(null == (element = list.get(i))){ 261 throw new IllegalArgumentException("can't cast List<Long> to long[] because of null element"); 262 } 263 dst[i] = element.longValue(); 264 } 265 return dst; 266 } 267 /** 268 * {@code source}转为{@code type}指定的类型 269 * @param type destination type 270 * @param source source object 271 * @return T instance 272 */ 273 @SuppressWarnings({ "unchecked" }) 274 static final <T> T cast(Class<T> type,Object source){ 275 try{ 276 if(null ==source && type.isPrimitive()){ 277 throw new NullCastPrimitiveException(String.format("can't convert null to primitive type %s",type.getSimpleName())); 278 } 279 return (T) source; 280 }catch(ClassCastException cce){ 281 // long[] -> List 282 if(List.class.isAssignableFrom(type) && null != source && source instanceof long[]){ 283 return (T) toList((long[]) source); 284 } 285 // List -> long[] 286 if(long[].class == type && null != source && source instanceof List){ 287 return (T) toPrimitive( (List<Long>) source); 288 } 289 // Long -> Date 290 if(java.util.Date.class.isAssignableFrom(type) && null != source && source instanceof Long){ 291 try { 292 // call constructor,such as java.util.Date#Date(long), java.sql.Time.Time(long) 293 return type.getConstructor(long.class).newInstance(source); 294 } catch (Exception e) { 295 StringWriter writer = new StringWriter(); 296 e.printStackTrace(new PrintWriter(writer)); 297 throw new ClassCastException(writer.toString()); 298 } 299 } 300 // Date -> Long,long 301 if( long.class == type || Long.class == type){ 302 if(null != source && source instanceof java.util.Date){ 303 Long time = ((java.util.Date)source).getTime(); 304 return (T)time; 305 } 306 } 307 // byte[] -> ByteBuffer 308 if(ByteBuffer.class == type && null != source && source instanceof byte[]){ 309 return (T) ByteBuffer.wrap((byte[]) source); 310 } 311 // ByteBuffer -> byte[] 312 if(byte[].class == type && null != source && source instanceof ByteBuffer){ 313 return (T) getBytesInBuffer((ByteBuffer) source); 314 } 315 throw cce; 316 } 317 } 318 static final boolean bitCheck(int index,int...bits){ 319 return 0 != (bits[index>>STATE_BIT_SHIFT]&(1<<(index&0x1f))); 320 } 321 static final int[] bitOR(int index,int... bits){ 322 bits[index>>STATE_BIT_SHIFT] |= (1<<(index&0x1f)); 323 return bits; 324 } 325 private static class NullCastPrimitiveException extends ClassCastException { 326 private static final long serialVersionUID = 1L; 327 NullCastPrimitiveException(String message) { 328 super(message); 329 } 330 } 331} 332