Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.fluxtion.compiler.generation.targets.JavaSourceGenerator Maven / Gradle / Ivy
/*
* Copyright (c) 2019-2025 gregory higgins.
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* .
*/
package com.fluxtion.compiler.generation.targets;
import com.fluxtion.compiler.EventProcessorConfig;
import com.fluxtion.compiler.EventProcessorConfig.DISPATCH_STRATEGY;
import com.fluxtion.compiler.NodeDispatchTable;
import com.fluxtion.compiler.builder.filter.FilterDescription;
import com.fluxtion.compiler.generation.GenerationContext;
import com.fluxtion.compiler.generation.model.*;
import com.fluxtion.compiler.generation.util.ClassUtils;
import com.fluxtion.compiler.generation.util.NaturalOrderComparator;
import com.fluxtion.runtime.EventProcessorContext;
import com.fluxtion.runtime.annotations.ExportService;
import com.fluxtion.runtime.annotations.OnEventHandler;
import com.fluxtion.runtime.annotations.OnParentUpdate;
import com.fluxtion.runtime.annotations.builder.FluxtionIgnore;
import com.fluxtion.runtime.audit.Auditor;
import com.fluxtion.runtime.audit.EventLogManager;
import com.fluxtion.runtime.callback.ExportFunctionAuditEvent;
import com.fluxtion.runtime.event.Event;
import com.fluxtion.runtime.input.EventFeed;
import com.fluxtion.runtime.input.SubscriptionManager;
import com.fluxtion.runtime.node.ForkedTriggerTask;
import com.fluxtion.runtime.node.MutableEventProcessorContext;
import lombok.Getter;
import net.vidageek.mirror.dsl.Mirror;
import net.vidageek.mirror.list.dsl.MirrorList;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import static com.fluxtion.compiler.generation.targets.JavaGenHelper.mapPrimitiveToWrapper;
import static com.fluxtion.compiler.generation.targets.JavaGenHelper.mapWrapperToPrimitive;
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava;
/**
* @author Greg Higgins
*/
public class JavaSourceGenerator {
private static final String s4 = " ";
private static final String s8 = " ";
private static final String s12 = " ";
private static final String s16 = " ";
private static final String s20 = " ";
private static final String s24 = " ";
/**
* String representation of life-cycle callback methods for initialise,
* sorted in call order, in a list.
*/
@Getter
private final ArrayList initialiseMethodList;
/**
* String representation of life-cycle callback methods for start,
* sorted in call order, in a list.
*/
private final ArrayList startMethodList;
/**
* String representation of life-cycle callback methods for startComplete,
* sorted in call order, in a list.
*/
private final ArrayList startCompleteMethodList;
/**
* String representation of life-cycle callback methods for initialise,
* sorted in call order, in a list.
*/
private final ArrayList stopMethodList;
/**
* String representation of life-cycle callback methods for end of batch,
* sorted in call order.
*/
@Getter
private final ArrayList batchEndMethodList;
/**
* String representation of life-cycle callback methods for after event
* processing, sorted in call order.
*/
@Getter
private final ArrayList eventEndMethodList;
/**
* String representation of life-cycle callback methods for batch pause,
* sorted in call order.
*/
@Getter
private final ArrayList batchPauseMethodList;
/**
* String representation of life-cycle callback methods for tearDown, sorted
* in call order.
*/
@Getter
private final ArrayList tearDownMethodList;
/**
* String representation of node declarations as a list.
*/
@Getter
private final ArrayList nodeDeclarationList;
private final ArrayList importList;
/**
* String representation of the initial member assignments for each node
*/
@Getter
private final ArrayList nodeMemberAssignmentList;
/**
* String representation of public nodes as a list.
*/
private final ArrayList publicNodeIdentifierList;
private final SimpleEventProcessorModel model;
/**
* use reflection to assign private members
*/
private final boolean assignPrivateMembers;
private final StringBuilder nodeDecBuilder = new StringBuilder(5 * 1000 * 1000);
private final HashMap importMap = new HashMap<>();
private final StringBuilder ct = new StringBuilder(5 * 1000 * 1000);
private final StringBuilder switchF = new StringBuilder(5 * 1000 * 1000);
private final StringBuilder filteredMethodBuffer = new StringBuilder(5 * 1000 * 1000);
/**
* Create an if based dispatch using instanceof operator
*/
private final boolean instanceOfDispatch;
private final EventProcessorConfig eventProcessorConfig;
/**
* String representation of life-cycle callback methods for initialise,
* sorted in call order.
*/
@Getter
private String initialiseMethods;
/**
* String representation of life-cycle callback methods for start,
* sorted in call order.
*/
@Getter
private String startMethods;
/**
* String representation of life-cycle callback methods for startComplete,
* sorted in call order.
*/
@Getter
private String startCompleteMethods;
/**
* String representation of life-cycle callback methods for initialise,
* sorted in call order.
*/
@Getter
private String stopMethods;
/**
* String representation of life-cycle callback methods for end of batch,
* sorted in call order.
*/
@Getter
private String batchEndMethods;
/**
* String representation of life-cycle callback methods for end of batch,
* sorted in call order.
*/
@Getter
private String eventEndMethods;
/**
* String representation of life-cycle callback methods for batch pause,
* sorted in call order.
*/
@Getter
private String batchPauseMethods;
/**
* String representation of life-cycle callback methods for tearDown, sorted
* in call order.
*/
@Getter
private String tearDownMethods;
/**
* String representation of node declarations.
*/
@Getter
private String nodeDeclarations;
/**
* String representation of dirty flag.
*/
@Getter
private String dirtyFlagDeclarations;
/**
* String representation of looking up dirty flag by instance
*/
@Getter
private String dirtyFlagLookup;
/**
* String representation of updating dirty flag by instance
*/
@Getter
private String dirtyFlagUpdate;
/**
* String representation of all dirty flag reset to false.
*/
@Getter
private String resetDirtyFlags;
/**
*
*/
@Getter
private String guardCheckMethods;
/**
* String representation of filter constants declarations.
*/
@Getter
private String filterConstantDeclarations;
/**
* String representation of the initial member assignments for each node
*/
@Getter
private String nodeMemberAssignments;
@Getter
private String dispatchOnlyNodeMemberAssignments;
/**
* String representation of top level event dispatch, branches on event type
* -- GETTER --
* String representation of top level event dispatch
* public void onEvent(com.fluxtion.api.event.Event event)
*
*
* public void handleEvent([specific event] event)
* {
* [eventHandlers]
*
* }
*
*
* @return top level event dispatch string
*/
@Getter
private String eventDispatch;
/**
* String representing the delegated event handling methods for a specific
* event type, will include branching based on filterId.
* -- GETTER --
* String representation of java code handling subclass of
*
* , with support for specific
* dispatch based upon
*
* filterID. If
* inlining is false the following output will be produced:
*
*
* public void handleEvent([specific event] event)
* {
* [eventHandlers]
*
* }
*
*
* @return type specific event dispatch code
*/
@Getter
private String eventHandlers;
/**
* String representing filtered event handler methods
*/
@Getter
private String filterEventHandlers;
/**
* String representing exported events
*/
@Getter
private String exportedMethods;
/**
* determines whether separate delegate eventHandling methods are generated.
*/
private boolean isInlineEventHandling;
/**
* String representing event audit dispatch
*/
private String eventAuditDispatch;
/**
* String representing event audit dispatch
*/
@Getter
private String assignNodeField;
/**
* are any auditors registered for this SEP
*/
private boolean auditingEvent;
private boolean auditingInvocations;
private String auditMethodString;
private String additionalInterfaces;
@Getter
private String javaDocEventClassList;
@Getter
private String forkDeclarations;
@Getter
private String resetForkTasks;
final Set> nonUserClass;
private String forkedAssignments;
private boolean bufferDispatch = false;
public JavaSourceGenerator(
SimpleEventProcessorModel model, EventProcessorConfig eventProcessorConfig) {
this.isInlineEventHandling = eventProcessorConfig.isInlineEventHandling();
this.assignPrivateMembers = eventProcessorConfig.isAssignPrivateMembers();
this.instanceOfDispatch = eventProcessorConfig.getDispatchStrategy() == DISPATCH_STRATEGY.INSTANCE_OF;
this.model = model;
this.eventProcessorConfig = eventProcessorConfig;
this.eventHandlers = "";
initialiseMethodList = new ArrayList<>();
startMethodList = new ArrayList<>();
startCompleteMethodList = new ArrayList<>();
stopMethodList = new ArrayList<>();
batchEndMethodList = new ArrayList<>();
batchPauseMethodList = new ArrayList<>();
eventEndMethodList = new ArrayList<>();
tearDownMethodList = new ArrayList<>();
nodeDeclarationList = new ArrayList<>();
nodeMemberAssignmentList = new ArrayList<>();
publicNodeIdentifierList = new ArrayList<>();
importList = new ArrayList<>();
nonUserClass = new HashSet<>();
nonUserClass.add(EventLogManager.class);
}
private static boolean hasIdField(Class e) {
return false;
}
public void buildSourceModel() throws Exception {
checkAuditInvocations();
buildMethodSource(model.getInitialiseMethods(), initialiseMethodList);
buildMethodSource(model.getStartMethods(), startMethodList);
buildMethodSource(model.getStartCompleteMethods(), startCompleteMethodList);
buildMethodSource(model.getStopMethods(), stopMethodList);
buildMethodSource(model.getBatchPauseMethods(), batchPauseMethodList);
buildForkAwareMethodSource(model.getEventEndMethods(), eventEndMethodList);
buildMethodSource(model.getBatchEndMethods(), batchEndMethodList);
buildMethodSource(model.getTearDownMethods(), tearDownMethodList);
addDefaultImports();
buildNodeDeclarations();
buildDirtyFlags();
buildForkDeclarations();
buildFilterConstantDeclarations();
buildMemberAssignments();
buildNodeRegistrationListeners();
buildNodeFieldAssigner();
buildEventDispatch();
initialiseMethods = "";
boolean firstLine = true;
for (String initialiseMethod : initialiseMethodList) {
initialiseMethods += (firstLine ? "" : "\n") + initialiseMethod;
firstLine = false;
}
startMethods = String.join("\n", startMethodList);
startCompleteMethods = String.join("\n", startCompleteMethodList);
stopMethods = String.join("\n", stopMethodList);
batchPauseMethods = "";
firstLine = true;
for (String initialiseMethod : batchPauseMethodList) {
batchPauseMethods += (firstLine ? "" : "\n") + initialiseMethod;
firstLine = false;
}
batchEndMethods = "";
firstLine = true;
for (String initialiseMethod : batchEndMethodList) {
batchEndMethods += (firstLine ? "" : "\n") + initialiseMethod;
firstLine = false;
}
eventEndMethods = "";
firstLine = true;
for (String initialiseMethod : eventEndMethodList) {
eventEndMethods += (firstLine ? "" : "\n") + initialiseMethod;
firstLine = false;
}
tearDownMethods = "";
for (String initialiseMethod : tearDownMethodList) {
tearDownMethods += initialiseMethod + "\n";
}
tearDownMethods = StringUtils.chomp(tearDownMethods);
nodeMemberAssignments = "";
dispatchOnlyNodeMemberAssignments = "";
final StringBuilder memberAssignments = new StringBuilder(200 * nodeMemberAssignmentList.size());
firstLine = true;
for (String initialiseMethod : nodeMemberAssignmentList) {
memberAssignments.append(firstLine ? " " : "\n ").append(initialiseMethod);
firstLine = false;
}
if (model.isDispatchOnlyVersion()) {
dispatchOnlyNodeMemberAssignments = " @Override\n" +
" public final void assignMembers(Map memberMap) {\n" +
" memberMap.forEach((k, v) -> setField(k, v));\n" +
" " + forkedAssignments +
" " + memberAssignments +
" if(subscriptionManager != null){\n" +
" subscriptionManager.setSubscribingEventProcessor(this);\n" +
" }\n" +
" if(context != null) {\n" +
" context.setEventProcessorCallback(this);\n" +
" }\n" +
" }\n\n";
dispatchOnlyNodeMemberAssignments += " @Override\n" +
" public final void assignMembers(Map memberMap, Map contextMap) {\n" +
" memberMap.forEach((k, v) -> setField(k, v));\n" +
" if(context != null) {\n" +
" context.replaceMappings(contextMap);\n" +
" }\n" +
" " + forkedAssignments +
" " + memberAssignments +
" if(subscriptionManager != null){\n" +
" subscriptionManager.setSubscribingEventProcessor(this);\n" +
" }\n" +
" if(context != null) {\n" +
" context.setEventProcessorCallback(this);\n" +
" }\n" +
" }\n";
} else {
nodeMemberAssignments = memberAssignments.toString();
}
}
private void checkAuditInvocations() {
List listenerFields = model.getNodeRegistrationListenerFields();
auditingInvocations = listenerFields.stream()
.map(Field::getInstance)
.filter(Auditor.class::isInstance)
.map(Auditor.class::cast)
.anyMatch(Auditor::auditInvocations);
}
private void buildForkDeclarations() {
String forkWrapperClass = getClassTypeName(ForkedTriggerTask.class);
forkDeclarations = "";
forkedAssignments = "";
if (model.getTriggerOnlyCallBacks().isEmpty()) {
return;
}
if (model.isDispatchOnlyVersion()) {
forkDeclarations = model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(c -> "private " + forkWrapperClass + " " + c.forkVariableName() + ";")
.collect(Collectors.joining("\n", "//Forked declarations\n", "\n"));
forkedAssignments = model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(c -> c.forkVariableName()
+ " = new " + forkWrapperClass + "(" + c.invokeLambdaString()
+ ", \"" + c.variableName + "\" );")
.collect(Collectors.joining("\n", "\n//Forked assignment\n", "\n"));
} else {
forkDeclarations = model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(c -> "private final " + forkWrapperClass + " " + c.forkVariableName()
+ " = new " + forkWrapperClass + "(" + c.invokeLambdaString()
+ ", \"" + c.variableName + "\" );")
.collect(Collectors.joining("\n", "\n//Forked declarations\n", "\n"));
}
resetForkTasks = model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(c -> c.forkVariableName() + ".reinitialize();")
.collect(Collectors.joining("\n"));
}
private void buildMethodSource(List methodList, List methodSourceList) {
for (CbMethodHandle method : methodList) {
final String methodString = String.format("%8s%s.%s();", "", method.variableName, method.method.getName());
methodSourceList.add(methodString);
}
}
private void buildForkAwareMethodSource(List methodList, List methodSourceList) {
Set forkedTriggers = model.getForkedTriggerInstances();
resetForkTasks = model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(c -> c.forkVariableName() + ".reinitialize();")
.collect(Collectors.joining("\n"));
for (CbMethodHandle method : methodList) {
String waitForJoin = "";
if (forkedTriggers.contains(method.instance)) {
waitForJoin = method.forkVariableName() + ".afterEvent();\n";
}
final String methodString = String.format("%8s%s.%s();", "", method.variableName, method.method.getName());
methodSourceList.add(waitForJoin + methodString);
}
methodSourceList.add(resetForkTasks);
}
private void buildDirtyFlags() {
dirtyFlagDeclarations = "";
resetDirtyFlags = "";
dirtyFlagLookup = "";
dirtyFlagUpdate = "";
guardCheckMethods = "";
final ArrayList values = new ArrayList<>(model.getDirtyFieldMap().values());
NaturalOrderComparator comparator = new NaturalOrderComparator<>();
values.sort((o1, o2) -> comparator.compare(o1.name, o2.name));
for (DirtyFlag flag : values) {
dirtyFlagDeclarations += String.format("%4sprivate boolean %s = false;%n", "", flag.name);
resetDirtyFlags += String.format("%8s%s = false;%n", "", flag.name);
}
for (DirtyFlag flag : values) {
if (flag.requiresInvert) {
dirtyFlagDeclarations += String.format("%4sprivate boolean not%s = false;%n", "", flag.name);
resetDirtyFlags += String.format("%8snot%s = false;%n", "", flag.name);
}
}
List> sortedDirtyFlags = model.getDirtyFieldMap().entrySet().stream()
.sorted(Comparator.comparing(e -> e.getKey().getName()))
.collect(Collectors.toList());
sortedDirtyFlags
.forEach(e -> {
dirtyFlagLookup += String.format("%12sdirtyFlagSupplierMap.put(%s, () -> %s);", "",
e.getKey().getName(), e.getValue().name);
});
sortedDirtyFlags
.forEach(e -> {
dirtyFlagUpdate += String.format("%12sdirtyFlagUpdateMap.put(%s, (b) -> %s = b);", "",
e.getKey().getName(), e.getValue().name);
});
//build guard methods
Set forkedTriggers = model.getForkedTriggerInstances();
model.getNodeFields().forEach(field -> {
String guardConditions = model.getNodeGuardConditions(field.instance).stream()
.map(d -> (d.isRequiresInvert() ? "not" : "") + d.getName())
.collect(Collectors.joining(" |\n"));
String forkedReturn = model.getNodeGuardConditions(field.instance).stream()
.filter(f -> {
return forkedTriggers.contains(f.node.getInstance());
})
.map(d -> {
List updateListenerCbList = model.getParentUpdateListenerMethodMap().get(d.getNode().getInstance());
//child callbacks
boolean unguarded = false;
StringBuilder sbUnguarded = new StringBuilder();
StringBuilder guarded = new StringBuilder();
String parentVar = d.getNode().getName();
for (CbMethodHandle cbMethod : updateListenerCbList) {
if (!cbMethod.method.getAnnotation(OnParentUpdate.class).guarded()) {
if (auditingInvocations) {
sbUnguarded.append(s24).append("auditInvocation(")
.append(cbMethod.variableName)
.append(", \"").append(cbMethod.variableName).append("\"")
.append(", \"").append(cbMethod.method.getName()).append("\"")
.append(", \"\"")
.append(");\n");
}
sbUnguarded.append(s20).append(cbMethod.variableName).append(".").append(cbMethod.method.getName()).append("(").append(parentVar).append(");\n");
} else {
if (auditingInvocations) {
guarded.append(s24).append("auditInvocation(")
.append(cbMethod.variableName)
.append(", \"").append(cbMethod.variableName).append("\"")
.append(", \"").append(cbMethod.method.getName()).append("\"")
.append(", \"\"")
.append(");\n");
}
guarded.append(s24).append(cbMethod.variableName).append(".").append(cbMethod.method.getName()).append("(").append(parentVar).append(");\n");
}
}
return d.getName() + " = " + d.getForkedName() + ".afterEvent();\n"
+ "if(" + d.getName() + "){\n"
+ guarded + "\n"
+ "}"
+ sbUnguarded;
})
.collect(Collectors.joining("\n"));
if (!model.getNodeGuardConditions(field.instance).isEmpty()) {
guardCheckMethods += String.format("%1$4sprivate boolean guardCheck_%2$s() {%n" +
forkedReturn +
"%1$8sreturn %3$s;" +
"}%n", "", field.getName(), guardConditions);
}
}
);
dirtyFlagDeclarations = StringUtils.chomp(dirtyFlagDeclarations);
resetDirtyFlags = StringUtils.chomp(resetDirtyFlags);
}
private String getClassTypeName(Class> clazzName) {
return getClassName(clazzName.getCanonicalName());
}
private String getClassName(String clazzName) {
clazzName = model.getMappedClass(clazzName);
String[] split = clazzName.split("\\.");
String ret = clazzName;
if (split.length > 1) {
String simpleName = split[split.length - 1];
String pkgName = clazzName.replace("." + simpleName, "");
ret = simpleName;
if (clazzName.startsWith("java.lang")
|| (GenerationContext.SINGLETON != null && GenerationContext.SINGLETON.getPackageName().equals(pkgName))) {
//ignore java.lang
} else if (importMap.containsKey(simpleName)) {
if (!importMap.get(simpleName).equalsIgnoreCase(clazzName)) {
ret = clazzName;
}
} else {
importMap.put(simpleName, clazzName);
importList.add(clazzName);
}
}
return ret;
}
private void buildNodeDeclarations() {
nodeDeclarations = "";
final StringBuilder declarationBuilder = new StringBuilder(2000);
final StringBuilder fqnBuilder = new StringBuilder(500);
boolean firstLine = true;
if (assignPrivateMembers) {
declarationBuilder.append(s4).append("final net.vidageek.mirror.dsl.Mirror constructor = new net.vidageek.mirror.dsl.Mirror();\n");
}
for (Field field : model.getTopologicallySortedNodeFields()) {
Class> fieldClass = field.instance.getClass();
boolean dispatchOnlyVersion = model.isDispatchOnlyVersion();
boolean isUserClass = !nonUserClass.contains(fieldClass);
final String access = (field.publicAccess | (dispatchOnlyVersion & isUserClass)) ? "public" : "private";
fqnBuilder.append(getClassName(field.fqn));
boolean syntheticConstructor = false;
try {
fieldClass.getConstructor();
} catch (Exception e) {
syntheticConstructor = true;
}
StringBuilder declarationRoot = declarationBuilder.append(s4).append(access).append(" transient ");
if (!dispatchOnlyVersion) {
declarationRoot.append(" final ");
}
declarationRoot.append(fqnBuilder)
.append(model.getFieldSerializer().buildTypeDeclaration(field, this::getClassTypeName))
.append(" ")
.append(field.name);
if (assignPrivateMembers && syntheticConstructor) {
//new constructor.on(clazz).invoke().constructor().bypasser();
declarationRoot
.append(" = constructor.on(").append(fqnBuilder).append(".class).invoke().constructor().bypasser();");
} else {
List constructorArgs = model.constructorArgs(field.instance);
if (String.class.isAssignableFrom(fieldClass)) {
declarationRoot
.append(" = ").append("\"")
.append(escapeJava((String) field.instance))
.append("\";");
} else if (Integer.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = ").append(field.instance).append(";");
} else if (Float.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = ").append(field.instance).append("f;");
} else if (Double.class.isAssignableFrom(fieldClass)) {
if (!Double.isNaN((double) field.instance)) {
declarationRoot.append(" = ").append(field.instance).append("d;");
} else {
declarationRoot.append(" = Double.NaN;");
}
} else if (Long.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = ").append(field.instance).append("L;");
} else if (Short.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = (short)").append(field.instance).append(";");
} else if (Byte.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = (byte)").append(field.instance).append(";");
} else if (Character.class.isAssignableFrom(fieldClass)) {
declarationRoot.append(" = '").append(field.instance).append("';");
} else if (dispatchOnlyVersion && isUserClass) {
declarationRoot.append(" = null;");
} else {
String generic = field.isGeneric() ? "<>" : "";
String args = constructorArgs.stream().map(Field.MappedField::value).collect(Collectors.joining(", "));
declarationRoot.append(" = new ").append(fqnBuilder).append(generic + "(" + args + ");");
}
}
final String declaration = declarationBuilder.toString();
nodeDeclarationList.add(declaration);
nodeDecBuilder.append(firstLine ? "" : "\n").append(declaration);
firstLine = false;
if (field.publicAccess) {
publicNodeIdentifierList.add(field.name);
}
fqnBuilder.delete(0, fqnBuilder.length());
declarationBuilder.delete(0, declarationBuilder.length());
}
nodeDeclarations = nodeDecBuilder.toString();
nodeDecBuilder.delete(0, nodeDecBuilder.length());
}
private void buildFilterConstantDeclarations() {
filterConstantDeclarations = "";
boolean firstLine = true;
ArrayList tmp = new ArrayList<>(model.getFilterDescriptionList());
tmp.sort((FilterDescription o1, FilterDescription o2) -> {
return o1.value - o2.value;
});
HashMap filterVariableMap = new HashMap<>();
for (FilterDescription filterDescription : tmp) {
if (filterDescription.variableName != null) {
int value = filterDescription.value;
String variableName = filterDescription.variableName;
if (filterVariableMap.containsKey(variableName)) {
int mappedValue = filterVariableMap.get(variableName);
if (mappedValue != value) {
throw new IllegalStateException("two mappings for the same filter"
+ " constant '" + variableName + "'");
} else {
continue;
}
}
filterVariableMap.put(variableName, value);
final String declaration = String.format(" "
+ "public static final int %s = %d;",
variableName, value);
filterConstantDeclarations += (firstLine ? "" : "\n") + declaration;
firstLine = false;
}
}
}
private void buildEventDispatch() {
generateClassBasedDispatcher();
generateExportMethodDispatcher();
generateEventBufferedDispatcher();
addEventAsJavaDoc();
if (auditingEvent) {
eventHandlers += auditMethodString;
}
}
private void addEventAsJavaDoc() {
javaDocEventClassList = ClassUtils.sortClassHierarchy(model.getHandlerOnlyDispatchMap().keySet()).stream()
.map(Class::getCanonicalName)
.collect(Collectors.joining(
"\n* ",
"* ",
" "
));
}
private void generateEventBufferedDispatcher() {
eventHandlers += "\n //EVENT BUFFERING - START\n";
if (!eventProcessorConfig.isSupportBufferAndTrigger()) {
eventHandlers += " public void bufferEvent(Object event) {" +
" throw new UnsupportedOperationException(\"bufferEvent not supported\");\n" +
" }\n";
eventHandlers += "\n public void triggerCalculation() {" +
" throw new UnsupportedOperationException(\"triggerCalculation not supported\");\n" +
" }\n";
eventHandlers += " //EVENT BUFFERING - END\n\n";
return;
}
bufferDispatch = true;
filteredMethodBuffer.setLength(0);
boolean patternSwitch = eventProcessorConfig.getDispatchStrategy() == DISPATCH_STRATEGY.PATTERN_MATCH;
StringBuilder noTriggerDispatch = new StringBuilder(" public void bufferEvent(Object event){\n" +
" buffering = true;\n");
//build buffer event method
String bufferEvents = "";
if (instanceOfDispatch) {
bufferEvents = "";
} else if (!patternSwitch) {
noTriggerDispatch.append(" switch (event.getClass().getName()) {\n");
} else if (patternSwitch) {
noTriggerDispatch.append(" switch (event) {\n");
}
Map, Map>> handlerOnlyDispatchMap = model.getHandlerOnlyDispatchMap();
//sort class so repeatable
boolean prev = isInlineEventHandling;
isInlineEventHandling = true;
//sort the classes and then loop through the sorted list
List> sortedClasses = ClassUtils.sortClassHierarchy(handlerOnlyDispatchMap.keySet());
sortedClasses.remove(ExportFunctionMarker.class);
String elsePrefix = "if";
for (Class> eventId : sortedClasses) {
Map> m = handlerOnlyDispatchMap.get(eventId);
String className = getClassName(eventId.getCanonicalName());
if (instanceOfDispatch) {
String eventClassName = mapPrimitiveToWrapper(eventId).getName().replace("$", ".");
noTriggerDispatch.append(String.format("%12s (event instanceof %s) {%n", elsePrefix, eventClassName));
elsePrefix = "else if";
noTriggerDispatch.append(String.format("%16s%s typedEvent = (%s)event;%n", "", className, className));
} else if (patternSwitch) {
noTriggerDispatch.append(String.format("%12scase %s typedEvent -> {", "", className));
} else {
noTriggerDispatch.append(String.format("%12scase (\"%s\"):{%n", "", eventId.getName()));
noTriggerDispatch.append(String.format("%16s%s typedEvent = (%s)event;%n", "", className, className));
}
noTriggerDispatch.append(buildFilteredDispatch(m, Collections.emptyMap(), eventId));
if (instanceOfDispatch) {
noTriggerDispatch.append(String.format("%12s}%n", ""));
} else if (patternSwitch) {
noTriggerDispatch.append(String.format("%12s}%n", ""));
} else {
noTriggerDispatch.append(String.format("%16sbreak;%n", ""));
noTriggerDispatch.append(String.format("%12s}%n", ""));
}
}
if (!instanceOfDispatch) {
if (!handlerOnlyDispatchMap.keySet().contains(Object.class) && patternSwitch) {
noTriggerDispatch.append("default -> {}");
}
noTriggerDispatch.append(String.format("%8s}%n", ""));
}
noTriggerDispatch.append(String.format("%4s}%n", ""));
bufferEvents += noTriggerDispatch.toString();
//build buffered dispatch trigger
Map> cbMap = new HashMap<>();
List triggerOnlyCallBacks = model.getTriggerOnlyCallBacks();
if (!triggerOnlyCallBacks.isEmpty()) {
cbMap.put(FilterDescription.DEFAULT_FILTER, triggerOnlyCallBacks);
}
Map> cbMapPostDispatch = new HashMap<>();
cbMapPostDispatch.put(FilterDescription.DEFAULT_FILTER, model.getAllPostEventCallBacks());
String dispatchString = buildFilteredSwitch(cbMap, cbMapPostDispatch, Object.class, false, true);
String bufferedTrigger = "\n public void triggerCalculation(){\n" +
" buffering = false;\n" +
" String typedEvent = \"No event information - buffered dispatch\";\n" +
(dispatchString == null ? "" : dispatchString) +
" afterEvent();\n" +
"\n }\n";
bufferDispatch = false;
isInlineEventHandling = prev;
eventHandlers += bufferEvents;
eventHandlers += filteredMethodBuffer;
eventHandlers += bufferedTrigger;
eventHandlers += " //EVENT BUFFERING - END\n\n";
}
/**
* generates the implementation of the onEvent method, and writes to the
* eventDispatch or debugEventDispatch string. This is the top level method
* called, and dispatches methods internally. The generated method, switches
* on class type, casts to the correct object and then invokes specific
* event handling for that type.
*/
private void generateClassBasedDispatcher() {
boolean patternSwitch = eventProcessorConfig.getDispatchStrategy() == DISPATCH_STRATEGY.PATTERN_MATCH;
String dispatchStringNoId = " switch (event.getClass().getName()) {\n";
if (instanceOfDispatch) {
dispatchStringNoId = "";
} else if (patternSwitch) {
dispatchStringNoId = " switch (event) {\n";
}
Map, Map>> dispatchMap = model.getDispatchMap();
Map, Map>> postDispatchMap = model.getPostDispatchMap();
Set> keySet = dispatchMap.keySet();
HashSet> classSet = new HashSet<>(keySet);
classSet.addAll(postDispatchMap.keySet());
classSet.remove(ExportFunctionMarker.class);
List> clazzList = ClassUtils.sortClassHierarchy(classSet);
String elsePrefix = "if";
for (Class> eventId : clazzList) {
String className = getClassName(eventId.getCanonicalName());
if (instanceOfDispatch) {
String eventClassName = mapPrimitiveToWrapper(eventId).getName().replace("$", ".");
dispatchStringNoId += String.format("%12s (event instanceof %s) {%n", elsePrefix, eventClassName);
elsePrefix = "else if";
dispatchStringNoId += String.format("%16s%s typedEvent = (%s)event;%n", "", className, className);
} else if (patternSwitch) {
dispatchStringNoId += String.format("%12scase %s typedEvent -> ", "", className);
} else {
dispatchStringNoId += String.format("%12scase (\"%s\"):{%n", "", eventId.getName());
dispatchStringNoId += String.format("%16s%s typedEvent = (%s)event;%n", "", className, className);
}
Map> cbMap = dispatchMap.get(eventId);
Map> cbMapPostEvent = postDispatchMap.get(eventId);
dispatchStringNoId += buildFilteredDispatch(cbMap, cbMapPostEvent, eventId);
if (instanceOfDispatch) {
dispatchStringNoId += String.format("%12s}%n", "");
} else if (patternSwitch) {
// dispatchStringNoId += String.format("%n");
} else {
dispatchStringNoId += String.format("%16sbreak;%n", "");
dispatchStringNoId += String.format("%12s}%n", "");
}
}
//default handling
if (!instanceOfDispatch) {
if (keySet.contains(Object.class) && !patternSwitch) {
dispatchStringNoId += String.format("%12sdefault :{%n", "");
dispatchStringNoId += String.format("%16shandleEvent(event);%n", "");
dispatchStringNoId += String.format("%12s}%n", "");
} else if (!keySet.contains(Object.class) && patternSwitch) {
dispatchStringNoId += "default -> {unKnownEventHandler(event);}";
}
dispatchStringNoId += String.format("%8s}%n", "");
} else if (!keySet.contains(Object.class)) {
dispatchStringNoId += String.format("%12selse{ %n", "");
dispatchStringNoId += String.format("%12sunKnownEventHandler(event); %n", "");
dispatchStringNoId += String.format("%12s}%n", "");
}
if (isInlineEventHandling) {
dispatchStringNoId += String.format("%8safterEvent();%n", "");
}
//build a noIddispatchString - just copy method above and only
//process .ID free events.
eventDispatch = dispatchStringNoId + "\n";
eventHandlers += " //EVENT DISPATCH - END\n";
if (!(filteredMethodBuffer.length() == 0)) {
filteredMethodBuffer.insert(0, "\n //FILTERED DISPATCH - START");
filteredMethodBuffer.append(" //FILTERED DISPATCH - END\n");
}
eventHandlers += filteredMethodBuffer.toString();
}
private void generateExportMethodDispatcher() {
Map> eventDispatch = model.getDispatchMap().get(ExportFunctionMarker.class);
Map> postDispatch = model.getPostDispatchMap().get(ExportFunctionMarker.class);
final String audit;
List listenerFields = model.getNodeRegistrationListenerFields();
if (listenerFields != null && !listenerFields.isEmpty()) {
audit = "beforeServiceCall(\"&&FUNC&&\");\n" +
"ExportFunctionAuditEvent typedEvent = functionAudit;\n";
} else {
audit = "beforeServiceCall(\"&&FUNC&&\");\n";
}
if (eventDispatch != null) {
eventHandlers += "\n//EXPORTED SERVICE FUNCTIONS - START\n";
List list = new ArrayList<>(eventDispatch.keySet());
list.sort(Comparator.comparing(FilterDescription::getStringValue));
list.forEach(f -> {
String exportAudit = "";
if (f.getExportFunction() != null) {
exportAudit = f.getExportFunction().toGenericString();
} else {
return;
}
StringBuilder sb = new StringBuilder(f.getStringValue() + "{\n")
.append(audit.replace("&&FUNC&&", exportAudit));
buildDispatchForCbMethodHandles(eventDispatch.get(f), sb);
buildPostDispatchForCbMethodHandles(postDispatch.get(f), sb);
sb.append("afterServiceCall();\n");
sb.append(f.getStringValue().contains("void") ? "}\n" : "return true;}\n");
eventHandlers += sb.toString();
});
eventHandlers += "//EXPORTED SERVICE FUNCTIONS - END\n";
}
}
private String buildFilteredSwitch(Map> cbMap,
Map> cbMapPostEvent, Class eventClass,
boolean intFilter, boolean noFilter) {
Set filterIdSet = new HashSet<>(cbMap.keySet());
filterIdSet.addAll(cbMapPostEvent.keySet());
ArrayList clazzList = new ArrayList<>(filterIdSet);
clazzList.sort((FilterDescription o1, FilterDescription o2) -> {
int ret = o1.value - o2.value;
if (!o1.isIntFilter && !o2.isIntFilter) {
ret = o1.stringValue.compareTo(o2.stringValue);
} else if (o1.isIntFilter && !o2.isIntFilter) {
ret = 1;
} else if (!o1.isIntFilter && o2.isIntFilter) {
ret = -1;
}
return ret;
});
switchF.delete(0, switchF.length());
for (FilterDescription filterDescription : clazzList) {
//INVOKE TARGET
InvokerFilterTarget invokerTarget = new InvokerFilterTarget();
invokerTarget.methodName = JavaGenHelper.generateFilteredDispatchMethodName(filterDescription, bufferDispatch);
invokerTarget.filterDescription = filterDescription;
invokerTarget.eventClassName = filterDescription.eventClass == null ? null : getClassTypeName(filterDescription.eventClass);
invokerTarget.stringMapName = JavaGenHelper.generateFilteredDispatchMap(filterDescription);
invokerTarget.intMapName = JavaGenHelper.generateFilteredDispatchMap(filterDescription);
if (intFilter == filterDescription.isIntFilter || (noFilter)) {
boolean isDefaultFilter = filterDescription == FilterDescription.DEFAULT_FILTER;
boolean isNoFilter = filterDescription == FilterDescription.NO_FILTER || filterDescription == FilterDescription.INVERSE_FILTER;
String filterValue = intFilter ? filterDescription.value + "" : "\"" + filterDescription.stringValue + "\"";
String filterVariable = filterDescription.variableName;
List cbList = cbMap.get(filterDescription);
if (noFilter && isDefaultFilter) {
//progress without header
invokerTarget.filterDescription = new FilterDescription(eventClass);
invokerTarget.eventClassName = filterDescription.eventClass == null ? null : getClassTypeName(filterDescription.eventClass);
invokerTarget.methodName = JavaGenHelper.generateFilteredDispatchMethodName(invokerTarget.filterDescription, bufferDispatch);
invokerTarget.stringMapName = JavaGenHelper.generateFilteredDispatchMap(invokerTarget.filterDescription);
invokerTarget.intMapName = JavaGenHelper.generateFilteredDispatchMap(invokerTarget.filterDescription);
} else if (noFilter || isNoFilter || isDefaultFilter) {
//ignore the NO_FILTER
continue;
} else {
//build switch header
if (filterDescription.comment != null) {
switchF.append(s20).append("//").append(filterDescription.comment).append("\n");
}
if (filterVariable == null) {
switchF.append(s20).append("case(").append(filterValue).append("):\n");
} else {
switchF.append(s20).append("case(").append(filterVariable).append("):\n");
}
}
ct.delete(0, ct.length());
buildDispatchForCbMethodHandles(cbList, ct);
buildPostDispatchForCbMethodHandles(cbMapPostEvent.get(filterDescription), ct);
invokerTarget.methodBody = ct.toString();
if (!noFilter) {
ct.delete(0, ct.length());
ct.append(invokerTarget.getMethodDispatch());
ct.append(s24 + "afterEvent();\n");
ct.append(s24 + "return;\n");
}
switchF.append(ct);
filteredMethodBuffer.append(invokerTarget.toMethodString());
}
}
return switchF.length() == 0 ? null : switchF.toString();
}
private void buildDispatchForCbMethodHandles(List cbList, StringBuilder stringBuilder) {
if (cbList == null || cbList.isEmpty()) {
return;
}
for (CbMethodHandle method : cbList) {
DirtyFlag dirtyFlagForUpdateCb = model.getDirtyFlagForUpdateCb(method);
String dirtyAssignment = "";
if (dirtyFlagForUpdateCb != null) {
if (dirtyFlagForUpdateCb.alwaysDirty) {
dirtyAssignment = dirtyFlagForUpdateCb.name + " = true;\n" + s24;
} else {
dirtyAssignment = dirtyFlagForUpdateCb.name + " = ";
}
}
//protect with guards
Collection nodeGuardConditions = model.getNodeGuardConditions(method);
//HERE TO JOIN THINGS
String OR = "";
if (nodeGuardConditions.size() > 0) {
stringBuilder.append(s24).append("if(guardCheck_" + method.getVariableName() + "()) {\n");
}
//add audit
if (auditingInvocations) {
if (method.isForkExecution()) {
stringBuilder.append(s24).append("auditInvocation(")
.append(method.forkVariableName())
.append(", \"").append(method.variableName).append("\"")
.append(", \"").append(method.method.getName()).append("\"")
.append(", typedEvent")
.append(");\n");
} else {
stringBuilder.append(s24).append("auditInvocation(")
.append(method.variableName)
.append(", \"").append(method.variableName).append("\"")
.append(", \"").append(method.method.getName()).append("\"")
.append(", typedEvent")
.append(");\n");
}
}
//assign return if appropriate
if (method.parameterClass == null) {//triggers
if (method.isForkExecution()) {
stringBuilder.append(method.forkVariableName() + ".onTrigger();\n");
} else {
stringBuilder.append(s24).append(dirtyAssignment).append(method.getMethodTarget()).append(".").append(method.method.getName()).append("();\n");
}
} else if (method.isExportedHandler()) {
StringJoiner sjInvoker = new StringJoiner(", ", "(", ");\n\t");
for (int i = 0; i < method.getMethod().getParameterCount(); i++) {
sjInvoker.add("arg" + i);
}
if (dirtyFlagForUpdateCb != null) {
if (method.getMethod().getReturnType() == boolean.class) {
dirtyAssignment = dirtyFlagForUpdateCb.name + " = ";
} else {
//dirtyAssignment = dirtyFlagForUpdateCb.name + " = true;\n" + s24;
}
}
stringBuilder.append(s24).append(dirtyAssignment).append(method.getMethodTarget()).append(".").append(method.method.getName()).append(sjInvoker);
} else {
stringBuilder.append(s24).append(dirtyAssignment).append(method.getMethodTarget()).append(".").append(method.method.getName()).append("(typedEvent);\n");
}
if (dirtyFlagForUpdateCb != null && dirtyFlagForUpdateCb.requiresInvert) {
stringBuilder.append(s24).append("not" + dirtyFlagForUpdateCb.name + " = !" + dirtyFlagForUpdateCb.name + ";\n");
}
//child callbacks - listening to an individual parent change
//if guards are in operation for the parent node, conditionally invoke only on a change
final Map> listenerMethodMap = model.getParentUpdateListenerMethodMap();
Object parent = method.instance;
String parentVar = method.variableName;
//guard
DirtyFlag parentFlag = model.getDirtyFieldMap().get(model.getFieldForInstance(parent));
//the parent listener map should be keyed on instance and event filter
//we carry out filtering here so that no propagate annotations on parents
//do not generate the parent callback
List updateListenerCbList = listenerMethodMap.get(parent);
final OnEventHandler handlerAnnotation = method.method.getAnnotation(OnEventHandler.class);
if (handlerAnnotation != null && (!handlerAnnotation.propagate())) {
} else if (model.getForkedTriggerInstances().contains(parent)) {
//close guards clause
if (nodeGuardConditions.size() > 0) {
//callTree += String.format("%16s}\n", "");
stringBuilder.append(s16 + "}\n");
}
} else {
if (parentFlag != null && updateListenerCbList.size() > 0) {
//callTree += String.format("%20sif(%s) {\n", "", parentFlag.name);
stringBuilder.append(s20 + "if(").append(parentFlag.name).append(") {\n");
}
//child callbacks
boolean unguarded = false;
StringBuilder sbUnguarded = new StringBuilder();
for (CbMethodHandle cbMethod : updateListenerCbList) {
//callTree += String.format("%24s%s.%s(%s);%n", "", cbMethod.variableName, cbMethod.method.getName(), parentVar);
if (!cbMethod.method.getAnnotation(OnParentUpdate.class).guarded()) {
unguarded = true;
sbUnguarded.append(s20).append(cbMethod.variableName).append(".").append(cbMethod.method.getName()).append("(").append(parentVar).append(");\n");
} else {
stringBuilder.append(s24).append(cbMethod.variableName).append(".").append(cbMethod.method.getName()).append("(").append(parentVar).append(");\n");
}
}
if (parentFlag != null && updateListenerCbList.size() > 0) {
//callTree += String.format("%20s}\n", "", parentFlag.name);
stringBuilder.append(s20).append("}\n");
if (unguarded) {
stringBuilder.append(sbUnguarded);
}
}
//close guards clause
if (nodeGuardConditions.size() > 0) {
//callTree += String.format("%16s}\n", "");
stringBuilder.append(s16 + "}\n");
}
}
}
}
private void buildPostDispatchForCbMethodHandles(List cbList, StringBuilder stringBuilder) {
if (cbList == null || cbList.isEmpty()) {
return;
}
stringBuilder.append(s16 + "//event stack unwind callbacks\n");
for (CbMethodHandle method : cbList) {
//protect with guards
Collection nodeGuardConditions = model.getNodeGuardConditions(method);
String OR = "";
if (!nodeGuardConditions.isEmpty()) {
Set forkedTriggers = model.getForkedTriggerInstances();
if (forkedTriggers.contains(method.getInstance())) {
stringBuilder.append(method.forkVariableName()).append(".afterEvent();\n");
}
stringBuilder.append(s24 + "if(");
for (DirtyFlag nodeGuardCondition : nodeGuardConditions) {
stringBuilder.append(OR).append(nodeGuardCondition.name);
OR = " | ";
}
stringBuilder.append(") {\n");
}
stringBuilder.append(s24).append(method.variableName).append(".").append(method.method.getName()).append("();\n");
//close guarded clause
if (!nodeGuardConditions.isEmpty()) {
stringBuilder.append(s16 + "}\n");
}
}
}
private String buildFilteredDispatch(Map> cbMap,
Map> cbMapPostEvent,
Class eventClass) {
String dispatchString = "";
String eventHandlerString = "";
String intFilterSwitch = buildFilteredSwitch(cbMap, cbMapPostEvent, eventClass, true, false);
String stringFilterSwitch = buildFilteredSwitch(cbMap, cbMapPostEvent, eventClass, false, false);
String noFilterDispatch = buildFilteredSwitch(cbMap, cbMapPostEvent, eventClass, false, true);
if (!isInlineEventHandling) {
//Handle with case statements
dispatchString += String.format("%16shandleEvent(typedEvent);%n", "");
eventHandlerString += String.format("%n%4spublic void handleEvent(%s typedEvent) {%n", "", getClassName(eventClass.getCanonicalName()));
eventHandlerString += eventAuditDispatch;
//intFiltered
if (intFilterSwitch != null) {
eventHandlerString += String.format("%8sswitch (typedEvent.filterId()) {%n", "");
eventHandlerString += intFilterSwitch;
eventHandlerString += String.format("%8s}%n", "");
}
//String filtered
if (stringFilterSwitch != null) {
eventHandlerString += String.format("%8sswitch (typedEvent.filterString()) {%n", "");
eventHandlerString += stringFilterSwitch;
eventHandlerString += String.format("%8s}%n", "");
}
//TODO if both are null then
if (noFilterDispatch != null) {
eventHandlerString += " //Default, no filter methods\n";
eventHandlerString += noFilterDispatch;
}
eventHandlerString += String.format("%8safterEvent();%n", "");
eventHandlerString += String.format("%4s}%n", "");
} else {
dispatchString += eventAuditDispatch;
//int filtered
if (intFilterSwitch != null) {
dispatchString += String.format("%16sswitch (typedEvent.filterId()) {%n", "");
dispatchString += intFilterSwitch;
dispatchString += String.format("%16s}%n", "");
}
//String filtered
if (stringFilterSwitch != null) {
dispatchString += String.format("%16sswitch (typedEvent.filterString()) {%n", "");
dispatchString += stringFilterSwitch;
dispatchString += String.format("%16s}%n", "");
}
//no filtered event dispatch
if (noFilterDispatch != null) {
dispatchString += noFilterDispatch;
}
}
eventHandlers += eventHandlerString;
return dispatchString;
}
private void buildMemberAssignments() throws Exception {
final List nodeFields = model.getNodeFields();
if (assignPrivateMembers) {
nodeMemberAssignmentList.add(" final net.vidageek.mirror.dsl.Mirror assigner = new net.vidageek.mirror.dsl.Mirror();");
}
boolean dispatchOnlyVersion = model.isDispatchOnlyVersion();
for (Field field : nodeFields) {
Object object = field.instance;
String varName = field.name;
Class> fieldClass = object.getClass();
boolean isUserClass = !nonUserClass.contains(fieldClass);
if (isUserClass && dispatchOnlyVersion) {
continue;
}
model.beanProperties(object).stream().forEach(s -> nodeMemberAssignmentList.add(varName + "." + s + ";"));
java.lang.reflect.Field[] fields = object.getClass().getFields();
MirrorList fields1 = new Mirror().on(object.getClass()).reflectAll().fields();
fields = fields1.toArray(fields);
for (java.lang.reflect.Field instanceField : fields) {
boolean useRefelction = false;
if ((instanceField.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) {
continue;
}
if (instanceField.getAnnotation(FluxtionIgnore.class) != null) {
continue;
}
if (!assignPrivateMembers && (instanceField.getModifiers() & (Modifier.PRIVATE | Modifier.PROTECTED | Modifier.FINAL)) != 0) {
continue;
}
if (!assignPrivateMembers && (instanceField.getModifiers() == 0)) {
continue;
}
if (assignPrivateMembers && !Modifier.isPublic(instanceField.getModifiers())) {
useRefelction = true;
}
instanceField.setAccessible(true);
if (instanceField.getType().isArray()) {
//array fields
Object array = instanceField.get(object);
if (array == null) {
continue;
}
int length = Array.getLength(array);
final String className = getClassName(instanceField.getType().getComponentType().getCanonicalName());
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4s%s[] %s_%s = new %s[%d];", "", className, varName, instanceField.getName(), className, length));
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue(%s_%s);", "", varName, instanceField.getName(), varName, instanceField.getName()));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = new %s[%d];", "", varName, instanceField.getName(), className, length));
}
if (instanceField.getType().getComponentType().isPrimitive()) {
//primitive array
for (int i = 0; i < length; i++) {
Object value = Array.get(array, i).toString();
String joiner = useRefelction ? "_" : ".";
nodeMemberAssignmentList.add(String.format("%4s%s%s%s[%d] = %s;", "", varName, joiner, instanceField.getName(), i, value));
}
} else if (instanceField.getType().getComponentType().equals(String.class)) {
//String array
for (int i = 0; i < length; i++) {
Object value = StringEscapeUtils.escapeJava(Array.get(array, i).toString());
String joiner = useRefelction ? "_" : ".";
nodeMemberAssignmentList.add(String.format("%4s%s%s%s[%d] = \"%s\";", "", varName, joiner, instanceField.getName(), i, value));
}
} else {
//object array
for (int i = 0; i < length; i++) {
Object refField = Array.get(array, i);
Field nodeReference = model.getFieldForInstance(refField);
String joiner = useRefelction ? "_" : ".";
if (nodeReference != null) {
nodeMemberAssignmentList.add(String.format("%4s%s%s%s[%d] = %s;", "", varName, joiner, instanceField.getName(), i, nodeReference.name));
}
}
}
} else {
//scalar fields
if (instanceField.get(object) == null) {
continue;
}
Field nodeReference = model.getFieldForInstance(instanceField.get(object));
if (instanceField.getType().equals(Character.TYPE)) {
String value = instanceField.get(object).toString();
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue('%s');", "", varName, instanceField.getName(), value));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = '%s';", "", varName, instanceField.getName(), value));
}
} else if (instanceField.getType().isPrimitive()) {
String value = instanceField.get(object).toString();
value = value.equalsIgnoreCase("NaN") ? "Double.NaN" : value;
value = "(" + instanceField.getType() + ")" + value;
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue(%s);", "", varName, instanceField.getName(), value));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = %s;", "", varName, instanceField.getName(), value));
}
} else if (instanceField.getType().isEnum()) {
String value = instanceField.get(object).toString();
String enumClass = instanceField.getType().getCanonicalName();
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue(%s.%s);", "", varName, instanceField.getName(), enumClass, value));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = %s.%s;", "", varName, instanceField.getName(), enumClass, value));
}
} else if (nodeReference != null) {
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue(%s);", "", varName, instanceField.getName(), nodeReference.name));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = %s;", "", varName, instanceField.getName(), nodeReference.name));
}
} else if (instanceField.getType().equals(String.class)) {
String value = StringEscapeUtils.escapeJava(instanceField.get(object).toString());
if (useRefelction) {
nodeMemberAssignmentList.add(String.format("%4sassigner.on(%s).set().field(\"%s\").withValue(\"%s\");", "", varName, instanceField.getName(), value));
} else {
nodeMemberAssignmentList.add(String.format("%4s%s.%s = \"%s\";", "", varName, instanceField.getName(), value));
}
} else if (Collection.class.isAssignableFrom(instanceField.getType())) {
Collection list = (Collection) instanceField.get(object);
if (list != null) {
String joiner = useRefelction ? "_" : ".";
if (useRefelction) {
final String className = getClassName(Collection.class.getCanonicalName());
nodeMemberAssignmentList.add(String.format("%4sCollection %s_%s = (Collection)assigner.on(%s).get().field(\"%s\");", "", varName, instanceField.getName(), varName, instanceField.getName()));
}
for (Object parent : list) {
if (instanceField.getGenericType() instanceof ParameterizedType) {
ParameterizedType integerListType = (ParameterizedType) instanceField.getGenericType();
Class> classType = Object.class;
if (integerListType.getActualTypeArguments()[0] instanceof Class) {
classType = (Class>) integerListType.getActualTypeArguments()[0];
}
Field nodeParentReference = model.getFieldForInstance(parent);
if (nodeParentReference != null) {
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add(%s);", "", varName, joiner, instanceField.getName(), nodeParentReference.name));
} else if (classType == String.class) {
String value = StringEscapeUtils.escapeJava(parent.toString());
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add(\"%s\");", "", varName, joiner, instanceField.getName(), value));
} else if (classType == Character.class) {
String value = parent.toString();
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add('%s');", "", varName, joiner, instanceField.getName(), parent));
} else if (Number.class.isAssignableFrom(classType)) {
String value = parent.toString();
String cast = mapWrapperToPrimitive(classType).getSimpleName();
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add((%s)%s);", "", varName, joiner, instanceField.getName(), cast, value));
} else if (classType.isEnum()) {
String enumClass = getClassName(classType.getCanonicalName());
String value = parent.toString();
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add(%s.%s);", "", varName, joiner, instanceField.getName(), enumClass, value));
}
} else {
Field nodeParentReference = model.getFieldForInstance(parent);
if (nodeParentReference != null) {
nodeMemberAssignmentList.add(String.format("%4s%s%s%s.add(%s);", "", varName, joiner, instanceField.getName(), nodeParentReference.name));
}
}
}
}
}
}
}
}
}
public ArrayList getPublicNodeList() {
return publicNodeIdentifierList;
}
public String getAdditionalInterfaces() {
return additionalInterfaces == null ? "" : additionalInterfaces;
}
public int getDirtyFlagCount() {
return model.getDirtyFieldMap() == null ? 32 : model.getDirtyFieldMap().size();
}
public String getImports() {
List dedupeList = new ArrayList<>(new HashSet<>(importList));
Collections.sort(dedupeList);
StringBuilder sb = new StringBuilder(2048);
dedupeList.stream().forEach(s -> {
sb.append("import ")
.append(s)
.append(";\n");
});
return sb.toString();
}
@Override
public String toString() {
return "SepJavaSourceModel{" + "\ninitialiseMethods=" + initialiseMethodList
+ ", \nbatchEndMethods=" + batchEndMethodList
+ ", \nbatchPauseMethods=" + batchPauseMethodList
+ ", \neventEndMethods=" + eventEndMethodList
+ ", \ntearDownMethods=" + tearDownMethodList
+ ", \nnodeDeclarations=" + nodeDeclarationList
+ ", \neventDispatch=" + eventDispatch
+ ", \nnodeMemberAssignments=" + nodeMemberAssignmentList
+ ", \nmodel=" + model
+ "\n}";
}
private void buildNodeRegistrationListeners() {
final List nodeFields = model.getNodeFields();
this.auditingEvent = false;
eventAuditDispatch = "";
List listenerFields = model.getNodeRegistrationListenerFields();
if (listenerFields == null || listenerFields.isEmpty()) {
return;
}
this.auditingEvent = true;
this.auditingInvocations = false;
String eventClassName = getClassTypeName(Event.class);
importList.add(Event.class.getCanonicalName());
importList.add(EventProcessorContext.class.getCanonicalName());
importList.add(MutableEventProcessorContext.class.getCanonicalName());
importList.add(Map.class.getCanonicalName());
importList.add(EventFeed.class.getCanonicalName());
importList.add(EventLogManager.class.getCanonicalName());
importList.add(ExportFunctionAuditEvent.class.getCanonicalName());
importList.add(SubscriptionManager.class.getCanonicalName());
auditMethodString = "";
String auditObjet = "private void auditEvent(Object typedEvent){\n";
String auditEvent = String.format("private void auditEvent(%s typedEvent){\n", eventClassName);
String auditInvocation = "private void auditInvocation(Object node, String nodeName, String methodName, Object typedEvent){\n";
String initialiseAuditor = "private void initialiseAuditor(" + getClassName(Auditor.class.getName()) + " auditor){\n"
+ "\tauditor.init();\n";
for (Field nodeField : nodeFields) {
String nodeName = nodeField.name;
if (listenerFields.stream().anyMatch((t) -> t.name.equals(nodeName))) {
continue;
}
initialiseAuditor += (String.format("auditor.nodeRegistered(%s, \"%s\");", nodeName, nodeName));
}
initialiseAuditor += model.getTriggerOnlyCallBacks().stream()
.filter(CbMethodHandle::isForkExecution)
.map(CbMethodHandle::forkVariableName)
.map(s -> String.format("auditor.nodeRegistered(%s, \"%s\");", s, s))
.collect(Collectors.joining());
eventAuditDispatch = "";
nodeMemberAssignmentList.add("\t//node auditors");
for (Field listenerField : listenerFields) {
String listenerName = listenerField.name;
nodeMemberAssignmentList.add("initialiseAuditor(" + listenerField.name + ");");
//add init
//initialiseMethodList.add(String.format("%8s%s.init();", "", listenerName));
//add tear down
tearDownMethodList.add(0, String.format("%8s%s.tearDown();", "", listenerName));
//add event complete
eventEndMethodList.add(String.format("%8s%s.processingComplete();", "", listenerName));
//add event audit
eventAuditDispatch += String.format("%8s%s.eventReceived(typedEvent);%n", "", listenerName);
//node invocation audit
if (((Auditor) listenerField.instance).auditInvocations()) {
auditInvocation += String.format("%8s%s.nodeInvoked(node, nodeName, methodName, typedEvent);%n", "", listenerName);
auditingInvocations = true;
}
}
auditEvent += eventAuditDispatch + "}\n";
auditObjet += eventAuditDispatch + "}\n";
if (auditingInvocations) {
auditEvent += auditInvocation + "}\n";
}
initialiseAuditor += "}\n";
eventAuditDispatch = "auditEvent(typedEvent);\n";
auditMethodString += auditObjet;
auditMethodString += auditEvent;
auditMethodString += initialiseAuditor;
}
private void buildNodeFieldAssigner() {
boolean dispatchOnlyVersion = model.isDispatchOnlyVersion();
if (!dispatchOnlyVersion) {
assignNodeField = "";
return;
}
String prefix = " public void setField(String fieldName, T field){\n" +
" switch (fieldName){\n";
String suffix = " default: {}\n" +
" }\n" +
" }\n";
final List nodeFields = model.getNodeFields();
StringBuilder switchString = new StringBuilder(prefix);
for (Field nodeField : nodeFields) {
String nodeName = nodeField.name;
Class> nodeClass = nodeField.getInstance().getClass();
if (nonUserClass.contains(nodeClass)) {
continue;
}
String className = getClassTypeName(nodeClass);//.getCanonicalName();
switchString.append(
String.format("%1$7s case \"%2$s\" :{\n%1$9s %2$s = (%3$s) field;\n%1$7s break;}\n", "", nodeName, className)
);
}
switchString.append(suffix);
assignNodeField = switchString.toString();
}
private void addDefaultImports() {
model.getImportClasses().stream()
.map(Class::getCanonicalName)
.peek(Objects::toString)
.sorted()
.forEach(this::getClassName);
}
public void additionalInterfacesToImplement(Set> interfacesToImplement) {
additionalInterfaces = "";
if (!interfacesToImplement.isEmpty()) {
importList.add(ExportService.class.getCanonicalName());
additionalInterfaces = interfacesToImplement.stream()
.map(this::getClassTypeName)
.sorted()
.collect(Collectors.joining(", @ExportService ", "\n /*--- @ExportService start ---*/\n @ExportService ", ",\n/*--- @ExportService end ---*/\n"));
}
if (model.isDispatchOnlyVersion()) {
additionalInterfaces += getClassTypeName(NodeDispatchTable.class) + ",\n";
}
}
}