/* * Copyright 2015, The Querydsl Team (http://www.querydsl.com/team) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.querydsl.jpa.codegen; import java.io.*; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.*; import javax.annotation.Nullable; import javax.persistence.Embeddable; import javax.persistence.Entity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.mysema.codegen.CodeWriter; import com.mysema.codegen.JavaWriter; import com.mysema.codegen.model.Type; import com.mysema.codegen.model.TypeCategory; import com.querydsl.codegen.*; import com.querydsl.core.QueryException; import com.querydsl.core.annotations.Config; import com.querydsl.core.annotations.PropertyType; import com.querydsl.core.annotations.QueryInit; import com.querydsl.core.annotations.QueryType; import com.querydsl.core.util.Annotations; import com.querydsl.core.util.ReflectionUtils; /** * {@code AbstractDomainExporter} is a common supertype for domain exporters * * @author tiwe * */ public abstract class AbstractDomainExporter { private static final Logger logger = LoggerFactory.getLogger(HibernateDomainExporter.class); private final File targetFolder; private final Map<Class<?>, EntityType> allTypes = Maps.newHashMap(); private final Map<Class<?>, EntityType> entityTypes = Maps.newHashMap(); private final Map<Class<?>, EntityType> embeddableTypes = Maps.newHashMap(); private final Map<Class<?>, EntityType> superTypes = Maps.newHashMap(); private final Map<Class<?>, SerializerConfig> typeToConfig = Maps.newHashMap(); private final Set<EntityType> serialized = new HashSet<EntityType>(); @SuppressWarnings("unchecked") protected final TypeFactory typeFactory = new TypeFactory(Arrays.asList(Entity.class, javax.persistence.MappedSuperclass.class, Embeddable.class)); private final QueryTypeFactory queryTypeFactory; private final TypeMappings typeMappings; private final Serializer embeddableSerializer; private final Serializer entitySerializer; private final Serializer supertypeSerializer; private final SerializerConfig serializerConfig; private final Charset charset; private final Set<File> generatedFiles = new HashSet<File>(); private Function<EntityType, String> variableNameFunction; @SuppressWarnings("unchecked") public AbstractDomainExporter(String namePrefix, String nameSuffix, File targetFolder, SerializerConfig serializerConfig, Charset charset) { this.targetFolder = targetFolder; this.serializerConfig = serializerConfig; this.charset = charset; CodegenModule module = new CodegenModule(); module.bind(CodegenModule.PREFIX, namePrefix); module.bind(CodegenModule.SUFFIX, nameSuffix); module.bind(CodegenModule.KEYWORDS, Constants.keywords); this.queryTypeFactory = module.get(QueryTypeFactory.class); this.typeMappings = module.get(TypeMappings.class); this.embeddableSerializer = module.get(EmbeddableSerializer.class); this.entitySerializer = module.get(EntitySerializer.class); this.supertypeSerializer = module.get(SupertypeSerializer.class); this.variableNameFunction = module.get(Function.class, CodegenModule.VARIABLE_NAME_FUNCTION_CLASS); } /** * Export the contents * * @throws IOException */ public void execute() throws IOException { // collect types try { collectTypes(); } catch (Exception e) { throw new QueryException(e); } // go through supertypes Set<Supertype> additions = Sets.newHashSet(); for (Map.Entry<Class<?>, EntityType> entry : allTypes.entrySet()) { EntityType entityType = entry.getValue(); if (entityType.getSuperType() != null && !allTypes.containsKey(entityType.getSuperType().getType().getJavaClass())) { additions.add(entityType.getSuperType()); } } for (Supertype type : additions) { type.setEntityType(createEntityType(type.getType(), this.superTypes)); } // merge supertype fields into subtypes Set<EntityType> handled = new HashSet<EntityType>(); for (EntityType type : superTypes.values()) { addSupertypeFields(type, allTypes, handled); } for (EntityType type : entityTypes.values()) { addSupertypeFields(type, allTypes, handled); } for (EntityType type : embeddableTypes.values()) { addSupertypeFields(type, allTypes, handled); } // serialize them serialize(superTypes, supertypeSerializer); serialize(embeddableTypes, embeddableSerializer); serialize(entityTypes, entitySerializer); } private void addSupertypeFields(EntityType model, Map<Class<?>, EntityType> superTypes, Set<EntityType> handled) { if (handled.add(model)) { for (Supertype supertype : model.getSuperTypes()) { EntityType entityType = superTypes.get(supertype.getType().getJavaClass()); if (entityType != null) { addSupertypeFields(entityType, superTypes, handled); supertype.setEntityType(entityType); model.include(supertype); } } } } protected abstract void collectTypes() throws Exception; protected EntityType createEmbeddableType(Class<?> cl) { return createEntityType(cl, embeddableTypes); } protected EntityType createEmbeddableType(Type type) { return createEntityType(type, embeddableTypes); } protected EntityType createEntityType(Class<?> cl) { return createEntityType(cl, entityTypes); } private EntityType createEntityType(Class<?> cl, Map<Class<?>, EntityType> types) { if (allTypes.containsKey(cl)) { return allTypes.get(cl); } else { EntityType type = typeFactory.getEntityType(cl); registerConfig(type); typeMappings.register(type, queryTypeFactory.create(type)); if (!cl.getSuperclass().equals(Object.class)) { type.addSupertype(new Supertype(typeFactory.get(cl.getSuperclass(), cl.getGenericSuperclass()))); } types.put(cl, type); allTypes.put(cl, type); return type; } } protected EntityType createEntityType(Type type) { return createEntityType(type, entityTypes); } protected EntityType createEntityType(Type type, Map<Class<?>, EntityType> types) { Class<?> key = type.getJavaClass(); if (allTypes.containsKey(key)) { return allTypes.get(key); } else { EntityType entityType = new EntityType(type, variableNameFunction); registerConfig(entityType); typeMappings.register(entityType, queryTypeFactory.create(entityType)); Class<?> superClass = key.getSuperclass(); if (entityType.getSuperType() == null && superClass != null && !superClass.equals(Object.class)) { entityType.addSupertype(new Supertype(typeFactory.get(superClass, key.getGenericSuperclass()))); } types.put(key, entityType); allTypes.put(key, entityType); return entityType; } } private void registerConfig(EntityType entityType) { Class<?> key = entityType.getJavaClass(); Config config = key.getAnnotation(Config.class); if (config == null && key.getPackage() != null) { config = key.getPackage().getAnnotation(Config.class); } if (config != null) { typeToConfig.put(key, SimpleSerializerConfig.getConfig(config)); } } @Nullable protected Type getTypeOverride(Type propertyType, AnnotatedElement annotated) { if (annotated.isAnnotationPresent(QueryType.class)) { QueryType queryType = annotated.getAnnotation(QueryType.class); if (queryType.value().equals(PropertyType.NONE)) { return null; } return propertyType.as(TypeCategory.valueOf(queryType.value().name())); } else { return propertyType; } } protected Property createProperty(EntityType entityType, String propertyName, Type propertyType, AnnotatedElement annotated) { List<String> inits = Collections.emptyList(); if (annotated.isAnnotationPresent(QueryInit.class)) { inits = ImmutableList.copyOf(annotated.getAnnotation(QueryInit.class).value()); } return new Property(entityType, propertyName, propertyType, inits); } protected EntityType createSuperType(Class<?> cl) { return createEntityType(cl, superTypes); } protected AnnotatedElement getAnnotatedElement(Class<?> cl, String propertyName) throws NoSuchMethodException { Field field = ReflectionUtils.getFieldOrNull(cl, propertyName); Method method = ReflectionUtils.getGetterOrNull(cl, propertyName); if (field != null) { if (method != null) { return new Annotations(field, method); } else { return field; } } else if (method != null) { return method; } else { throw new IllegalArgumentException("No property found for " + cl.getName() + "." + propertyName); } } public Set<File> getGeneratedFiles() { return generatedFiles; } protected Type getType(Class<?> cl, Class<?> mappedType, String propertyName) throws NoSuchMethodException { Field field = ReflectionUtils.getFieldOrNull(cl, propertyName); if (field != null) { if (mappedType.isAssignableFrom(field.getType())) { return typeFactory.get(field.getType(), field.getGenericType()); } else { return typeFactory.get(mappedType); } } else { Method method = ReflectionUtils.getGetterOrNull(cl, propertyName); if (method != null) { if (mappedType.isAssignableFrom(method.getReturnType())) { return typeFactory.get(method.getReturnType(), method.getGenericReturnType()); } else { return typeFactory.get(mappedType); } } else { throw new IllegalArgumentException("No property found for " + cl.getName() + "." + propertyName); } } } private void serialize(Map<Class<?>, EntityType> types, Serializer serializer) throws IOException { for (EntityType entityType : types.values()) { if (serialized.add(entityType)) { Type type = typeMappings.getPathType(entityType, entityType, true); String packageName = type.getPackageName(); String className = packageName.length() > 0 ? (packageName + "." + type.getSimpleName()) : type.getSimpleName(); write(serializer, className.replace('.', '/') + ".java", entityType); } } } private void write(Serializer serializer, String path, EntityType type) throws IOException { File targetFile = new File(targetFolder, path); generatedFiles.add(targetFile); Writer w = writerFor(targetFile); try { CodeWriter writer = new JavaWriter(w); if (typeToConfig.containsKey(type.getJavaClass())) { serializer.serialize(type, typeToConfig.get(type.getJavaClass()), writer); } else { serializer.serialize(type, serializerConfig, writer); } } finally { w.close(); } } private Writer writerFor(File file) { if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) { logger.error("Folder " + file.getParent() + " could not be created"); } try { return new OutputStreamWriter(new FileOutputStream(file), charset); } catch (FileNotFoundException e) { throw new RuntimeException(e.getMessage(), e); } } protected Type normalize(Type first, Type second) { if (first.getFullName().equals(second.getFullName())) { return first; } else { return second; } } public void setUnknownAsEntity(boolean unknownAsEntity) { typeFactory.setUnknownAsEntity(unknownAsEntity); } }