package org.jooq.codegen;
import static org.jooq.codegen.GenerationUtil.convertToIdentifier;
import static org.jooq.codegen.GenerationUtil.escapeWindowsForbiddenNames;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.jooq.Record;
import org.jooq.codegen.AbstractGenerator.Language;
import org.jooq.impl.AbstractRoutine;
import org.jooq.impl.TableImpl;
import org.jooq.impl.TableRecordImpl;
import org.jooq.impl.UDTRecordImpl;
import org.jooq.impl.UpdatableRecordImpl;
import org.jooq.meta.AttributeDefinition;
import org.jooq.meta.CatalogDefinition;
import org.jooq.meta.ColumnDefinition;
import org.jooq.meta.Definition;
import org.jooq.meta.ForeignKeyDefinition;
import org.jooq.meta.ParameterDefinition;
import org.jooq.meta.RoutineDefinition;
import org.jooq.meta.SchemaDefinition;
import org.jooq.meta.TableDefinition;
import org.jooq.meta.TypedElementDefinition;
import org.jooq.meta.UDTDefinition;
import org.jooq.tools.StringUtils;
class GeneratorStrategyWrapper extends AbstractGeneratorStrategy {
private final Map<Class<?>, Map<Integer, Set<String>>> reservedColumns = new HashMap<>();
final Generator generator;
final GeneratorStrategy delegate;
GeneratorStrategyWrapper(Generator generator, GeneratorStrategy delegate) {
this.generator = generator;
this.delegate = delegate;
}
@Override
public String getTargetDirectory() {
return delegate.getTargetDirectory();
}
@Override
public void setTargetDirectory(String directory) {
delegate.setTargetDirectory(directory);
}
@Override
public String getTargetPackage() {
return delegate.getTargetPackage();
}
@Override
public void setTargetPackage(String packageName) {
delegate.setTargetPackage(packageName);
}
@Override
public Locale getTargetLocale() {
return delegate.getTargetLocale();
}
@Override
public void setTargetLocale(Locale targetLocale) {
delegate.setTargetLocale(targetLocale);
}
@Override
public Language getTargetLanguage() {
return delegate.getTargetLanguage();
}
@Override
public void setTargetLanguage(Language targetLanguage) {
delegate.setTargetLanguage(targetLanguage);
}
@Override
public void setInstanceFields(boolean instanceFields) {
delegate.setInstanceFields(instanceFields);
}
@Override
public boolean getInstanceFields() {
return delegate.getInstanceFields();
}
@Override
public void setJavaBeansGettersAndSetters(boolean javaBeansGettersAndSetters) {
delegate.setJavaBeansGettersAndSetters(javaBeansGettersAndSetters);
}
@Override
public boolean getJavaBeansGettersAndSetters() {
return delegate.getJavaBeansGettersAndSetters();
}
@Override
public String (Definition container, Class<? extends Definition> objectType) {
return delegate.getGlobalReferencesFileHeader(container, objectType);
}
@Override
public String (Definition definition, Mode mode) {
return delegate.getFileHeader(definition, mode);
}
@Override
public String getJavaIdentifier(Definition definition) {
String identifier = getFixedJavaIdentifier(definition);
if (identifier != null)
return identifier;
identifier = convertToIdentifier(delegate.getJavaIdentifier(definition), getTargetLanguage());
if (definition instanceof ColumnDefinition ||
definition instanceof AttributeDefinition) {
TypedElementDefinition<?> e = (TypedElementDefinition<?>) definition;
if (identifier.equals(getJavaIdentifier(e.getContainer())))
return identifier + "_";
if (identifier.equals(getJavaPackageName(e.getContainer()).replaceAll("\\..*", "")))
return identifier + "_";
}
else if (definition instanceof TableDefinition) {
SchemaDefinition schema = definition.getSchema();
if (identifier.equals(getJavaIdentifier(schema)))
return identifier + "_";
}
else if (definition instanceof SchemaDefinition) {
CatalogDefinition catalog = definition.getCatalog();
if (identifier.equals(getJavaIdentifier(catalog)))
return identifier + "_";
}
identifier = overload(definition, Mode.DEFAULT, identifier);
return identifier;
}
@Override
public String getJavaSetterName(Definition definition, Mode mode) {
return fixMethodName(definition, mode, delegate.getJavaSetterName(definition, mode));
}
@Override
public String getJavaGetterName(Definition definition, Mode mode) {
return fixMethodName(definition, mode, delegate.getJavaGetterName(definition, mode));
}
@Override
public String getJavaMethodName(Definition definition, Mode mode) {
return fixMethodName(definition, mode, delegate.getJavaMethodName(definition, mode));
}
private String fixMethodName(Definition definition, Mode mode, String methodName) {
methodName = overload(definition, mode, methodName);
methodName = convertToIdentifier(methodName, getTargetLanguage());
return disambiguateMethod(definition, methodName);
}
private String overload(Definition definition, Mode mode, String identifier) {
if (!StringUtils.isBlank(definition.getOverload()))
identifier += getOverloadSuffix(definition, mode, definition.getOverload());
return identifier;
}
private String disambiguateMethod(Definition definition, String method) {
Set<String> reserved = null;
if (definition instanceof AttributeDefinition) {
reserved = reservedColumns(UDTRecordImpl.class, 0);
}
else if (definition instanceof ColumnDefinition) {
if (((ColumnDefinition) definition).getContainer().getPrimaryKey() != null)
reserved = reservedColumns(UpdatableRecordImpl.class, 0);
else
reserved = reservedColumns(TableRecordImpl.class, 0);
}
else if (definition instanceof ParameterDefinition) {
reserved = reservedColumns(AbstractRoutine.class, 0);
}
else if (definition instanceof RoutineDefinition) {
RoutineDefinition routine = (RoutineDefinition) definition;
if (routine.getPackage() instanceof UDTDefinition
&& routine.getInParameters().size() > 0
&& "SELF".equalsIgnoreCase(routine.getInParameters().get(0).getName()))
reserved = reservedColumns(UDTRecordImpl.class, routine.getInParameters().size() - 1);
}
else if (definition instanceof ForeignKeyDefinition) {
reserved = reservedColumns(TableImpl.class, 0);
}
if (reserved != null) {
if (reserved.contains(method))
return method + "_";
if (method.startsWith("set")) {
String base = method.substring(3);
if (reserved.contains("get" + base) || reserved.contains("is" + base))
return method + "_";
}
}
return method;
}
private Set<String> reservedColumns(Class<?> clazz, int length) {
if (clazz == null)
return Collections.emptySet();
Map<Integer, Set<String>> map = reservedColumns.get(clazz);
if (map == null) {
map = new HashMap<>();
reservedColumns.put(clazz, map);
}
Set<String> result = map.get(length);
if (result == null) {
result = new HashSet<>();
map.put(length, result);
result.addAll(reservedColumns(clazz.getSuperclass(), length));
for (Class<?> c : clazz.getInterfaces())
result.addAll(reservedColumns(c, length));
for (Method m : clazz.getDeclaredMethods())
if (m.getParameterTypes().length == length)
result.add(m.getName());
if (getTargetLanguage() == Language.SCALA)
for (Field f : clazz.getDeclaredFields())
result.add(f.getName());
}
return result;
}
@Override
public String getGlobalReferencesJavaClassExtends(Definition container, Class<? extends Definition> objectType) {
return delegate.getGlobalReferencesJavaClassExtends(container, objectType);
}
@Override
public String getJavaClassExtends(Definition definition, Mode mode) {
return delegate.getJavaClassExtends(definition, mode);
}
@Override
public List<String> getGlobalReferencesJavaClassImplements(Definition container, Class<? extends Definition> objectType) {
return delegate.getGlobalReferencesJavaClassImplements(container, objectType);
}
@Override
public List<String> getJavaClassImplements(Definition definition, Mode mode) {
Set<String> result = new LinkedHashSet<>(delegate.getJavaClassImplements(definition, mode));
if (mode == Mode.INTERFACE
&& generator.generateSerializableInterfaces())
result.add(Serializable.class.getName());
else if (mode == Mode.POJO
&& generator.generateSerializablePojos()
&& (!generator.generateInterfaces() || !generator.generateSerializableInterfaces()))
result.add(Serializable.class.getName());
return new ArrayList<>(result);
}
@Override
public String getGlobalReferencesJavaClassName(Definition container, Class<? extends Definition> objectType) {
return fixJavaClassName(delegate.getGlobalReferencesJavaClassName(container, objectType));
}
@Override
public String getJavaClassName(Definition definition, Mode mode) {
String name = getFixedJavaClassName(definition);
if (name != null)
return name;
if (definition instanceof TableDefinition && !generator.generateRecords() && mode == Mode.RECORD)
return Record.class.getSimpleName();
String className;
className = delegate.getJavaClassName(definition, mode);
className = overload(definition, mode, className);
return fixJavaClassName(className);
}
private String fixJavaClassName(String className) {
className = convertToIdentifier(className, getTargetLanguage());
className = escapeWindowsForbiddenNames(className);
return className;
}
@Override
public String getGlobalReferencesJavaPackageName(Definition container, Class<? extends Definition> objectType) {
return fixJavaPackageName(delegate.getGlobalReferencesJavaPackageName(container, objectType));
}
@Override
public String getJavaPackageName(Definition definition, Mode mode) {
if (!generator.generateRecords() && mode == Mode.RECORD && definition instanceof TableDefinition)
return Record.class.getPackage().getName();
return fixJavaPackageName(delegate.getJavaPackageName(definition, mode));
}
private String fixJavaPackageName(String packageName) {
String[] split = packageName.split("\\.");
for (int i = 0; i < split.length; i++) {
split[i] = convertToIdentifier(split[i], getTargetLanguage());
split[i] = escapeWindowsForbiddenNames(split[i]);
}
return StringUtils
.join(split, ".")
.replaceAll("\\._?\\.", ".");
}
@Override
public String getJavaMemberName(Definition definition, Mode mode) {
String identifier = convertToIdentifier(delegate.getJavaMemberName(definition, mode), getTargetLanguage());
if (identifier.equals(getJavaPackageName(definition, mode).replaceAll("\\..*", ""))) {
return identifier + "_";
}
return identifier;
}
@Override
public String getOverloadSuffix(Definition definition, Mode mode, String overloadIndex) {
return delegate.getOverloadSuffix(definition, mode, overloadIndex);
}
}