Code generation for ADM is run by using com.neeve.tools.AdmCodeGenerator
class. It is possible to supply listener to AdmCodeGenerator
instance to subscribe to events that are fired at certain points in code generation run. The events are given in AdmCodeGenerator.CodeGenerateEventType
enum which is defined as shown below.
/** * Code generation steps that external listener can listen for. */ public enum CodeGenerateEventType { /** * When code generation is about to run. */ START, /** * After model has been parsed */ MODEL_PARSED, /** * When code generation finished. */ END, /** * When code generation is skipped because nothing changed since previous run * Can happen when incrementalBuild is on. */ SKIP } |
Event listener can be given to AdmCodeGenerator
instance directly through CODEGEN_EVENT_LISTENERS
parameter. Maven plugin exposes it through codegenListenerClassName
configuration parameter. This parameter accepts full qualified name of the class that implements listener interface.Listener interface is defined in AdmCodeGenerator.CodegenListener
as shown below. So, while AdmCodeGenerator can accept multiple listeners, maven plugin accepts only one class name and will create only one instance of that listener class per execution. The class shpuld be in project's build classpath (either in project being built or in one of its dependencies).
/** * Event listener for code generation events. */ public interface CodegenListener { /** * When code generation event is triggered this method will be called. */ void codeGenerateEvent(CodeGenerateEvent e); } |
The event data is passed in AdmCodeGenerator.CodeGenerateEvent
class instance. the interesting methods from event objects are given below.
/** * Holds event data for code generation events. */ public class CodeGenerateEvent { /** * @return the eventType */ public final CodeGenerateEventType getEventType() { return eventType; } /** * @return the model. Only available if eventType is {@link CodeGenerateEventType#MODEL_PARSED} or {@link CodeGenerateEventType#END}. */ public final AdmModel getModel() { return model; } /** * @return the errorAggregator. Only available if eventType is {@link CodeGenerateEventType#MODEL_PARSED} or {@link CodeGenerateEventType#END}. */ public final AdmSourceCodeErrorAggregator getErrorAggregator() { return errorAggregator; } } |
Developers may tap into listener mechanism to perform additional model validations as given in example below. The example demonstrates how to use the listener mechanism to enforce globally unique field names (meaning both the model for which code is generated and all its imports recursively).
package mypackage; import java.util.HashSet; import java.util.Set; import com.neeve.adm.AdmField; import com.neeve.adm.AdmModel; import com.neeve.adm.AdmModelImport; import com.neeve.adm.AdmSourceCodeErrorAggregator; import com.neeve.tools.AdmCodeGenerator; import com.neeve.tools.AdmCodeGenerator.CodeGenerateEvent; public class CodegenListener implements AdmCodeGenerator.CodegenListener { @Override public void codeGenerateEvent(CodeGenerateEvent e) { switch (e.getEventType()) { case START: System.err.println("CodegenListener START"); break; case SKIP: System.err.println("CodegenListener SKIP"); break; case MODEL_PARSED: System.err.println("CodegenListener MODEL_PARSED " + e.getModel().getFullName()); validateFieldNamesUnique(e.getModel(), e.getErrorAggregator(), new HashSet<String>(), new HashSet<String>()); // e.getErrorAggregator().add("Test external error", AdmSourceCodeErrorAggregator.Severity.ERROR, e.getModel().getCodeSource()); break; case END: System.err.println("CodegenListener END " + e.getModel().getFullName()); // e.getErrorAggregator().add("Test external error", AdmSourceCodeErrorAggregator.Severity.ERROR, e.getModel().getCodeSource()); break; } } private void validateFieldNamesUnique(AdmModel model, AdmSourceCodeErrorAggregator aggregator, Set<String> fields, Set<String> modelsProcessed) { // iterate imports and run validation for them if not already run. We will run bottom -> top validation so we assume that imported models have correct // field names and the importing model doesn't if it defines the same name as in one of the imports. for (AdmModelImport modelImport : model.getModelImports()) { if (!modelsProcessed.contains(modelImport.getModel().getFullName())) { validateFieldNamesUnique(modelImport.getModel(), aggregator, fields, modelsProcessed); modelsProcessed.add(modelImport.getModel().getFullName()); } } // now we check fields of current model for (AdmField field : model.getFields()) { if (fields.contains(field.getName())) { // aggregator collects errors and they will be displayed at the end of code generation. aggregator.add("Duplicate field name", AdmSourceCodeErrorAggregator.Severity.ERROR, field.getCodeSource()); // note that classes derived from AdmModelElement // usually have source code (error line) information retrieved through getCodeSource() // pointing back to place in XML where their XML representation is. } else { fields.add(field.getName()); } } } } |