ru.curs.celesta.plugin.maven.CursorGenerator Maven / Gradle / Ivy
The newest version!
package ru.curs.celesta.plugin.maven;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import ru.curs.celesta.CallContext;
import ru.curs.celesta.CelestaException;
import ru.curs.celesta.ICelesta;
import ru.curs.celesta.dbutils.BasicCursor;
import ru.curs.celesta.dbutils.BasicDataAccessor;
import ru.curs.celesta.dbutils.CelestaGenerated;
import ru.curs.celesta.dbutils.Cursor;
import ru.curs.celesta.dbutils.CursorIterator;
import ru.curs.celesta.dbutils.MaterializedViewCursor;
import ru.curs.celesta.dbutils.ParameterizedViewCursor;
import ru.curs.celesta.dbutils.ReadOnlyTableCursor;
import ru.curs.celesta.dbutils.Sequence;
import ru.curs.celesta.dbutils.ViewCursor;
import ru.curs.celesta.event.TriggerType;
import ru.curs.celesta.score.BasicTable;
import ru.curs.celesta.score.BinaryColumn;
import ru.curs.celesta.score.Column;
import ru.curs.celesta.score.ColumnMeta;
import ru.curs.celesta.score.DataGrainElement;
import ru.curs.celesta.score.Grain;
import ru.curs.celesta.score.GrainElement;
import ru.curs.celesta.score.IntegerColumn;
import ru.curs.celesta.score.MaterializedView;
import ru.curs.celesta.score.NamedElement;
import ru.curs.celesta.score.Parameter;
import ru.curs.celesta.score.ParameterizedView;
import ru.curs.celesta.score.ReadOnlyTable;
import ru.curs.celesta.score.SequenceElement;
import ru.curs.celesta.score.StringColumn;
import ru.curs.celesta.score.Table;
import ru.curs.celesta.score.TableElement;
import ru.curs.celesta.score.VersionedElement;
import ru.curs.celesta.score.View;
import ru.curs.celesta.score.ZonedDateTimeColumn;
import ru.curs.celesta.score.io.FileResource;
import javax.annotation.Generated;
import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public final class CursorGenerator {
private static final String GRAIN_FIELD_NAME = "GRAIN_NAME";
private static final String OBJECT_FIELD_NAME = "OBJECT_NAME";
private static final String COLUMNS_FIELD_NAME = "COLUMNS";
private static final HashMap<
Class extends GrainElement>,
Function>
>
GRAIN_ELEMENTS_TO_DATA_ACCESSORS = new HashMap<>();
static {
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(SequenceElement.class, ge -> Sequence.class);
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(Table.class, ge -> Cursor.class);
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(ReadOnlyTable.class, ge -> ReadOnlyTableCursor.class);
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(View.class, ge -> ViewCursor.class);
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(MaterializedView.class, ge -> MaterializedViewCursor.class);
GRAIN_ELEMENTS_TO_DATA_ACCESSORS.put(ParameterizedView.class, ge -> ParameterizedViewCursor.class);
}
private static final Map TRIGGER_REGISTRATION_METHOD_TO_TRIGGER_TYPE;
static {
Map map = new LinkedHashMap<>();
map.put("onPreDelete", "PRE_DELETE");
map.put("onPostDelete", "POST_DELETE");
map.put("onPreInsert", "PRE_INSERT");
map.put("onPostInsert", "POST_INSERT");
map.put("onPreUpdate", "PRE_UPDATE");
map.put("onPostUpdate", "POST_UPDATE");
TRIGGER_REGISTRATION_METHOD_TO_TRIGGER_TYPE = Collections.unmodifiableMap(map);
}
private final File srcDir;
private final boolean snakeToCamel;
/**
* Creates code generator for data accessor classes.
*
* @param srcDir Path to directory where generated files should be put
* @param snakeToCamel True if snake_case identifiers should be converted to camelCase
*/
public CursorGenerator(File srcDir, boolean snakeToCamel) {
this.srcDir = srcDir;
this.snakeToCamel = snakeToCamel;
}
/**
* Generate code for schema (grain) element.
*
* @param ge Schema (grain) element
* @param scorePath path to CelestaSQL file
*/
public void generateCursor(GrainElement ge, String scorePath) {
final String sourcePackage = calcSourcePackage(ge, scorePath);
if (sourcePackage.isEmpty()) {
throw new CelestaException(
"Couldn't generate class file for %s.%s without package",
ge.getGrain().getName(), ge.getName()
);
}
final String className = calcClassName(ge);
final String columnsClassName = "Columns";
boolean isVersionedGe = ge instanceof VersionedElement && ((VersionedElement) ge).isVersioned();
ClassName classType = ClassName.bestGuess(className);
TypeSpec.Builder cursorClass = buildClassDefinition(ge, classType);
ClassName columnsClassType = classType.nestedClass(columnsClassName);
cursorClass.addFields(buildMetaFields(ge));
cursorClass.addMethods(buildConstructors(ge));
//FIELDS
if (ge instanceof DataGrainElement) {
DataGrainElement dge = (DataGrainElement) ge;
FieldSpec columnsField = buildColumnsField(columnsClassType);
cursorClass.addField(columnsField);
cursorClass.addInitializerBlock(buildColumnsFiledInitializer(columnsField));
List fieldSpecs = buildDataFields(dge);
cursorClass.addFields(fieldSpecs);
cursorClass.addMethods(generateGettersAndSetters(fieldSpecs, classType));
cursorClass.addMethod(buildGetFieldValue(dge.getColumns()));
cursorClass.addMethod(buildSetFieldValue(dge.getColumns()));
StringBuilder parseResultOverridingMethodNameBuilder = new StringBuilder("_parseResult");
Set> pk = Collections.emptySet();
if (dge instanceof TableElement && !(dge instanceof ReadOnlyTable)) {
TableElement te = (TableElement) dge;
pk = new LinkedHashSet<>(te.getPrimaryKey().values());
cursorClass.addMethod(buildCurrentKeyValues(pk));
cursorClass.addMethod(buildTryGet(pk));
cursorClass.addMethod(buildGet(pk));
if (te instanceof Table) {
parseResultOverridingMethodNameBuilder.append("Internal");
}
}
final Map> columns = dge.getColumns();
MethodSpec buildParseResultMethod = buildParseResult(
columns, parseResultOverridingMethodNameBuilder.toString(), isVersionedGe);
cursorClass.addMethod(buildParseResultMethod);
cursorClass.addMethod(buildClearBuffer(columns, pk));
cursorClass.addMethod(buildCurrentValues(columns));
cursorClass.addType(buildCursorColumnsAsInnerStaticClass(dge, columnsClassType));
if (dge instanceof BasicTable) {
BasicTable t = (BasicTable) dge;
if (t instanceof Table) {
cursorClass.addMethods(buildCalcBlobs(columns, className));
cursorClass.addMethod(buildSetAutoIncrement(columns));
cursorClass.addMethods(buildTriggerRegistration(classType));
}
cursorClass.addTypes(
buildOptionFieldsAsInnerStaticClasses(t.getColumns().values()));
}
cursorClass.addMethods(buildCompileCopying(ge, classType, columns.keySet(), isVersionedGe));
cursorClass.addMethod(buildIterator(classType));
}
cursorClass.addMethods(buildGrainNameAndObjectName());
JavaFile javaFile = JavaFile.builder(sourcePackage, cursorClass.build())
.skipJavaLangImports(true)
.indent(" ")
.build();
try {
javaFile.writeTo(srcDir);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static String calcSourcePackage(GrainElement ge, String scorePath) {
String result;
Grain g = ge.getGrain();
if (g.getName().equals(g.getScore().getSysSchemaName())) {
result = "ru.curs.celesta.syscursors";
} else {
String grainPartRelativePath =
new FileResource(new File(scorePath)).getRelativePath(ge.getGrainPart().getSource());
result = Optional.of(grainPartRelativePath.lastIndexOf(File.separatorChar))
.filter(i -> i >= 0)
.map(i -> grainPartRelativePath.substring(0, i).replace(File.separator, "."))
.orElse("");
if (result.startsWith(".")) {
result = result.substring(1);
}
}
return result;
}
private String calcClassName(GrainElement ge) {
final String sourceFileNamePrefix = CaseUtils.capitalize(camelize(ge.getName()));
if (ge instanceof SequenceElement) {
return sourceFileNamePrefix + "Sequence";
} else {
return sourceFileNamePrefix + "Cursor";
}
}
private String camelize(String s) {
return snakeToCamel ? CaseUtils.snakeToCamel(s) : s;
}
private static String getCurrentDate() {
return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
}
private static AnnotationSpec buildGeneratedAnnotation() {
return AnnotationSpec.builder(Generated.class)
.addMember("value", "$S", CursorGenerator.class.getCanonicalName())
.addMember("date", "$S", getCurrentDate())
.build();
}
private static TypeSpec.Builder buildClassDefinition(GrainElement ge, ClassName classType) {
TypeSpec.Builder builder = TypeSpec.classBuilder(classType)
.addModifiers(Modifier.PUBLIC)
.superclass(GRAIN_ELEMENTS_TO_DATA_ACCESSORS.get(ge.getClass()).apply(ge))
.addAnnotation(buildGeneratedAnnotation())
.addAnnotation(AnnotationSpec.builder(CelestaGenerated.class).build());
if (ge instanceof DataGrainElement) {
builder.addSuperinterface(
ParameterizedTypeName.get(ClassName.get(Iterable.class), classType)
);
}
if (ge instanceof BasicTable) {
BasicTable t = (BasicTable) ge;
if (!(t instanceof ReadOnlyTable)) {
t.getImplements().forEach(
i -> builder.addSuperinterface(ClassName.bestGuess(i))
);
}
}
return builder;
}
private List buildOptionFieldsAsInnerStaticClasses(Collection> columns) {
return columns.stream()
.filter(c -> (c instanceof IntegerColumn || c instanceof StringColumn) && !c.getOptions().isEmpty())
.map(
c -> {
TypeSpec.Builder builder = TypeSpec.classBuilder(
CaseUtils.capitalize(camelize(c.getName())))
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addAnnotation(buildGeneratedAnnotation())
.addAnnotation(CelestaGenerated.class);
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PRIVATE)
.addStatement("throw new $T()", AssertionError.class)
.build();
builder.addMethod(constructor);
List options = c.getOptions();
builder.addFields(
options.stream().map(s -> {
FieldSpec.Builder fb = FieldSpec.builder(
c.getJavaClass(), s,
Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL
);
if (c instanceof IntegerColumn) {
fb.initializer("$L", options.indexOf(s));
} else {
fb.initializer("$S", s);
}
return fb.build();
}
).collect(Collectors.toList())
);
return builder.build();
}
).collect(Collectors.toList());
}
private static List buildConstructors(GrainElement ge) {
List results = new ArrayList<>();
ParameterSpec contextParam = ParameterSpec.builder(CallContext.class, "context")
.build();
ParameterSpec columnsParam = ParameterSpec.builder(
ArrayTypeName.of(
ParameterizedTypeName.get(ClassName.get(ColumnMeta.class),
WildcardTypeName.subtypeOf(Object.class))),
"columns")
.build();
ParameterSpec parametersParam = ParameterSpec.builder(
ParameterizedTypeName.get(Map.class, String.class, Object.class), "parameters")
.build();
Supplier msp = () -> MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(contextParam);
// Common constructor
MethodSpec.Builder builder = msp.get();
if (ge instanceof ParameterizedView) {
builder.addParameter(parametersParam);
builder.addStatement("super(context, parameters)");
} else {
builder.addStatement("super(context)");
}
results.add(builder.build());
if (ge instanceof SequenceElement) {
return results;
}
// Constructor with columns limitation
builder = msp.get();
if (ge instanceof ParameterizedView) {
builder.addParameter(parametersParam);
builder.addParameter(columnsParam).varargs();
builder.addStatement("super(context, parameters, columns)");
} else {
builder.addParameter(columnsParam).varargs();
builder.addStatement("super(context, columns)");
}
results.add(builder.build());
//ParameterizedView constructors
if (ge instanceof ParameterizedView) {
ParameterizedView pv = (ParameterizedView) ge;
builder = msp.get();
for (Parameter parameter : pv.getParameters().values()) {
builder.addParameter(ParameterSpec.builder(
parameter.getJavaClass(), parameter.getName()
).build());
}
String spec = "super (context, paramsMap("
+ pv.getParameters().values().stream().map(c -> "$N").collect(Collectors.joining(", "))
+ "))";
builder.addStatement(spec, pv.getParameters().keySet().toArray());
results.add(builder.build());
builder = msp.get();
for (Parameter parameter : pv.getParameters().values()) {
builder.addParameter(ParameterSpec.builder(
parameter.getJavaClass(), parameter.getName()
).build());
}
builder.addParameter(columnsParam).varargs();
spec = "super (context, paramsMap("
+ pv.getParameters().values().stream().map(c -> "$N").collect(Collectors.joining(", "))
+ "), columns)";
builder.addStatement(spec, pv.getParameters().keySet().toArray());
results.add(builder.build());
results.add(getParameterizedViewTypedConstructorHelper(pv));
}
return results;
}
private static MethodSpec getParameterizedViewTypedConstructorHelper(ParameterizedView pv) {
MethodSpec.Builder builder = MethodSpec.methodBuilder("paramsMap")
.addModifiers(Modifier.PRIVATE, Modifier.STATIC).returns(
ParameterizedTypeName.get(Map.class,
String.class, Object.class));
builder.addStatement("$T<$T,$T> params = new $T<>()",
Map.class, String.class, Object.class, HashMap.class);
for (Parameter parameter : pv.getParameters().values()) {
String paramName = parameter.getName();
builder.addParameter(ParameterSpec.builder(
parameter.getJavaClass(), paramName
).build());
builder.addStatement("params.put($S, $N)", paramName, paramName);
}
builder.addStatement("return params");
return builder.build();
}
private static List buildGrainNameAndObjectName() {
MethodSpec grainName = MethodSpec.methodBuilder("_grainName")
.addAnnotation(Override.class)
.returns(String.class)
.addModifiers(Modifier.PROTECTED)
.addStatement("return $L", GRAIN_FIELD_NAME)
.build();
MethodSpec objectName = MethodSpec.methodBuilder("_objectName")
.addAnnotation(Override.class)
.returns(String.class)
.addModifiers(Modifier.PROTECTED)
.addStatement("return $L", OBJECT_FIELD_NAME)
.build();
return Arrays.asList(grainName, objectName);
}
private TypeSpec buildCursorColumnsAsInnerStaticClass(DataGrainElement dge, ClassName columnsClassType) {
TypeSpec.Builder builder = TypeSpec.classBuilder(columnsClassType)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class)
.addMember("value", "$S", "unchecked")
.build())
.addAnnotation(buildGeneratedAnnotation())
.addAnnotation(CelestaGenerated.class);
FieldSpec elementField = FieldSpec.builder(
dge.getClass(), "element", Modifier.PRIVATE, Modifier.FINAL)
.build();
builder.addField(elementField);
builder.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ICelesta.class, "celesta")
.addStatement("this.$N = celesta.getScore().getGrains().get($L).getElements($T.class).get($L)",
elementField, GRAIN_FIELD_NAME, elementField.type, OBJECT_FIELD_NAME)
.build());
dge.getColumns().entrySet().stream()
.filter(e -> !BinaryColumn.CELESTA_TYPE.equals(e.getValue().getCelestaType()))
.map(e -> {
final String columnName = e.getKey();
final TypeName columnType =
ParameterizedTypeName.get(ColumnMeta.class, e.getValue().getJavaClass());
return MethodSpec.methodBuilder(camelize(columnName))
.addModifiers(Modifier.PUBLIC)
.returns(columnType)
.addStatement("return ($T) this.$N.getColumns().get($S)",
columnType, elementField, columnName)
.build();
})
.forEach(builder::addMethod);
return builder.build();
}
private static List buildMetaFields(GrainElement ge) {
final String grainName = ge.getGrain().getName();
FieldSpec grainField = FieldSpec.builder(String.class, GRAIN_FIELD_NAME,
Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", grainName)
.build();
FieldSpec objectField = FieldSpec.builder(String.class, OBJECT_FIELD_NAME,
Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", ge.getName())
.build();
if (!grainName.equals(ge.getGrain().getScore().getSysSchemaName())) {
return Arrays.asList(grainField, objectField);
}
FieldSpec tableField = FieldSpec.builder(String.class, "TABLE_NAME",
Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$N", objectField)
.build();
return Arrays.asList(grainField, objectField, tableField);
}
private static FieldSpec buildColumnsField(TypeName columnsClassType) {
return FieldSpec.builder(columnsClassType, COLUMNS_FIELD_NAME, Modifier.PUBLIC, Modifier.FINAL)
.build();
}
private static CodeBlock buildColumnsFiledInitializer(FieldSpec columnsField) {
return CodeBlock.builder().addStatement(
"this.$N = new $T(callContext().getCelesta())", columnsField, columnsField.type)
.build();
}
private List buildDataFields(DataGrainElement dge) {
Map> columns = dge.getColumns();
return columns.entrySet().stream()
.map(e -> FieldSpec.builder(e.getValue().getJavaClass(), camelize(e.getKey()), Modifier.PRIVATE))
.map(FieldSpec.Builder::build)
.collect(Collectors.toList());
}
private List generateGettersAndSetters(List fieldSpecs, TypeName selfTypeName) {
List result = new ArrayList<>();
fieldSpecs.forEach(
fieldSpec -> {
String methodSuffix = CaseUtils.capitalize(camelize(fieldSpec.name));
MethodSpec getter = MethodSpec.methodBuilder("get" + methodSuffix)
.addModifiers(Modifier.PUBLIC)
.returns(fieldSpec.type)
.addStatement("return this.$N", fieldSpec.name).build();
MethodSpec setter = MethodSpec.methodBuilder("set" + methodSuffix)
.addModifiers(Modifier.PUBLIC)
.returns(selfTypeName)
.addParameter(fieldSpec.type, fieldSpec.name)
.addStatement("this.$N = $N", fieldSpec.name, fieldSpec.name)
.addStatement("return this").build();
result.add(getter);
result.add(setter);
}
);
return result;
}
private MethodSpec buildTryGet(Set> pk) {
MethodSpec.Builder builder = MethodSpec.methodBuilder("tryGet")
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.BOOLEAN);
for (Column> pkColumn : pk) {
builder.addParameter(pkColumn.getJavaClass(), camelize(pkColumn.getName()));
}
String pkColumnNames = pk.stream().map(NamedElement::getName)
.map(this::camelize)
.collect(Collectors.joining(", "));
builder.addStatement("return tryGetByValuesArray($N)", pkColumnNames);
return builder.build();
}
private MethodSpec buildGet(Set> pk) {
MethodSpec.Builder builder = MethodSpec.methodBuilder("get")
.addModifiers(Modifier.PUBLIC);
for (Column> pkColumn : pk) {
builder.addParameter(pkColumn.getJavaClass(), camelize(pkColumn.getName()));
}
String pkColumnNames = pk.stream().map(NamedElement::getName)
.map(this::camelize).collect(Collectors.joining(", "));
builder.addStatement("getByValuesArray($N)", pkColumnNames);
return builder.build();
}
private MethodSpec buildParseResult(
Map> columns, String methodName, boolean isVersionedObject
) {
MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.addParameter(ResultSet.class, "rs")
.addException(SQLException.class);
columns.forEach((name, meta) -> {
String cursorField = camelize(name);
if (BinaryColumn.CELESTA_TYPE.equals(meta.getCelestaType())) {
builder.addStatement("this.$N = null", cursorField);
} else {
builder.beginControlFlow("if (this.$N($S))", "inRec", name);
if (ZonedDateTimeColumn.CELESTA_TYPE.equals(meta.getCelestaType())) {
builder.addStatement(
"$T ts = rs.$N($S, $T.getInstance($T.getTimeZone($S)))",
Timestamp.class, meta.jdbcGetterName(), name, Calendar.class, TimeZone.class, "UTC"
);
builder.beginControlFlow("if ($N != null)", "ts");
builder.addStatement("this.$N = $T.of(ts.toLocalDateTime(), $T.systemDefault())",
cursorField, ZonedDateTime.class, ZoneOffset.class);
builder.endControlFlow();
builder.beginControlFlow("else");
builder.addStatement("this.$N = null", cursorField);
builder.endControlFlow();
} else {
builder.addStatement("this.$N = rs.$N($S)", cursorField, meta.jdbcGetterName(), name);
builder.beginControlFlow("if (rs.$N())", "wasNull");
builder.addStatement("this.$N = null", cursorField);
builder.endControlFlow();
}
builder.endControlFlow();
}
});
if (isVersionedObject) {
builder.addStatement("this.setRecversion(rs.getInt($S))", "recversion");
}
return builder.build();
}
private MethodSpec buildGetFieldValue(Map> columns) {
String nameParam = "name";
MethodSpec.Builder builder = MethodSpec.methodBuilder("_getFieldValue")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED)
.returns(TypeName.OBJECT)
.addParameter(String.class, nameParam)
.beginControlFlow("switch (name)");
for (String columnName : columns.keySet()) {
builder.addStatement("case $S: return this.$N", columnName, camelize(columnName));
}
builder.addStatement("default: return null").endControlFlow();
return builder.build();
}
private MethodSpec buildSetFieldValue(Map> columns) {
String nameParam = "name";
String valueParam = "value";
MethodSpec.Builder builder = MethodSpec.methodBuilder("_setFieldValue")
.addAnnotation(Override.class)
.addModifiers(Modifier.PROTECTED)
.addParameter(String.class, nameParam)
.addParameter(Object.class, valueParam)
.beginControlFlow("switch (name)");
for (Map.Entry> column : columns.entrySet()) {
builder.beginControlFlow("case $S:", column.getKey())
.addStatement("this.$N = ($T) $N", camelize(column.getKey()),
column.getValue().getJavaClass(),
valueParam)
.addStatement("break")
.endControlFlow();
}
builder.addStatement("default:").endControlFlow();
return builder.build();
}
private MethodSpec buildClearBuffer(Map> columns, Set> pk) {
ParameterSpec param = ParameterSpec.builder(boolean.class, "withKeys").build();
MethodSpec.Builder builder = MethodSpec.methodBuilder("_clearBuffer")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(param);
if (!pk.isEmpty()) {
builder.beginControlFlow("if ($N)", param.name);
pk.forEach(c -> builder.addStatement("this.$N = null", camelize(c.getName())));
builder.endControlFlow();
}
columns.entrySet().stream()
.filter(e -> !pk.contains(e.getValue()))
.forEach(e -> builder.addStatement("this.$N = null", camelize(e.getKey())));
return builder.build();
}
private MethodSpec buildCurrentKeyValues(Set> pk) {
ArrayTypeName resultType = ArrayTypeName.of(Object.class);
MethodSpec.Builder builder = MethodSpec.methodBuilder("_currentKeyValues")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class)
.returns(resultType);
String spec = "return new Object[] {"
+ pk.stream().map(c -> "$N").collect(Collectors.joining(", "))
+ "}";
builder.addStatement(spec, pk.stream()
.map(NamedElement::getName).map(this::camelize).toArray());
return builder.build();
}
@SuppressWarnings("rawtypes")
private MethodSpec buildCurrentValues(Map> columns) {
ArrayTypeName resultType = ArrayTypeName.of(Object.class);
MethodSpec.Builder builder = MethodSpec.methodBuilder("_currentValues")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(resultType);
String spec = "return new Object[] {"
+ columns.values().stream().map(c -> "$N").collect(Collectors.joining(", "))
+ "}";
builder.addStatement(spec, columns.values().stream()
.map(ColumnMeta::getName).map(this::camelize).toArray());
return builder.build();
}
private List buildCalcBlobs(Map> columns, String className) {
return columns.entrySet().stream()
.filter(e -> e.getValue() instanceof BinaryColumn)
.map(e ->
MethodSpec.methodBuilder("calc" + CaseUtils.capitalize(camelize(e.getKey())))
.addModifiers(Modifier.PUBLIC)
.addStatement("this.$N = this.calcBlob($S)", camelize(e.getKey()), e.getKey())
.addStatement(
"(($N)this.getXRec()).$N = this.$N.clone()",
className, camelize(e.getKey()), camelize(e.getKey())
).build()
).collect(Collectors.toList());
}
private MethodSpec buildSetAutoIncrement(Map> columns) {
MethodSpec.Builder builder = MethodSpec
.methodBuilder("_setAutoIncrement")
.addModifiers(Modifier.PROTECTED)
.addAnnotation(Override.class);
ParameterSpec param = ParameterSpec.builder(int.class, "val").build();
builder.addParameter(param);
columns.entrySet().stream()
.filter(e -> e.getValue() instanceof IntegerColumn)
.filter(e -> ((IntegerColumn) e.getValue()).getSequence() != null)
.findAny()
.ifPresent(e -> builder.addStatement("this.$N = $N", camelize(e.getKey()), param.name));
return builder.build();
}
private static List buildTriggerRegistration(TypeName selfTypeName) {
ParameterSpec celestaParam = ParameterSpec.builder(
ICelesta.class, "celesta")
.build();
ParameterSpec consumerParam = ParameterSpec.builder(
ParameterizedTypeName.get(ClassName.get(Consumer.class),
WildcardTypeName.supertypeOf(selfTypeName)),
"cursorConsumer")
.build();
return TRIGGER_REGISTRATION_METHOD_TO_TRIGGER_TYPE.entrySet().stream()
.map(e -> {
String methodName = e.getKey();
String triggerType = e.getValue();
return MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(celestaParam)
.addParameter(consumerParam)
.addStatement(
"$N.getTriggerDispatcher().registerTrigger($T.$N, $T.class, $N)",
celestaParam.name, TriggerType.class, triggerType, selfTypeName,
consumerParam.name
).build();
}
).collect(Collectors.toList());
}
private List buildCompileCopying(
GrainElement ge, TypeName selfTypeName, Collection columns, boolean isVersionedObject
) {
final String copyFieldsFromMethodName = "copyFieldsFrom";
final ParameterSpec context = ParameterSpec.builder(CallContext.class, "context").build();
final ParameterizedTypeName columnMetaOfQ = //ColumnMeta>
ParameterizedTypeName.get(ClassName.get(ColumnMeta.class),
WildcardTypeName.subtypeOf(Object.class));
final ParameterSpec fields = ParameterSpec.builder(
ParameterizedTypeName.get(ClassName.get(Collection.class),
WildcardTypeName.subtypeOf(columnMetaOfQ)), "fields"
).build();
MethodSpec.Builder getBufferCopyBuilder = MethodSpec.methodBuilder("_getBufferCopy")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameters(Arrays.asList(context, fields))
.returns(selfTypeName)
.addStatement("final $T result", selfTypeName)
.beginControlFlow("if ($T.isNull($N))", Objects.class, fields.name);
if (ge instanceof ParameterizedView) {
getBufferCopyBuilder.addStatement("result = new $T($N, this.parameters)", selfTypeName, context.name);
} else {
getBufferCopyBuilder.addStatement("result = new $T($N)", selfTypeName, context.name);
}
getBufferCopyBuilder.endControlFlow()
.beginControlFlow("else");
if (ge instanceof ParameterizedView) {
getBufferCopyBuilder.addStatement(
"result = new $T($N, this.parameters, $N.toArray(new $T[0]))",
selfTypeName, context.name, fields.name, columnMetaOfQ
);
} else {
getBufferCopyBuilder.addStatement(
"result = new $T($N, $N.toArray(new $T[0]))",
selfTypeName, context.name, fields.name, columnMetaOfQ
);
}
getBufferCopyBuilder.endControlFlow()
.addStatement("result.$N(this)", copyFieldsFromMethodName)
.addStatement("return result");
MethodSpec getBufferCopy = getBufferCopyBuilder.build();
MethodSpec.Builder copyFieldsFromBuilder = MethodSpec.methodBuilder(copyFieldsFromMethodName)
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(BasicCursor.class, "c");
copyFieldsFromBuilder.addStatement("$T from = ($T)c", selfTypeName, selfTypeName);
columns.forEach(c ->
copyFieldsFromBuilder.addStatement("this.$N = from.$N", camelize(c), camelize(c))
);
if (isVersionedObject) {
copyFieldsFromBuilder.addStatement("this.setRecversion(from.getRecversion())");
}
return Arrays.asList(getBufferCopy, copyFieldsFromBuilder.build());
}
private static MethodSpec buildIterator(TypeName selfTypeName) {
TypeName iteratorTypeName = ParameterizedTypeName.get(ClassName.get(Iterator.class), selfTypeName);
TypeName cursorIterator = ParameterizedTypeName.get(ClassName.get(CursorIterator.class), selfTypeName);
return MethodSpec.methodBuilder("iterator")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(iteratorTypeName)
.addStatement("return new $T(this)", cursorIterator)
.build();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy