/* * This file is part of the GeoLatte project. * * GeoLatte is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GeoLatte is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with GeoLatte. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010 - 2012 and Ownership of code is shared by: * Qmino bvba - Romeinsestraat 18 - 3001 Heverlee (http://www.qmino.com) * Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com) */ package org.geolatte.common.automapper; import javassist.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.ProtectionDomain; /** * Generates POJO Classes from <code>TableMetaData</code>. * <p/> * <p>The <code>MappedClassGenerator</code> also loads the generated classes in a <code>ClassLoader</code>, and records * the details of the mapping between table and POJO Class in a <code>TableMapping</code> instance.</p> * * @author Karel Maesen, Geovise BVBA */ class MappedClassGenerator { final private static Logger LOGGER = LoggerFactory.getLogger(MappedClassGenerator.class); final private static ClassPool pool = ClassPool.getDefault(); static { pool.insertClassPath(new ClassClassPath(MappedClassGenerator.class)); } final private String packageName; final private NamingStrategy naming; final private TypeMapper typeMapper; /** * Constructs an instance * * @param packageName the name of the package to use for classes generated by this instance. * @param naming the <code>NamingStrategy</code> to use * @param typeMapper the <code>TypeMapper</code> to use */ MappedClassGenerator(String packageName, NamingStrategy naming, TypeMapper typeMapper) { this.packageName = packageName; this.naming = naming; this.typeMapper = typeMapper; } /** * Creates a <code>TableMapping</code> for the specified <code>TableMetaData</code>. * * @param tableMetaData the <code>TableMetaData</code> for which to construct a <code>TableMapping</code>. * @param classLoader the <code>ClassLoader</code> into which the POJO class * @return a <code>TableMapping</code> that contains the generated POJO class */ TableMapping generate(TableMetaData tableMetaData, ClassLoader classLoader) { try { String className = packageName + "." + naming.createClassName(tableMetaData.getTableRef()); LOGGER.info(String.format("Mapping table %s to %s", tableMetaData.getTableRef(), className)); TableMapping result = new TableMapping(tableMetaData); CtClass ctClass = pool.makeClass(className); for (ColumnMetaData ai : tableMetaData.getColumnMetaData()) { generatePropertyForAttribute(result, ctClass, ai); } Class<?> clazz = loadClass(classLoader, ctClass); result.setGeneratedClass(clazz); return result; } catch (CannotCompileException e) { throw new RuntimeException("Problem generating class for table " + tableMetaData.getTableRef(), e); } } private Class<?> loadClass(ClassLoader classLoader, CtClass ctClass) throws CannotCompileException { ProtectionDomain pd = ctClass.getClass().getProtectionDomain(); Class<?> clazz = ctClass.toClass(classLoader, pd); ctClass.detach(); return clazz; } private void generatePropertyForAttribute(TableMapping tableMapping, CtClass pojo, ColumnMetaData ai) { try { CtClass ctClass = getCtClass(ai); String propertyName = naming.createPropertyName(ai.getColumnName()); CtField field = new CtField(ctClass, propertyName, pojo); CtMethod getter = createGetterMethod(field); CtMethod setter = createSetterMethod(field); pojo.addField(field); pojo.addMethod(getter); pojo.addMethod(setter); tableMapping.addColumnMapping(ai, propertyName, typeMapper.getHibernateType(ai.getDbTypeName(), ai.getSqlType()), typeMapper.getClass(ai.getDbTypeName(), ai.getSqlType())); return; } catch (CannotCompileException e) { LOGGER.warn("Error compiling getter/setter methods for column: " + ai.getColumnName(), e); } catch (TypeNotFoundException e) { LOGGER.warn(String.format("Cannot match type for column %s (sql-type %d).", ai.getColumnName(), ai.getSqlType())); } LOGGER.warn("No property included in mapped class corresponding to column " + ai.getColumnName()); } private CtClass getCtClass(ColumnMetaData ai) throws TypeNotFoundException { Class<?> javaClass = typeMapper.getClass(ai.getDbTypeName(), ai.getSqlType()); if (javaClass == null) { throw new TypeNotFoundException(String.format("No type found in Typemapper for: %s (java.sql.Types: %s).", ai.getDbTypeName(), ai.getSqlType())); } try { return pool.get(javaClass.getCanonicalName()); } catch (NotFoundException e) { throw new TypeNotFoundException(javaClass.getCanonicalName(), e); } } private CtMethod createGetterMethod(CtField field) throws CannotCompileException { String fn = field.getName(); return CtNewMethod.getter(this.naming.createGetterName(fn), field); } private CtMethod createSetterMethod(CtField field) throws CannotCompileException { String fn = field.getName(); return CtNewMethod.setter(naming.createSetterName(fn), field); } }