001/* 002 * JKNIV, whinstone one contract to access your database. 003 * 004 * Copyright (C) 2017, the original author or authors. 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software Foundation, Inc., 018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 019 */ 020package net.sf.jkniv.whinstone.types; 021 022import java.lang.reflect.Field; 023import java.lang.reflect.Method; 024import java.util.Collection; 025import java.util.HashMap; 026import java.util.Map; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import net.sf.jkniv.reflect.beans.ObjectProxy; 032import net.sf.jkniv.reflect.beans.ObjectProxyFactory; 033import net.sf.jkniv.reflect.beans.PropertyAccess; 034import net.sf.jkniv.whinstone.JdbcColumn; 035import net.sf.jkniv.whinstone.types.Converter.EnumType; 036 037/** 038 * A register type of {@link Convertible} data 039 * 040 * @author Alisson Gomes 041 * @since 0.6.0 042 */ 043@SuppressWarnings({ "unchecked", "rawtypes" }) 044public class RegisterType 045{ 046 private final static Logger LOG = LoggerFactory.getLogger(RegisterType.class); 047 private final Map<TypeMap, Convertible<Object, Object>> registry = new HashMap<TypeMap, Convertible<Object, Object>>(); 048 049 public void register(Convertible convertible) 050 { 051 LOG.info("Registering converter {} {}", convertible); 052 Convertible<Object, Object> c = registry.put(new TypeMap(convertible.getType(), convertible.getColumnType()), convertible); 053 if (c != null && c != convertible) 054 LOG.warn("The converter {} was replaced by {}", c, convertible); 055 056 TypeMap typeMapByClass = new TypeMap(convertible.getType(), UnknowType.getInstance()); 057 c = registry.put(typeMapByClass, convertible); 058 if (c != null && c != convertible) 059 LOG.warn("Default converter for {} was replaced by {}", c, convertible); 060 } 061 062 /** 063 * Retrieve a {@link Convertible} instance to customize the 064 * value of database to class field. 065 * @param <T> type of object into proxy reference 066 * @param access for property 067 * @param proxy of return type from query 068 * @return A convertible instance if found into class proxy or {@link NoConverterType} 069 * instance when the field or method is not annotated. 070 */ 071 public <T> Convertible<Object, Object> toJdbc(PropertyAccess access, ObjectProxy<T> proxy) 072 { 073 Convertible<Object, Object> convertible = getConverterByAnnotation(access.getField(), access.getReadMethod(), proxy); 074 if (convertible == null) 075 { 076 if(access.hasField()) 077 convertible = registry.get(new TypeMap(access.getField().getType(), UnknowType.getInstance())); 078 if (convertible == null) 079 convertible = NoConverterType.getInstance(); 080 } 081 return convertible; 082 } 083 084 public Convertible<Object, Object> getConverter(Class classType) 085 { 086 Convertible<Object, Object> convertible = registry.get(new TypeMap(classType, UnknowType.getInstance())); 087 if (convertible == null) 088 convertible = NoConverterType.getInstance(); 089 090 return convertible; 091 } 092 093 /** 094 * Retrieve a {@link Convertible} instance to customize the 095 * value of database to class field. 096 * @param <T> type of object into proxy reference 097 * @param <R> the result of a query (like a {@link java.sql.ResultSet} 098 * @param column metadata of it 099 * @param proxy of return type from query 100 * @return A convertible instance if found into class proxy or {@link NoConverterType} 101 * instance when the field or method is not annotated. 102 */ 103 public <T,R> Convertible<Object, Object> toAttribute(JdbcColumn<R> column, ObjectProxy<T> proxy) 104 { 105 PropertyAccess access = column.getPropertyAccess(); 106 Convertible<Object, Object> convertible = getConverterByAnnotation(access.getField(), access.getWriterMethod(), proxy); 107 if (convertible == null) 108 { 109 if (column.getPropertyAccess().hasField()) 110 convertible = registry.get(new TypeMap(column.getPropertyAccess().getField().getType(), column.getType())); 111 else if (column.getPropertyAccess().hasWriterMethod()) 112 convertible = registry.get(new TypeMap(column.getPropertyAccess().getWriterMethod().getParameterTypes()[0], column.getType())); 113 114 if (convertible == null) 115 convertible = NoConverterType.getInstance(); 116 } 117 return convertible; 118 } 119 120 /** 121 * Retrieve a {@link Convertible} instance to customize the 122 * value of database to class field. 123 * @param <T> type of proxy instance 124 * @param field attribute name from class 125 * @param method getter or setter method 126 * @param proxy of return type from query 127 * @return A convertible instance if found into class proxy or {@link NoConverterType} 128 * instance when the field or method is not annotated. 129 */ 130 private <T> Convertible<Object, Object> getConverterByAnnotation(Field field, Method method, 131 ObjectProxy<T> proxy) 132 { 133 Convertible convertible = null; 134 Converter converter = null; 135 136 if (field == null || method == null || Map.class.isAssignableFrom(proxy.getTargetClass()) 137 || Collection.class.isAssignableFrom(proxy.getTargetClass()) || proxy.getTargetClass().isArray()) 138 return convertible; 139 140 converter = (Converter) method.getAnnotation(Converter.class); 141 if (converter == null) 142 converter = (Converter) field.getAnnotation(Converter.class); 143 144 if (converter != null) 145 { 146 ObjectProxy proxyConvertible = null; 147 if (converter.converter().isEnum()) 148 { 149 if (converter.isEnum() == EnumType.ORDINAL) 150 convertible = new EnumOrdinalType(converter.converter()); 151 else 152 convertible = new EnumNameType(converter.converter()); 153 } 154 else 155 { 156 proxyConvertible = ObjectProxyFactory.of(converter.converter()); 157 if (converter.pattern() != null) 158 proxyConvertible.setConstructorArgs(converter.pattern()); 159 convertible = (Convertible) proxyConvertible.newInstance(); 160 } 161 } 162 else if(field.getType().isEnum()) // doesn't use @Converter default is persist as String 163 { 164 convertible = new EnumNameType(field.getType()); 165 } 166 return convertible; 167 } 168 169 public boolean isEmpty() 170 { 171 return registry.isEmpty(); 172 } 173}