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.regnosys.rosetta.translate.IngesterGenerator.xtend Maven / Gradle / Ivy
package com.regnosys.rosetta.translate
import com.codahale.metrics.MetricRegistry
import com.google.common.base.Splitter
import com.google.common.collect.LinkedListMultimap
import com.google.common.collect.Multimap
import com.google.common.collect.TreeMultimap
import com.regnosys.rosetta.common.util.UrlUtils
import com.regnosys.rosetta.generator.java.enums.EnumHelper
import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator
import com.regnosys.rosetta.generator.object.ExpandedAttribute
import com.regnosys.rosetta.generator.object.ExpandedType
import com.regnosys.rosetta.rosetta.RosettaAttributeReference
import com.regnosys.rosetta.rosetta.RosettaBuiltinType
import com.regnosys.rosetta.rosetta.RosettaDataReference
import com.regnosys.rosetta.rosetta.RosettaEnumValueReference
import com.regnosys.rosetta.rosetta.RosettaEnumeration
import com.regnosys.rosetta.rosetta.RosettaMapTestExpression
import com.regnosys.rosetta.rosetta.RosettaMergeSynonymValue
import com.regnosys.rosetta.rosetta.RosettaModel
import com.regnosys.rosetta.rosetta.RosettaSynonymSource
import com.regnosys.rosetta.rosetta.RosettaType
import com.regnosys.rosetta.rosetta.RosettaTypeAlias
import com.regnosys.rosetta.rosetta.expression.RosettaBooleanLiteral
import com.regnosys.rosetta.rosetta.expression.RosettaIntLiteral
import com.regnosys.rosetta.rosetta.expression.RosettaLiteral
import com.regnosys.rosetta.rosetta.expression.RosettaNumberLiteral
import com.regnosys.rosetta.rosetta.expression.RosettaStringLiteral
import com.regnosys.rosetta.rosetta.simple.Data
import com.regnosys.rosetta.translate.CustomDeserialise.ParseException
import com.regnosys.rosetta.translate.MappingError.MappingErrorLevel
import com.regnosys.rosetta.translate.datamodel.Cardinality
import com.regnosys.rosetta.translate.datamodel.ModelParser
import com.regnosys.rosetta.translate.datamodel.Schema
import com.regnosys.rosetta.translate.datamodel.SchemaDeserialise
import com.regnosys.rosetta.translate.datamodel.SchemaSerialise
import com.regnosys.rosetta.translate.datamodel.json.JsonSchemaParser
import com.regnosys.rosetta.translate.datamodel.xsd.BeansXsdParser
import com.regnosys.rosetta.translate.synonymmap.AttributeGroup
import com.regnosys.rosetta.translate.synonymmap.AttributeGroupMapping
import com.regnosys.rosetta.translate.synonymmap.CardinalityChecker
import com.regnosys.rosetta.translate.synonymmap.Element
import com.regnosys.rosetta.translate.synonymmap.ModelCombiner
import com.regnosys.rosetta.translate.synonymmap.SynonymAbsentTest
import com.regnosys.rosetta.translate.synonymmap.SynonymBinaryTest
import com.regnosys.rosetta.translate.synonymmap.SynonymCondition
import com.regnosys.rosetta.translate.synonymmap.SynonymConditionFuncTest
import com.regnosys.rosetta.translate.synonymmap.SynonymExistsTest
import com.regnosys.rosetta.translate.synonymmap.SynonymGroup
import com.regnosys.rosetta.translate.synonymmap.SynonymMap
import com.regnosys.rosetta.translate.synonymmap.SynonymMapBuilder
import com.regnosys.rosetta.translate.synonymmap.SynonymPathTest
import com.regnosys.rosetta.translate.synonymmap.SynonymRosettaPathTest
import com.regnosys.rosetta.translate.synonymmap.SynonymTest
import com.regnosys.rosetta.translate.synonymmap.SynonymTest.TestPriority
import com.regnosys.rosetta.translate.synonymmap.SynonymValue
import com.regnosys.rosetta.types.builtin.RBuiltinTypeService
import com.rosetta.model.lib.RosettaModelObject
import com.rosetta.model.lib.meta.FieldWithMeta
import com.rosetta.util.types.JavaClass
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.URL
import java.util.Collection
import java.util.Collections
import java.util.HashMap
import java.util.LinkedHashMap
import java.util.LinkedHashSet
import java.util.List
import java.util.Map
import java.util.Set
import java.util.function.Function
import java.util.function.Predicate
import java.util.stream.Collectors
import javax.inject.Inject
import org.apache.commons.text.StringEscapeUtils
import org.apache.log4j.Logger
import org.eclipse.xtend2.lib.StringConcatenationClient
import static extension com.regnosys.rosetta.generator.util.RosettaAttributeExtensions.*
import static extension com.regnosys.rosetta.generator.util.IterableUtil.*
import static extension com.regnosys.rosetta.translate.GeneratorUtil.*
import static extension com.regnosys.rosetta.translate.synonymmap.AttributeGroup.*
import com.rosetta.util.types.generated.GeneratedJavaClass
import com.regnosys.rosetta.generator.java.RosettaJavaPackages
import com.regnosys.rosetta.generator.java.types.JavaTypeUtil
import com.rosetta.util.DottedPath
class IngesterGenerator {
static val Logger LOGGER = Logger.getLogger(IngesterGenerator);
public static val MetricRegistry GENERATOR_METRICS = new MetricRegistry();
@Inject
extension JavaTypeTranslator typeTranslator
@Inject
JsonSchemaParser jsonSchemaParser;
@Inject
RBuiltinTypeService builtins
@org.eclipse.xtend.lib.annotations.Data static class GeneratedIngesters {
GeneratedNameAndSource generatedFactory
List generatedHandlers
List errors
def Map classNameToDef() {
val classes = newHashMap
classes.put(generatedFactory.className, generatedFactory.source)
generatedHandlers.forEach[classes.put(className, source)]
return classes
}
}
@org.eclipse.xtend.lib.annotations.Data static class ParseHandlersAndMappingErrors {
List parseHandlers
List errors
}
def GeneratedIngesters generateXml(String envName, String factoryName, List generatorParamsList) {
val handlersAndErrors = parseHandlersAndMappingErrors(new BeansXsdParser(), generatorParamsList)
val factory = generateXmlFactory(envName, factoryName, generatorParamsList)
return new GeneratedIngesters(
new GeneratedNameAndSource(envName + "." + factoryName, new GenerationResult(false, factory)),
handlersAndErrors.parseHandlers,
handlersAndErrors.errors
);
}
def GeneratedIngesters generateJson(String envName, String factoryName, List generatorParamsList) {
LOGGER.info("JSON model parser " + jsonSchemaParser.class.simpleName)
val handlersAndErrors = parseHandlersAndMappingErrors(jsonSchemaParser, generatorParamsList)
val factory = generateJsonFactory(envName, factoryName, generatorParamsList)
return new GeneratedIngesters(
new GeneratedNameAndSource(envName + "." + factoryName, new GenerationResult(false, factory)),
handlersAndErrors.parseHandlers,
handlersAndErrors.errors
);
}
private def parseHandlersAndMappingErrors(ModelParser modelParser, List params) {
val parseHandlers = newArrayList
val errors = newArrayList
for (param : params) {
val parsers = generateParsers(
modelParser,
param.schema,
param.rosettaEntities,
param.sources,
param.topLevelTags,
param.childPackageName
)
parseHandlers.addAll(parsers.key.entrySet.map [
new GeneratedNameAndSource(param.childPackageName + '.' + key + "ParseHandler", value)
])
errors.addAll(parsers.value)
}
new ParseHandlersAndMappingErrors(parseHandlers, errors)
}
private def Pair, Collection> generateParsers(ModelParser modelParser, URL modelUrl,
Collection rosettaEntities, Collection sources,
Collection topLevelTags, String childPackageName) {
val schema = getSchema(modelParser, modelUrl, childPackageName)
generateParsers(sources, rosettaEntities, schema, topLevelTags, childPackageName);
}
def Schema getSchema(ModelParser modelParser, URL modelUrl, String childPackageName) {
val jschemaFileName = UrlUtils.getBaseFileName(modelUrl) + ".schema"
val schemaURL = new URL(modelUrl, jschemaFileName)
try {
val loadStart = System.currentTimeMillis
val deserial = new SchemaDeserialise
val schema = deserial.deserialise(schemaURL.openStream) as Schema
LOGGER.info("Loaded schema from file " + schemaURL + " in " + (System.currentTimeMillis - loadStart))
return schema
} catch (IOException | ParseException e) {
LOGGER.info("Failed to read schema file from " + schemaURL)
// if loading fails we fall back to generating the schema
}
val type = if (UrlUtils.getFileExtension(modelUrl).toLowerCase == "xsd") "XSD" else "JSON"
LOGGER.info("Parsing " + type + " model for " + childPackageName)
val schema = modelParser.parseModel(modelUrl)
// This code is useful if you need to serialise a schema
if (!schemaURL.toURI.isOpaque) {
val schemaFile = new File(schemaURL.toURI())
val serialise = new SchemaSerialise()
serialise.serialiseObject(schema, new FileOutputStream(schemaFile))
}
schema
}
def Pair, Collection> generateParsers(
Collection sources, Collection rosettaEntities, Schema schema,
Collection topLevelTags, String childPackageName) {
LOGGER.info("Building synonym map")
val synonymSourceNames = sources.synonymSourceNames
val externalSynonyms = sources.externalSynonymSources
val mappingBuilder = new SynonymMapBuilder(synonymSourceNames, externalSynonyms, typeTranslator)
return generateParsers(mappingBuilder, sources, rosettaEntities, schema, topLevelTags, childPackageName,
new HashMap)
}
def Pair, Collection> generateParsers(SynonymMapBuilder mappingBuilder,
Collection sources, Collection rosettaEntities, Schema schema,
Collection topLevelTags, String childPackageName, Map synHashCache) {
var syns = rosettaEntities.map[c|mappingBuilder.buildMap(c)].toList
LOGGER.info("Validating synonyms")
val combiner = new ModelCombiner
val combineErrors = newArrayList
syns = combiner.combineModels(schema, syns, combineErrors, topLevelTags)
LOGGER.info("Pruning synonyms")
syns = combiner.prune(syns);
if (combineErrors.stream().anyMatch(e|e.getLevel() == MappingErrorLevel.ERROR)) {
// throw new XMLIngestException("Errors combining " + combineErrors)
// TODO fix the error in the CDM then re-enable this to prevent new ones from creeping in
}
LOGGER.info("Checking cardinalities")
combineErrors.addAll(new CardinalityChecker().checkCardinalities(syns))
return new Pair(generateParsers(syns, sources, childPackageName, synHashCache), combineErrors)
}
def Map generateParsers(Collection topLevels,
Collection sources, String childName, Map synHashCache) {
LOGGER.debug("Creating java files");
val Map generated = new HashMap
for (mapping : topLevels) {
generateParsers(mapping, generated, sources, childName, synHashCache)
}
return generated;
}
private def void generateParsers(
SynonymMap mapping,
Map generated,
Collection sources,
String childPackageName,
Map synHashCache
) {
if (generated.containsKey(mapping.rosetta.name)) {
return;
}
if (mapping.rosetta.isClassOrData) {
generateClassParsers(mapping, generated, sources, childPackageName, synHashCache);
} else if (mapping.rosetta instanceof RosettaEnumeration) {
generateEnumParsers(mapping, generated, childPackageName, synHashCache);
}
}
private def validNamespaces(RosettaModel model, String importedNamespace) {
val validNamespacesSet = if (model.eResource.resourceSet?.resources === null) {
LOGGER.warn("No resource set found for " + model.eResource.URI.toString)
newHashSet
} else
model.eResource.resourceSet.resources.flatMap[contents].filter(RosettaModel).flatMap[it.elements].filter[
it instanceof Data || it instanceof RosettaEnumeration
].map[(RosettaType.cast(it))].map[it.model.name].toSet
val namespace = importedNamespace.substring(0, indexOrLength(importedNamespace, "."))
return validNamespacesSet.contains(namespace)
}
private def int indexOrLength(String toSearch, char find) {
val index = toSearch.lastIndexOf(find)
if (index === -1) {
toSearch.length
} else {
index
}
}
private def void generateClassParsers(SynonymMap mapping, Map generated,
Collection synonymSources, String envName, Map synHashCache) {
if (generated.containsKey(mapping.rosetta.fullname)) {
return;
}
val rosettaModel = mapping.rosetta.eContainer as RosettaModel
if (rosettaModel.eResource === null) {
LOGGER.warn("Unable to generate translate parsers. No resource found for rosetta model: " + rosettaModel)
return;
}
val prevHash = synHashCache.get(mapping.rosetta.fullname)
val newHash = mapping.hashForGeneration
if (prevHash === null || newHash != prevHash) {
val header = '''
package «envName».«mapping.rosetta.packageName»;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Optional;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.inject.Injector;
import com.regnosys.rosetta.translate.HandlerSupplier;
import com.rosetta.model.lib.meta.Reference;
import com.regnosys.rosetta.translate.GeneratorPathUtil;
import com.regnosys.rosetta.translate.HandlerCache;
import com.regnosys.rosetta.translate.IngestMerger;
import com.regnosys.rosetta.translate.MappingProcessorSupplier;
import com.regnosys.rosetta.translate.SynonymValueGroup;
import com.regnosys.rosetta.translate.ROMParseHandler;
import com.regnosys.rosetta.common.translation.Mapping;
import com.regnosys.rosetta.common.translation.MappingContext;
import com.regnosys.rosetta.common.translation.Path;
import com.regnosys.rosetta.common.util.PathUtils;
import com.regnosys.rosetta.translate.basic.*;
import com.regnosys.rosetta.common.translation.Path.PathElement;
«FOR importResources : rosettaModel.imports»
«val importedNamespace = importResources.importedNamespace»
«IF validNamespaces(rosettaModel, importedNamespace)»
import «importedNamespace»;
«ENDIF»
«ENDFOR»
import «rosettaModel.name».*;
«FOR traversedImports : mapping.traversedImports»
import «traversedImports»;
«ENDFOR»
import com.rosetta.model.lib.records.*;
import com.rosetta.model.lib.RosettaModelObjectBuilder;
import com.rosetta.model.lib.path.RosettaPath;
import com.rosetta.model.lib.process.BuilderProcessor;
import com.rosetta.model.metafields.MetaFields;
'''
val body = '''
public «IF (mapping.rosetta.isAbstract)»abstract «ENDIF»class «mapping.pNameW» «mapping.romExtends(envName)» {
«pathConstants(mapping)»
«conditionalValues(mapping)»
«handlerCaches(mapping, envName)»
«constructor(mapping)»
// regular handlers
«FOR h : mapping.mappedGroups.filter[group.getSynonymGroups.exists[!synonymValues.isEmpty && conditions.forall[condition.isEmpty]]].sortBy[group].sortBy[group.toHandlerMethodName] SEPARATOR "\n"»
«handler(h, mapping, envName)»
«ENDFOR»
// conditional handlers
«FOR h : mapping.mappedGroups.filter[group.getSynonymGroups.exists[!synonymValues.isEmpty && conditions.exists[!condition.isEmpty]]].sortBy[group] SEPARATOR "\n"»
«condHandler(h, mapping, envName)»
«ENDFOR»
// conditional capture handlers
«FOR ent : mapping.conditionalCaptures.asMap.entrySet.sortBy[e|e.key.toConditionalHandlerMethodName] SEPARATOR "\n"»
«conditionalCaptureHandler(ent, mapping)»
«ENDFOR»
// regular handler getter (for nested 'set to' conditional mapping with path predicate)
«val alreadyGenerated = newHashSet»
«FOR p : mapping.mappedGroups
.filter[group.getSynonymGroups.exists[synonymValues.isEmpty]] // empty synonym values
.filter[group.getSynonymGroups.flatMap[conditions].flatMap[condition].exists[it instanceof SynonymPathTest]] // conditional path predicate
.map[group.attributePath] // maps to attribute path (e.g. rosetta path)
.filter[size>1] // filter to paths with multiple path elements, e.g., nested
// result is list of paths to each attribute used in a set-to conditional mapping with path predicate
SEPARATOR "\n"»
«nestedSetToHandler(p, mapping, alreadyGenerated)»
«ENDFOR»
// mappers
«FOR h : mapping.mappedGroups.filter[group.getSynonymGroups.exists[mapperName !== null]].sortBy[group] SEPARATOR "\n"»
«mappingProcessor(h, mapping)»
«ENDFOR»
«IF !mapping.rosetta.abstract»
// builder proxy
«generateBuilderProxy(mapping)»
«ENDIF»
}
'''
var parser = '''
«header»
«body»
'''
generated.put(mapping.rosetta.fullname, new GenerationResult(false, parser))
synHashCache.put(mapping.rosetta.fullname, newHash);
} else {
generated.put(mapping.rosetta.fullname, new GenerationResult(true, null))
}
if (mapping.superMap !== null) {
generateParsers(mapping.superMap, generated, synonymSources, envName, synHashCache)
}
for (subParser : mapping.childMappings) {
if (subParser.rosetta !== null) {
generateParsers(subParser, generated, synonymSources, envName, synHashCache)
}
}
}
private def getTraversedImports(SynonymMap mapping) {
return mapping.attributeGroups
.flatMap[attributePath]
.filter[rosettaType?.type !== null]
.filter[!rosettaType?.type.toExpandedType.builtInType]
.filter[type.model !== null]
.map[type.model.name + "." + type.name]
.distinct
.sort
}
def dispatch boolean isAbstract(RosettaType type) {
true
}
def dispatch boolean isAbstract(Data type) {
false
}
def captureList(List groups) {
val Multimap conditionals = LinkedListMultimap.create
for (sg : groups) {
for (sv : sg.synonymValues) {
conditionals.putAll(sv, sg.conditions);
}
}
return conditionals;
}
private def toPathName(SynonymValue synonymValue) {
return synonymValue.synonymPath.map[name].join("_").toFirstLower;
}
private def romExtends(SynonymMap mapping, String envName) {
if (mapping.superMap === null) {
'''extends ROMParseHandler'''
} else {
'''extends «mapping.superMap.pFullNameW(envName)»'''
}
}
private def pNameW(SynonymMap mapping) { pNameW(mapping.rosetta) }
private def pFullNameT(SynonymMap mapping, String packageName) { pFullNameT(mapping.rosetta, packageName) }
private def pNameW(RosettaType rosetta) '''«rosetta.name»ParseHandler'''
private def pFullNameT(ExpandedType rosetta, String envName) '''«envName».«rosetta.model.name».«rosetta.name»'''
private def pFullNameW(ExpandedType rosetta, String envName) { '''«pFullNameT(rosetta, envName)»ParseHandler''' }
private def pFullNameW(SynonymMap mapping, String envName) { pFullNameW(mapping.rosetta, envName) }
private def pFullNameW(RosettaType rosetta, String envName) '''«envName».«rosetta.fullname»ParseHandler'''
private def pFullNameT(RosettaType rosetta,
String envName) '''«envName».«rosetta.fullname»ParseHandler<«rosetta.toBuilder»>'''
private def expandedTypeToBuilder(ExpandedType type) {
if (type.isType) {
'''«(type.expandedTypeToJavaType as JavaClass).toBuilderType»'''
} else {
'''«type.expandedTypeToJavaType»'''
}
}
@Inject
RosettaJavaPackages packages;
@Inject
private JavaTypeUtil typeUtil;
private def JavaClass expandedTypeToJavaType(ExpandedType type) {
if (type.name == METAFIELDS_CLASS_NAME || type == META_AND_TEMPLATE_FIELDS_CLASS_NAME) {
return new GeneratedJavaClass(packages.basicMetafields(), type.name, Object);
}
if (type.isMetaType) {//TODO ExpandedType needs to store the underlying type for meta types if we want them to be anything other than strings
return typeUtil.STRING;
}
if (type.isBuiltInType) {
return toJavaReferenceType(builtins.getType(type.name, Collections.emptyMap()));
}
return new GeneratedJavaClass(modelPackage(type.model), type.name, Object);
}
private def DottedPath modelPackage(RosettaModel model) {
return DottedPath.splitOnDots(model.getName());
}
private def dispatch StringConcatenationClient toBuilder(Data type) {
if (type.name.endsWith("WithMeta")) {
return '''«type.fullname».«type.name»Builder«type.definition.toFirstUpper»'''
}
'''«type.fullname».«type.name»Builder'''
}
private def dispatch StringConcatenationClient toBuilder(RosettaEnumeration type) '''«type.name»'''
private def dispatch StringConcatenationClient toBuilder(RosettaBuiltinType type) '''«builtins.getType(type.name, newHashMap)»'''
private def dispatch StringConcatenationClient toBuilder(RosettaTypeAlias type) {
type.typeCall.type.toBuilder
}
private def pathConstants(SynonymMap mapping) {
val svs = new LinkedHashSet
svs.addAll(mapping.attributeGroups.flatMap[synonymGroups]);
svs.addAll(mapping.conditionalCaptures.keySet.map [
new SynonymGroup(Collections.singletonList(it), Collections.emptyList(), null, null, null, null, false)
])
return svs.pathConstants
}
private def pathConstants(Iterable sgs) {
sgs.flatMap[synonymValues].sortBy[toPathName].map [ synonymValue |
'''private final static ImmutableList «synonymValue.toPathName»Path = ImmutableList.of(«synonymValue.synonymPath.list»);'''
].distinct.join("\n")
}
/*
* There are two types of temporary conditional values
* one where a primitive typed mapping has a condition meaning that the value may or may not be used
* the other where there is a path in a when clause and we need to capture the value of that path
*/
private def conditionalValues(SynonymMap mapping) '''
«val groups = mapping.attributeGroups.sortBy[toAttPathName]»
«««These Values hold primitive values that might be set on the rosetta object dependign on a when clase
«««required where there is an input path in the SynonymValues and there is a conditional clause
«FOR group : groups.filter[synonymGroups.exists[!synonymValues.isEmpty && conditions.exists[!condition.isEmpty]]]»
protected Map «group.toAttPathName»Values = new LinkedHashMap<>();
«ENDFOR»
«««These Values hold primitive values are going to be tested in when clauses to see if a rosetta field should be set
«FOR e : mapping.conditionalCaptures.asMap.entrySet.sortBy[key.toPathName]»
«IF e.key.synonymPath.exists[cardinality === Cardinality.MULTIPLE]»
protected Map «toPathName(e.key)»Values = new HashMap<>();
«ELSE»
protected String «toPathName(e.key)»Value;
«ENDIF»
«ENDFOR»
'''
private def handlerCaches(SynonymMap mapping, String packageName) '''
// basic
«basicTypeCaches(mapping)»
// rosetta
«rosettaTypeCaches(mapping, packageName)»
'''
private def basicTypeCaches(SynonymMap mapping) {
val attrPathSynGroups = mapping.mappedGroups.filter[!(mappings.rosetta.isClassOrData)].map[group]
val chopped = attrPathSynGroups.filter[attributePath.size() > 1].groupBy[toFirstMultiple.toAttPathName]
handlerCache(chopped)
}
private def toFirstMultiple(AttributeGroup group) {
group.attributePath.toFirstMultiple
}
/**
* @param atts - list of attributes represents a path (with attribute name/type for each path element).
* @returns the first attribute that has multiple cardinality
*/
private def toFirstMultiple(Iterable atts) {
val result = newArrayList
var multiple = false;
var i = 0;
while (!multiple && i <= atts.size - 2) {
val att = atts.get(i++);
result.add(att)
multiple = att.multiple;
}
result
}
private def handlerCache(Map> attributeGroupMap) '''
«FOR attrPathSynGroups : attributeGroupMap.entrySet.sortBy[value.sortBy[toAttPathName()].last.toAttPathName()]»
«IF attrPathSynGroups.value.map[removeLast].exists[multipleCardinality]»
protected HandlerCache<«attrPathSynGroups.value.last.toFirstMultiple.last.type.expandedTypeToBuilder»> «attrPathSynGroups.key»Underlyers =
new HandlerCache<>(ImmutableMultimap.builder()
«FOR attrPathSynGroup : attrPathSynGroups.value.sort»
«FOR sg:attrPathSynGroup.synonymGroups.sortBy[toSVG]»
.put("«attrPathSynGroup.attributePath.last.attributeName»", new SynonymValueGroup(«toSVG(sg)»))
«ENDFOR»
«ENDFOR»
.build());
«ELSE»
protected AnonHandler<«attrPathSynGroups.value.last.attributePath.removeLast.last.type.expandedTypeToBuilder»> «attrPathSynGroups.value.last.removeLast.toAttPathName»UnderlyerB;
«ENDIF»
«ENDFOR»
'''
private def rosettaTypeCaches(SynonymMap mapping, String packageName) '''
«FOR mappedGroup : mapping.mappedGroups.filter[mappings.rosetta.isClassOrData].sortBy[group.toAttPathName]»
«IF mappedGroup.group.multipleCardinality»
protected HandlerCache<«mappedGroup.group.attributePath.last.type.expandedTypeToBuilder»> «mappedGroup.group.toHandlerFieldName» =
new HandlerCache<>(ImmutableMultimap.builder()
«FOR sg:mappedGroup.group.synonymGroups.sortBy[toSVG]»
.put("«mappedGroup.group.attributePath.last.attributeName»", new SynonymValueGroup(«toSVG(sg)»))
«ENDFOR»
.build());
«ELSE»
«IF mappedGroup.group.synonymGroups.exists[conditions.isEmpty]»
protected «mappedGroup.mappings.pFullNameT(packageName)» «mappedGroup.group.toHandlerFieldName»;
«ENDIF»
«ENDIF»
«ENDFOR»
'''
private def toSVG(SynonymGroup group) {
if (group.synonymValues.empty) {
val pathExprs = group.conditions.map[condition].flatten.filter(SynonymPathTest).map[getPathWithDots]
val binExprs = group.conditions.map[condition].flatten.filter(SynonymBinaryTest).map[paths].flatten.map [ p |
p.synonymPath.map[name].join(".")
]
return newArrayList(pathExprs, binExprs).flatten.toSet.join("\"", "\", \"", "\"", [it])
} else {
return group.synonymValues.map[synonymPath.toFirstLeaf].join("\"", "\", \"", "\"", [ p |
p.map[e|e.name].join(".")
])
}
}
private def CharSequence toHandlerFieldName(AttributeGroup attrPathSynGroup) {
var handlerSuffix = if(attrPathSynGroup.multipleCardinality) 'Handlers' else 'Handler'
return attrPathSynGroup.toAttPathName + handlerSuffix
}
/**
* Simple handlers require one handler method per attribute
*/
private def String toHandlerMethodName(AttributeGroup attrPathSynGroup) {
return attrPathSynGroup.toAttPathName + 'Handler'
}
private def CharSequence toCondHandlerMethodName(AttributeGroup attrPathSynGroup) {
return attrPathSynGroup.toAttPathName + 'ConditionalHandler'
}
/**
* Conditional handlers require one handler method per synonym path (each attribute can have multiple synonym paths)
*/
private def String toConditionalHandlerMethodName(SynonymValue synonymValue) {
return synonymValue.toPathName + 'ConditionalCaptureHandler'
}
private def CharSequence toMappingProcessorMethodName(AttributeGroup attrPathSynGroup, SynonymGroup group) {
return attrPathSynGroup.toAttPathName + group.synonymValues.flatMap[synonymPath].map[name].join.toFirstUpper +
'MappingProcessor'
}
private def toSynonymLastElementName(SynonymGroup group) {
group.synonymValues.map[synonymPath].map[it.map[name].last].toSet
}
private def toFirstLeaf(List elements) {
var result = newArrayList
var isLeaf = false;
var i = 0;
while (!isLeaf && i < elements.size()) {
val e = elements.get(i++)
result.add(e)
isLeaf = false // e?.entity?.isLeaf
}
result
}
private def multipleCardinality(AttributeGroup attrPathSynGroup) {
return attrPathSynGroup.attributePath.exists[cardinalityIsListValue]
}
private def multipleCardinality(List attributePath) {
return attributePath.exists[cardinalityIsListValue]
}
private def getterMultipleCardinality(List attributePath) {
return attributePath.removeLast.exists[cardinalityIsListValue]
}
private def toAttPathName(AttributeGroup attrPathSynGroup) {
attrPathSynGroup.attributePath.toAttPathName
}
private def toAttPathName(List attributePath) {
attributePath.map[s|s.attributeName].join("").toFirstLower
}
private def list(List path) '''«FOR string : path.map[s|s.name] SEPARATOR ", "»"«string»"«ENDFOR»'''
private def constructor(SynonymMap mapping) '''
«IF (!mapping.rosetta.abstract)»@SuppressWarnings("unchecked")«ENDIF»
public «mapping.pNameW()»(Injector injector, MappingContext mappingContext) {
super(injector, mappingContext);
«IF (!mapping.rosetta.abstract)»setUnderlying((T)new BuilderProxy());«ENDIF»
handlers = ImmutableMultimap., HandlerSupplier>builder()
«IF mapping.superMap!==null»
.putAll(super.handlers)
«ENDIF»
«««There are three types of handler:
«««1. normal unconditional handler - the input is used to create the output
«««2. conditional handler - the input may or may not be used for the output depending on the evalutation of a condition
«««3. conditionCapture - the input is captured to be used as part of a condition
// unconditional handlers
«FOR path : mapping.unconditionalHandlerPaths»
.put(«path.synonymPath»Path, this::«path.attributePath»Handler_«path.index»)
«ENDFOR»
// conditional handlers
«FOR path : mapping.conditionalHandlerPaths»
.put(«path.synonymPath»Path, this::«path.attributePath»ConditionalHandler)
«ENDFOR»
// conditional captures
«FOR conditional : condCaptures(mapping).distinct.sort»
«conditional»
«ENDFOR»
.build();
mappingProcessors = ImmutableMultimap., MappingProcessorSupplier>builder()
«IF mapping.superMap!==null»
.putAll(super.mappingProcessors)
«ENDIF»
«FOR attrPathSynGroup : mapping.attributeGroups.filter[synonymGroups.exists[mapperName !== null]].sort»
«FOR mapperSynGroup : attrPathSynGroup.synonymGroups.filter[mapperName !== null]»
«FOR synonymValue : mapperSynGroup.synonymValues.distinctBy[toPathName].sortBy[toPathName]»
.put(«synonymValue.toPathName»Path, this::«attrPathSynGroup.toMappingProcessorMethodName(mapperSynGroup)»)
«ENDFOR»
«ENDFOR»
«ENDFOR»
.build();
}
'''
private def unconditionalHandlerPaths(SynonymMap mapping) {
val paths = newArrayList
for (attrPathSynGroup : mapping.attributeGroups.filter[synonymGroups.exists[conditions.forall[condition.isEmpty]]].sort) {
attrPathSynGroup.synonymGroups.forEach[sg, i|
val index = {
val last = attrPathSynGroup.attributePath.last
if (last.type.isType || last.type.name == "RosettaReference")
0
else
i
}
if (!sg.synonymValues.isEmpty && sg.conditions.forall[condition.isEmpty]) {
sg.synonymValues.distinctBy[toPathName].sortBy[toPathName].forEach[sv | {
paths.add(new SynonymAttributePath(sv.toPathName, attrPathSynGroup.toAttPathName, index))
}]
}
]
}
return paths.sort
}
private def conditionalHandlerPaths(SynonymMap mapping) {
val paths = newArrayList
for (attrPathSynGroup : mapping.attributeGroups.filter[synonymGroups.exists[conditions.exists[!condition.isEmpty]]].sort) {
for (synonymValue : attrPathSynGroup.synonymGroups
.filter[!synonymValues.isEmpty && conditions.exists[!condition.isEmpty]]
.flatMap[sg|sg.synonymValues]
.distinctBy[toPathName]
.sortBy[toPathName]) {
paths.add(new SynonymAttributePath(synonymValue.toPathName, attrPathSynGroup.toAttPathName, 0))
}
}
return paths.sort
}
private def condCaptures(SynonymMap mapping) {
mapping.conditionalCaptures.keySet.map[toPathName()].map [ name |
'''.put(«name»Path, this::«name»ConditionalCaptureHandler)'''
]
}
private def handler(AttributeGroupMapping mappedGroup, SynonymMap mapping, String packageName) {
val last = mappedGroup.group.attributePath.last
if (last.type.isType || last.type.name == "RosettaReference")
romHandler(mappedGroup, mapping, packageName)
else {
val handlers = newArrayList
mappedGroup.group.synonymGroups.forEach[sg, i|
if (last.type.enumeration)
handlers+=basicHandler(mappedGroup, mapping, last.type.pFullNameT(packageName), i)
else
handlers+=basicHandler(mappedGroup, mapping, last.type.name.toFirstUpper, i)
]
return handlers.join("\n");
}
}
private def boolean isClassOrData(RosettaType t) {
t instanceof Data
}
private def nestedSetToHandler(List attributePath, SynonymMap mapping, Set alreadyGenerated) {
val multiple = attributePath.take(attributePath.size-1).exists[cardinalityIsListValue]
val underlyerAttrName = attributePath.removeLast.toAttPathName + "UnderlyerB"
if (!multiple && alreadyGenerated.add(underlyerAttrName)) {
val underlyerType = firstMultipleBuilder(attributePath, mapping)
'''
protected AnonHandler<«underlyerType»> get«underlyerAttrName.toFirstUpper»() {
// nested (set-to conditional mapping with path predicate) anon handler
return «underlyerAttrName» != null ?
«underlyerAttrName» :
useOrNew(«underlyerAttrName»,
() -> {
«underlyerType» chainBuilder;
«builderChain(attributePath, mapping)»
return new AnonHandler<>(injector, mappingContext, rosettaPath, chainBuilder);
});
}
'''
}
}
private def condHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, String envName) {
val t = mappedGroup.group.attributePath.last.type
if (t.isType)
romCondHandler(mappedGroup, mapping, envName)
else if (t.enumeration)
enumCondHandler(mappedGroup, mapping, t, envName)
else
basicCondHandler(mappedGroup, mapping, t.name.toFirstUpper)
}
private def builderChain(List attributePath, SynonymMap mapping) '''
Path rosettaPath = getRosettaPath();
«var attList = attributePath.toFirstMultiple»
«var lastBuilderName = "getUnderlying()"»
«var nextBuilderName =""»
«FOR att : attList»
«IF (att.cardinalityIsListValue)»
int «att.name»Index = getMappingListIndex(«lastBuilderName».get«att.name.toFirstUpper»(), «mergeSynonyms(attributePath, mapping).asList»);
«att.type.expandedTypeToBuilder» «nextBuilderName=att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»(«att.name»Index);
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»", «att.name»Index));
«ELSE»
«att.type.expandedTypeToBuilder» «nextBuilderName = att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»();
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDIF»
«{lastBuilderName = nextBuilderName;""}»
«ENDFOR»
chainBuilder = («firstMultipleBuilder(attributePath, mapping)»)«lastBuilderName»;
'''
/**
* Find merge synonyms for the given model path.
*/
def mergeSynonyms(List attributePath, SynonymMap mapping) {
mapping.mappedGroups
.filter[it.group.attributePath === attributePath]
.flatMap[it.mappings.mergeSynonyms.values]
.toList
}
/**
* Write merge synonym values out as an list of MergeSynonymValue
*/
def asList(List mergeSyns) {
'''java.util.Arrays.asList(«mergeSyns.map['''new com.regnosys.rosetta.translate.MergeSynonymValue("«it.name»", "«it.excludePath»")'''].join(",")»)'''
}
def postAnon(AttributeGroup group, SynonymMap mapping) {
val afterMultiple = postFirstMultiple(group);
var lastBuilderName = "foundbuilder.getUnderlying()"
var String nextBuilderName = null
'''
«FOR att : afterMultiple.removeLast»
«IF (att.cardinalityIsListValue)»
int «att.name»Index = sizeOf(«lastBuilderName».get«att.name.toFirstUpper»());
«att.type.expandedTypeToBuilder» «nextBuilderName=att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»(«att.name»Index);
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»", «att.name»Index));
«ELSE»
«att.type.expandedTypeToBuilder» «nextBuilderName = att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»();
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDIF»
«{lastBuilderName = nextBuilderName;""}»
«ENDFOR»
«penultimateBuilder(group.attributePath, mapping)» builder = «lastBuilderName»;
'''
}
protected def List postFirstMultiple(AttributeGroup group) {
group.attributePath.subList(group.toFirstMultiple.size, group.attributePath.size)
}
protected def List postFirstMultiple(List attributePath) {
attributePath.subList(attributePath.toFirstMultiple.size, attributePath.size)
}
private def conditionalCaptureHandler(Map.Entry> sv, SynonymMap mapping) '''
private StringParseHandler «sv.key.toConditionalHandlerMethodName»(Path localPath, Path parentPath) {
«IF sv.key.synonymPath.exists[e | e.entity !== null && e.cardinality === Cardinality.MULTIPLE]»
«IF sv.value.forall[c|c.condition.forall[c2|c2 instanceof SynonymExistsTest || c2 instanceof SynonymAbsentTest]]»
«sv.key.toPathName»Values.put(localPath, "Exists");
StringParseHandler res = new StringParseHandler(true, true);
res.setParentSetter(a -> {});
res.setParentSupplier(() -> Optional.empty());
return res;
«ELSE»
StringParseHandler handler = new StringParseHandler(true, true);
useConditionalCache(handler, localPath, «sv.key.toPathName»Values);
return handler;
«ENDIF»
«ELSE»
«IF sv.value.forall[c|c.condition.forall[c2|c2 instanceof SynonymExistsTest || c2 instanceof SynonymAbsentTest]]»
«sv.key.toPathName»Value = "Exists";
StringParseHandler res = new StringParseHandler(true, true);
res.setParentSetter(a -> {});
res.setParentSupplier(() -> Optional.empty());
return res;
«ELSE»
StringParseHandler handler = new StringParseHandler(true, true);
handler.setParentSetter(val -> «sv.key.toPathName»Value = val);
handler.setParentSupplier(() -> Optional.ofNullable(«sv.key.toPathName»Value));
return handler;
«ENDIF»
«ENDIF»
}
'''
private def basicHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, CharSequence type, int index) {
val attrPathSynGroup = mappedGroup.group
val lastAttribute = attrPathSynGroup.attributePath.last
'''
private «type»ParseHandler «attrPathSynGroup.toHandlerMethodName»_«index»(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«generateAnonHandler(attrPathSynGroup, mapping, lastAttribute, "localPath", "xmlPath")»
«type»ParseHandler handler = new «basicHandlerConstructor(type, attrPathSynGroup.synonymGroups.get(index))»
handler.addXmlPath(xmlPath);
«IF lastAttribute.cardinalityIsSingleValue»
rosettaPath = rosettaPath.addElement(new PathElement("«lastAttribute.name»"));
handler.setParentSetter(val -> builder.set«lastAttribute.attributeName.toFirstUpper»(val));
handler.setParentSupplier(() -> «supplier(mappedGroup.group.attributePath)»
);
«ELSE»
int index = sizeOf(builder.get«lastAttribute.name.toFirstUpper»());
rosettaPath = rosettaPath.addElement(new PathElement("«lastAttribute.name»", index));
handler.setParentSetter(val -> builder.add«lastAttribute.name.toFirstUpper»(val));
handler.setParentSupplier(() -> Optional.empty());
«ENDIF»
handler.setRosettaPath(rosettaPath);
return handler;
}
'''
}
def basicHandlerConstructor(CharSequence type, SynonymGroup sg) {
basicHandlerConstructor(
type,
sg.synonymValues.map[mapsTo].max(0)>1,
sg.removeHtml,
sg.formatString,
sg.patternMatcher,
sg.patternReplace
)
}
def basicHandlerConstructor(CharSequence type, List synonymGroups) {
basicHandlerConstructor(
type,
synonymGroups.flatMap[synonymValues].map[mapsTo].max(0)>1,
synonymGroups.map[removeHtml].findFirst[true],
synonymGroups.map[formatString].findFirst[true],
synonymGroups.map[patternMatcher].findFirst[true],
synonymGroups.map[patternReplace].findFirst[true]
)
}
def basicHandlerConstructor(CharSequence type, boolean hasMapsTo, boolean removeHtml, String formatString, String patternMatcher, String patternReplace) {
switch (type) {
case "Date",
case "DateTime",
case "Time",
case "ZonedDateTime":
'''«type»ParseHandler(«hasMapsTo», false, «removeHtml»«IF formatString!==null», "«formatString»"«ENDIF»);'''
default :
'''«type»ParseHandler(«hasMapsTo», false, «removeHtml»«IF patternMatcher!==null», "«patternMatcher»", "«patternReplace»" «ENDIF»);'''
}
}
def enumHandlerConstructor(ExpandedType type, List synonymGroups, String envName) {
enumHandlerConstructor(
type,
synonymGroups.flatMap[synonymValues].map[mapsTo].max(0)>1,
synonymGroups.map[removeHtml].findFirst[true],
synonymGroups.map[formatString].findFirst[true],
synonymGroups.map[patternMatcher].findFirst[true],
synonymGroups.map[patternReplace].findFirst[true],
envName
)
}
def enumHandlerConstructor(ExpandedType type, boolean hasMapsTo, boolean removeHtml, String formatString, String patternMatcher, String patternReplace, String envName)
'''«type.pFullNameW(envName)»(«hasMapsTo», false, «removeHtml»«IF patternMatcher!==null», "«patternMatcher»", "«patternReplace»" «ENDIF»);'''
private def > T max(Iterable iterable, T defaultVal) {
if (iterable.isEmpty) defaultVal
else iterable.max
}
private def enumCondHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, ExpandedType type, String envName) '''
// enum condition handler
«val attrPathSynGroup = mappedGroup.group»
private «type.pFullNameW(envName)» «attrPathSynGroup.toCondHandlerMethodName»(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«type.pFullNameW(envName)» handler = new «enumHandlerConstructor(type, attrPathSynGroup.synonymGroups, envName)»
handler.addXmlPath(xmlPath);
Path rosettaPath = getRosettaPath();
«FOR att : mappedGroup.group.attributePath»
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDFOR»
useConditionalCache(handler, localPath, «mappedGroup.group.toAttPathName»Values);
handler.setRosettaPath(rosettaPath);
return handler;
}
'''
private def basicCondHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, String type) '''
// basic type condition handler
«val attrPathSynGroup = mappedGroup.group»
private «type»ParseHandler «attrPathSynGroup.toCondHandlerMethodName»(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«type»ParseHandler handler = new «basicHandlerConstructor(type, attrPathSynGroup.synonymGroups)»
handler.addXmlPath(xmlPath);
Path rosettaPath = getRosettaPath();
«FOR att : mappedGroup.group.attributePath»
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDFOR»
useConditionalCache(handler, localPath, «mappedGroup.group.toAttPathName»Values);
handler.setRosettaPath(rosettaPath);
return handler;
}
'''
private def generateAnonHandler(AttributeGroup group, SynonymMap mapping, ExpandedAttribute last, String localPathVar, String xmlPathVar) '''
// anon handler
«IF group.attributePath.size>1»
«IF group.attributePath.take(group.attributePath.size-1).exists[cardinalityIsListValue]»
AnonHandler<«firstMultipleBuilder(group.attributePath, mapping)»> foundbuilder = findOrNew(«group.toFirstMultiple.toAttPathName»Underlyers, "«last.name»", «localPathVar»,
«ELSE»
AnonHandler<«firstMultipleBuilder(group.attributePath, mapping)»> foundbuilder = «group.removeLast.toAttPathName»UnderlyerB = useOrNew(«group.removeLast.toAttPathName»UnderlyerB,
«ENDIF»
() -> {
«firstMultipleBuilder(group.attributePath, mapping)» chainBuilder;
«builderChain(group.attributePath, mapping)»
AnonHandler<«firstMultipleBuilder(group.attributePath, mapping)»> result = new AnonHandler<>(injector, mappingContext, rosettaPath, chainBuilder);
result.addXmlPath(«xmlPathVar»);
return result;
});
Path rosettaPath = foundbuilder.getRosettaPath();
«postAnon(group, mapping)»
«ELSE»
Path rosettaPath = getRosettaPath();
«mapping.rosetta.pNameW» foundbuilder = «mapping.rosetta.pNameW».this;
«postAnon(group, mapping)»
«ENDIF»
'''
private def mappingProcessor(AttributeGroupMapping mappedGroup, SynonymMap mapping) {
val attrPathSynGroup = mappedGroup.group
val mapperSynGroups = attrPathSynGroup.synonymGroups.filter[mapperName !== null].distinct
'''
«FOR mapperSynGroup : mapperSynGroups»
«val mapperClassName = mapperSynGroup.mapperName + "MappingProcessor"»
private Map.Entry «attrPathSynGroup.toMappingProcessorMethodName(mapperSynGroup)»(Path parentPath, MappingContext mappingContext) {
«builderChainForMapper(attrPathSynGroup.attributePath.removeLastReference, mapping)»
RosettaPath modelPath = PathUtils.toRosettaPath(rosettaPath);
List synonymPaths = java.util.stream.Stream.of(«mapperSynGroup.toSynonymLastElementName.join("\"", "\", \"", "\"", [it])»)
.map(Path::parse)
.map(parentPath::append)
.collect(Collectors.toList());
return new java.util.AbstractMap.SimpleEntry<>(modelPath, new «mapperClassName»(modelPath, synonymPaths, mappingContext));
}
«ENDFOR»
'''
}
private def builderChainForMapper(List attributePath, SynonymMap mapping) '''
Path rosettaPath = getRosettaPath();
«var attList = attributePath.removeLast»
«var lastBuilderName = "getUnderlying()"»
«var nextBuilderName =""»
«FOR att : attList»
«IF (att.cardinalityIsListValue)»
int «att.name»Index = Math.max(0, getMappingListIndex(«lastBuilderName».get«att.name.toFirstUpper»(), «mergeSynonyms(attributePath, mapping).asList») - 1);
«att.type.expandedTypeToBuilder» «nextBuilderName=att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»(«att.name»Index);
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»", «att.name»Index));
«ELSE»
«att.type.expandedTypeToBuilder» «nextBuilderName = att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»();
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDIF»
«{lastBuilderName = nextBuilderName;""}»
«ENDFOR»
rosettaPath = rosettaPath.addElement(new PathElement("«attributePath.last.name»"));
'''
def getRemoveLastReference(List attributes) {
if (attributes.isParentMeta) {
return attributes.removeLast
}
return attributes
}
def isMeta(ExpandedAttribute attribute) {
val clazz = attribute?.type?.toJavaClass
if (clazz === null) {
return false
}
return FieldWithMeta.isAssignableFrom(clazz)
}
def isParentMeta(List attributes) {
attributes.parentAttribute?.isMeta
}
def getParentAttribute(List attributes) {
attributes.size > 1 ? attributes.get(attributes.size - 2) : null
}
def toJavaClass(ExpandedType expandedType) {
val packageName = expandedType.model !== null ? expandedType.model.name + "." : ""
val className = packageName + expandedType.name
Thread.currentThread.contextClassLoader.loadClass(className)
}
private def supplier(List atts) {
if (atts.exists[cardinalityIsListValue]) {
'''Optional.empty()''' // if there is multi-cardinality on the path then a new value will be created every time - the supplier of the existing value should be null
} else {
'''
Optional.of(getUnderlying())
«FOR name : atts.map[AttributeGroup.attributeName(it)]»
.map(bu -> bu.get«name.toFirstUpper»())
«ENDFOR»
'''
}
}
private def romHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, String packageName) {
val last = mappedGroup.group.attributePath.last
if (mappedGroup.group.attributePath.exists[cardinalityIsListValue]) {
romHandlerList(mappedGroup, mapping, last, packageName)
} else {
romHandlerSingle(mappedGroup, mapping, last, packageName)
}
}
private def romCondHandler(AttributeGroupMapping mappedGroup, SynonymMap mapping, String packageName) {
val last = mappedGroup.group.attributePath.last
if (mappedGroup.group.attributePath.exists[cardinalityIsListValue]) {
romCondHandlerList(mappedGroup, mapping, last, packageName)
} else {
romCondHandlerSingle(mappedGroup, mapping, packageName)
}
}
private def romHandlerSingle(AttributeGroupMapping mappedGroup, SynonymMap mapping, ExpandedAttribute last,
String packageName) '''
private «mappedGroup.mappings.pFullNameT(packageName)» «mappedGroup.group.toHandlerMethodName»_0(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«mappedGroup.group.toAttPathName»Handler = useOrNew(«mappedGroup.group.toHandlerFieldName»,
«romSupllier(mappedGroup, mapping, last, packageName)»
return «mappedGroup.group.toHandlerFieldName»;
}
'''
private def romCondHandlerSingle(AttributeGroupMapping mappedGroup, SynonymMap mapping, String packageName) '''
private «mappedGroup.mappings.pFullNameT(packageName)» «mappedGroup.group.toCondHandlerMethodName»(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
Path rosettaPath = getRosettaPath();
«FOR att : mappedGroup.group.attributePath»
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDFOR»
«mappedGroup.mappings.pFullNameT(packageName)» handler = new «mappedGroup.mappings.pFullNameT(packageName)»(injector, mappingContext);
handler.setRosettaPath(rosettaPath);
handler.setUnderlying(«mappedGroup.group.toAttPathName»Values.computeIfAbsent(localPath, a -> handler.getUnderlying()));
return handler;
}
'''
private def romHandlerList(AttributeGroupMapping mappedGroup, SynonymMap mapping, ExpandedAttribute last,
String packageName) '''
private «mappedGroup.mappings.pFullNameT(packageName)» «mappedGroup.group.toHandlerMethodName»_0(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«mappedGroup.mappings.pFullNameT(packageName)» handler = findOrNew(«mappedGroup.group.toHandlerFieldName», "«last.name»", localPath,
«romSupllier(mappedGroup, mapping, last, packageName)»
return handler;
}
'''
private def romCondHandlerList(AttributeGroupMapping mappedGroup, SynonymMap mapping, ExpandedAttribute last,
String packageName) '''
private «mappedGroup.mappings.pFullNameT(packageName)» «mappedGroup.group.toCondHandlerMethodName»(Path localPath, Path parentPath) {
Path xmlPath = parentPath.addElement(localPath.getLastElement());
«mappedGroup.mappings.pFullNameT(packageName)» handler = findOrNew(«mappedGroup.group.toHandlerFieldName», "«last.name»", localPath,
() -> {
Path rosettaPath = getRosettaPath();
«mappedGroup.mappings.pFullNameT(packageName)» result = new «mappedGroup.mappings.pFullNameW(packageName)»<>(injector, mappingContext);
result.setUnderlying(«mappedGroup.group.toAttPathName»Values.computeIfAbsent(localPath, a -> «mappedGroup.mappings.rosetta.name».builder()));
«var cardUsed=false»
// Model index is guessed at the number of conditional options, e.g., if there's 1 option, then the index would be 0
int index = «mappedGroup.group.toAttPathName»Values.keySet().size() - 1;
«FOR att : mappedGroup.group.attributePath»
«IF att.cardinalityIsListValue && !cardUsed»
rosettaPath = rosettaPath.addElement(new PathElement("«{cardUsed=true;att.name}»", index));
«ELSE»
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDIF»
«ENDFOR»
result.addXmlPath(xmlPath);
result.setRosettaPath(rosettaPath);
return result;
});
return handler;
}
'''
private def CharSequence romSupllier(AttributeGroupMapping mappedGroup, SynonymMap mapping, ExpandedAttribute last,
String packageName) '''
()-> {«firstMultipleBuilder(mappedGroup.group.attributePath, mapping)» chainBuilder;
«builderChain(mappedGroup.group.attributePath, mapping)»
«penultimateBuilder(mappedGroup.group.attributePath, mapping)» penultimateBuilder;
«var lastBuilderName = "chainBuilder"»
«var nextBuilderName =""»
«««FOR att:mappedGroup.group.postFirstMultiple»
«FOR att : mappedGroup.group.attributePath.subList(mappedGroup.group.toFirstMultiple.size, mappedGroup.group.attributePath.size-1)»
«IF (att.cardinalityIsListValue)»
int «att.name»Index = sizeOf(«lastBuilderName».get«att.name.toFirstUpper»());
«att.type.expandedTypeToBuilder» «nextBuilderName=att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»(«att.name»Index);
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»", «att.name»Index));
«ELSE»
«att.type.expandedTypeToBuilder» «nextBuilderName = att.type.name.toFirstLower+"Builder"» = «lastBuilderName».getOrCreate«att.name.toFirstUpper»();
rosettaPath = rosettaPath.addElement(new PathElement("«att.name»"));
«ENDIF»
//«lastBuilderName = nextBuilderName»
«ENDFOR»
penultimateBuilder = «lastBuilderName»;
«mappedGroup.mappings.pFullNameT(packageName)» result = new «mappedGroup.mappings.pFullNameT(packageName)»(injector, mappingContext);
«IF last.cardinalityIsListValue»
rosettaPath = rosettaPath.addElement(new PathElement("«last.name»", sizeOf(penultimateBuilder.get«last.name.toFirstUpper»())));
penultimateBuilder.add«last.name.toFirstUpper»(result.getUnderlying());
«ELSE»
rosettaPath = rosettaPath.addElement(new PathElement("«last.name»"));
penultimateBuilder.set«last.name.toFirstUpper»(result.getUnderlying());
«ENDIF»
result.addXmlPath(xmlPath);
result.setRosettaPath(rosettaPath);
return result;
});
'''
private def generateBuilderProxy(SynonymMap mapping) {
val conditionedPaths = mapping.attributeGroups.filter[synonymGroups.exists[!conditions.isEmpty]].toList
if (mapping.superMap !== null) {
conditionedPaths.addAll(mapping.superMap.attributeGroups.filter[synonymGroups.exists[!conditions.isEmpty]])
}
return generateBuilderProxy(mapping, conditionedPaths);
}
private def generateBuilderProxy(SynonymMap mapping, List conditionedPaths) '''
private class BuilderProxy extends «mapping.rosetta.toBuilder»Impl {
AtomicBoolean evaluatedConditions = new AtomicBoolean();
@Override
public «mapping.rosetta.fullname» build() {
evaluateConditions();
if (super.hasData()) {
return super.build();
}
return null;
}
@Override
public boolean hasData() {
evaluateConditions();
return super.hasData();
}
@Override
public void process(RosettaPath path, BuilderProcessor processor) {
evaluateConditions();
super.process(path, processor);
}
«buildEvaluateConditions(mapping, conditionedPaths)»
}
'''
protected def CharSequence buildEvaluateConditions(SynonymMap mapping, Iterable conditionedPaths) {
var counterPerAttribute = new HashMap
// more than once makes no diff here
for (path : conditionedPaths) {
counterPerAttribute.put(path.toAttPathName.toFirstUpper, 0);
}
val methods = new LinkedHashMap
val methodOrder = TreeMultimap.create
// useful for breakpoints
// if (mapping.rosetta.name=="xyz") {
// println()
// }
for (path : conditionedPaths.sortBy[attsToString]) {
for (group : path.synonymGroups.filter[!conditions.isEmpty]) {
for (cond : group.conditions) {
// assigning counter per path name
val perPathCounter = counterPerAttribute.get(path.toAttPathName.toFirstUpper)
val methodName = 'evaluate' + path.toAttPathName.toFirstUpper + perPathCounter
counterPerAttribute.put(path.toAttPathName.toFirstUpper, perPathCounter + 1);
val priority = if(cond.condition.isEmpty) TestPriority.Last else cond.condition.minBy[priority].priority
methodOrder.put(priority, methodName)
if (cond.condition.forall[paths.forall[synonymPath.forall[cardinality === null || cardinality == Cardinality.SINGLE]]]) {
methods.put(methodName, evalSingleCardinality(cond, mapping, path.attributePath, group.toPathList, perPathCounter))
}
else if (!cond.condition.filter(SynonymPathTest).empty && !cond.condition.filter(SynonymBinaryTest).empty) {
methods.put(methodName, evalPathAndTest(cond, path.attributePath, group.toPathList, perPathCounter));
}
else {
methods.put(methodName, evalMultiCardinality(mapping, cond, path))
}
}
}
}
'''
«FOR e : methods.entrySet»
private void «e.key»() {
«e.value»
}
«ENDFOR»
private void evaluateConditions() {
if (evaluatedConditions.compareAndSet(false, true)) {
«FOR e : methodOrder.entries»
«e.value»();
«ENDFOR»
}
}
'''
}
protected def CharSequence evalMultiCardinality(SynonymMap mapping, SynonymCondition cond, AttributeGroup group) '''
«IF cond.condition.exists(SynonymRosettaPathTest)»
Path rosettaPath = getRosettaPath()«FOR att:group.attributePath.dropMeta».addElement(new PathElement("«att.getName»"))«ENDFOR»;
if («FOR test:cond.condition.filter(SynonymRosettaPathTest) SEPARATOR " && "»GeneratorPathUtil.matches(rosettaPath, "«test.path.toDotSep»")«ENDFOR») {
«evalPathFiltering(cond, group, mapping)»
}
«ELSE»
«evalPathFiltering(cond, group, mapping)»
«ENDIF»
'''
def List dropMeta(List attributes) {
val result = newArrayList
for (var i = 0; i < attributes.length; i++) {
if (attributes.size > 0 && attributes.get(i).attributeName.equals("value") && attributes.get(i-1).isMeta) {
// skip the value artificial field
} else if (attributes.get(i).attributeName.equals("meta")) {
i++ // skip meta fields
} else {
result.add(attributes.get(i));
}
}
result
}
protected def synonymPathHasMultipleCard(SynonymExistsTest synonymExistsTest) {
synonymExistsTest.path.synonymPath.exists[cardinality === Cardinality.MULTIPLE]
}
protected def CharSequence evalPathFiltering(SynonymCondition cond, AttributeGroup group, SynonymMap mapping) '''
// evaluate path filtering
Set paths = null;
«var initialSet=false»
«FOR test : cond.condition.filter(SynonymBinaryTest)»
// SynonymBinaryTest
// This next line hard codes a lot of stuff that probably needs to depend on the input
«IF !initialSet»paths = («{initialSet=true;""}»«ELSE» paths.retainAll(«ENDIF»«toPathName(test.leftPath)»Values.entrySet()
.stream()
.filter(e -> areEqual(e.getValue(), «test.rightLiteral.handleWhenRight»))
.map(e -> e.getKey())
.collect(Collectors.toSet()));
«ENDFOR»
«FOR test : cond.condition.filter(SynonymExistsTest).filter[synonymPathHasMultipleCard]»
// SynonymExistsTest
«IF !initialSet»paths = («{initialSet=true;""}»«ELSE» paths.retainAll(«ENDIF»«toPathName(test.path)»Values.entrySet()
.stream()
.filter(e -> areEqual(e.getValue(), "Exists"))
.map(e -> e.getKey())
.collect(Collectors.toSet()));
«ENDFOR»
«FOR test : cond.condition.filter(SynonymAbsentTest)»
// SynonymAbsentTest
«IF !initialSet»paths = («{initialSet=true;""}»«ELSE» paths.removeAll(«ENDIF»«toPathName(test.path)»Values.entrySet()
.stream()
.filter(e -> areEqual(e.getValue(), "Exists"))
.map(e -> e.getKey())
.collect(Collectors.toSet()));
«ENDFOR»
«IF cond.condition.exists[c|!(c instanceof SynonymAbsentTest)]»
«IF cond.setToValue === null && cond.condition.exists(SynonymExistsTest) && IngesterGeneratorUtils.isConditionalMappingPredicatedOnDifferentPath(group.synonymGroups.flatMap[synonymValues].toList, cond.condition.filter(SynonymExistsTest).map[path].toList)»
// (a) «cond.condition.map[class.simpleName].join(",")»
if (!paths.isEmpty()) {
int index = 0;
«group.attributePath.toAttPathName»Values.values().forEach(v -> «setterChain(group.attributePath)»(v));
}
«ELSE»
for (Path p : paths) {
// in evalPathFiltering
«IF group.attributePath.multipleCardinality»
int index = p.cardinality();
«ENDIF»
«IF cond.setToValue !== null && cond.condition.filter[priority === TestPriority.Last].empty»
«generateAnonHandler(group, mapping, group.attributePath.last, "p", "p")»
builder.«setter(group.attributePath)»(«evaluate(cond.setToValue)»);
«ELSEIF cond.setToValue !== null»
«setterChain(group.attributePath)»(«evaluate(cond.setToValue)»);
«ELSEIF group.attributePath.last.builtInType || !group.attributePath.multipleCardinality»
«group.attributePath.toAttPathName»Values.entrySet()
.stream()
.filter(e -> e.getKey().fullStartMatches(p))
.map(e -> e.getValue())
.forEach(v -> «mergeAttribute(group.attributePath)»);
«ELSE»
«group.attributePath.toAttPathName»Handlers.getAllHandlers()
.stream()
.filter(h -> h.getXmlPathsToHere().stream().anyMatch(k -> k.fullStartMatches(p)))
.forEach(v -> «setterChain(group.attributePath)»(v.getUnderlying()));
«ENDIF»
}
«ENDIF»
«ELSE»
// (b) «cond.condition.map[class.simpleName].join(",")»
«IF IngesterGeneratorUtils.isConditionalMappingPredicatedOnDifferentPath(group.synonymGroups.flatMap[synonymValues].toList, cond.condition.filter(SynonymAbsentTest).map[path].toList)»
«IF group.attributePath.getterMultipleCardinality»
// getOrCreateFoo(MAX_VALUE) creates new list item at next available index
int index = Integer.MAX_VALUE;
«ENDIF»
if (paths.isEmpty()) {
«group.attributePath.toAttPathName»Values.values().forEach(v -> «setterChain(group.attributePath)»(v));
}
«ELSE»
«IF group.attributePath.getterMultipleCardinality»
// getOrCreateFoo(MAX_VALUE) creates new list item at next available index
int index = Integer.MAX_VALUE;
«ENDIF»
for (Map.Entry entry : «group.attributePath.toAttPathName»Values.entrySet()) {
if (!paths.stream().anyMatch(p -> entry.getKey().fullStartMatches(p))) {
«setterChain(group.attributePath)»(entry.getValue());
}
}
«ENDIF»
«ENDIF»
'''
protected def CharSequence mergeAttribute(List attributePath) '''
«IF attributePath.last.dataType»«attributeGetters(attributePath)»merge(v, new IngestMerger())«ELSE»«setterChain(attributePath)»(v)«ENDIF»
'''
protected def CharSequence evalSingleCardinality(SynonymCondition cond, SynonymMap mapping, List attributePath, String pathList, int attributeConditionIndex) '''
// evaluate xml path that has single cardinality
if («cond.handleWhen(pathList, attributePath)») {
«conditionTrue(cond, attributePath, pathList, attributeConditionIndex)»
}
'''
/**
* Code generate the conditional mapping predicate for xml path that is single cardinality
*/
private def handleWhen(SynonymCondition cond, String pathList, List attributePath) {
if (cond.setToValue!==null && cond.condition.isEmpty) {
// `set to` with no predicate test, e.g. a default condition
'''!«supplier(attributePath)».isPresent()'''
}
else if (!cond.condition.isEmpty) {
// code generate conditional mapping predicate
'''«FOR test:cond.condition SEPARATOR " && "»«handleTest(test, pathList, attributePath)»«ENDFOR»'''
}
else {
// no `set to` and no predicate, this should never happen
'''/* no `set to` and `set when` condition */ true'''
}
}
protected def CharSequence evalPathAndTest(SynonymCondition cond, List attributePath, String pathList, int attributeConditionIndex) '''
«IF pathList !== ""»
// evaluate path and test
«conditionTrue(cond, attributePath, pathList, attributeConditionIndex)»
«ENDIF»
'''
/**
* If the conditional mapping predicate resolves to true, this method code generates the logic.
*/
private def conditionTrue(SynonymCondition cond, List attributePath, String pathList, int attributeConditionIndex) '''
«IF cond.setToValue!==null»
// `set to` conditional mapping
«IF cond.condition.exists(SynonymPathTest)»
// (c) «cond.condition.map[class.simpleName].join(",")»
«val underlying = attributePath.toFirstMultiple»
«IF underlying.size>0»
// nested set to (synonym path predicate)
«IF attributePath.postFirstMultiple.getterMultipleCardinality»
int index=Integer.MAX_VALUE;
«ENDIF»
evaluateConditionalPaths(
«IF attributePath.multipleCardinality»«underlying.map[name].join("").toFirstLower»Underlyers,
«ELSE»get«underlying.map[name].join("").toFirstUpper»UnderlyerB(),
getXmlPathsToHere(),«ENDIF»
handler -> handler.getUnderlying().«attributePath.postFirstMultiple.parentGetters»set«attributePath.last.name.toFirstUpper»(«evaluate(cond.setToValue)»),
ImmutableList.of("«cond.condition.filter(SynonymPathTest).map[getPathWithDots].join("\",\"")»")«IF cond.condition.exists(SynonymBinaryTest)»,
«toPathMatchBinary(cond.condition.filter(SynonymBinaryTest))»«ENDIF»);
«ELSE»
// set to (synonym path predicate)
Set paths = getXmlPathsToHere();
if (paths.stream().anyMatch(p -> «FOR test:cond.condition.filter(SynonymPathTest) SEPARATOR " || "»p.endsWith(Path.parse("«test.getPathWithDots»").getPathNames())«ENDFOR»)) {
set«attributePath.last.name.toFirstUpper»(«evaluate(cond.setToValue)»);
return;
}
«ENDIF»
«ELSE»
// (d) «cond.condition.map[class.simpleName].join(",")»
«IF attributePath.multipleCardinality»
// set to (rosetta path predicate) that has multiple cardinality
«IF attributePath.getterMultipleCardinality»
int relativeIndex = «attributeConditionIndex»;
// TODO This line needs further explanation
int index = (relativeIndex == 0 || relativeIndex == 1) ? 0 : 1;
«ENDIF»
«setterChain(attributePath)»(«evaluate(cond.setToValue)»);
«ELSE»
// set to (rosetta path predicate) that has single cardinality
«setterChain(attributePath)»(«evaluate(cond.setToValue)»);
«ENDIF»
«ENDIF»
«ELSEIF pathList != ""»
// `set when` conditional mapping
«IF cond.condition.exists(SynonymPathTest)»
// (e) «cond.condition.map[class.simpleName].join(",")»
// set rosetta path that has single cardinality
Set paths = getXmlPathsToHere();
if (paths.stream().anyMatch(p -> «FOR test:cond.condition.filter(SynonymPathTest) SEPARATOR " || "»p.endsWith(Path.parse("«test.getPathWithDots»").getPathNames())«ENDFOR»)) {
«conditionSetterSingleCardinality(attributePath, pathList)»
}
«ELSE»
// (f) «cond.condition.map[class.simpleName].join(",")»
«IF attributePath.multipleCardinality»
// set rosetta path that has multiple cardinality
«IF attributePath.getterMultipleCardinality»
// getOrCreateFoo(MAX_VALUE) creates new list item at next available index
int index = Integer.MAX_VALUE;
«ENDIF»
for («attributePath.last.type.expandedTypeToBuilder» b : GeneratorPathUtil.getValuesForPathEnding(«attributePath.toAttPathName»Values, ImmutableList.of("«pathList»"))) {
«setterChain(attributePath)»(b);
}
«ELSE»
«conditionSetterSingleCardinality(attributePath, pathList)»
«ENDIF»
«ENDIF»
«ENDIF»
'''
def conditionSetterSingleCardinality(List attributePath, String pathList) '''
// set rosetta path that has single cardinality
Optional<«attributePath.last.type.expandedTypeToBuilder»> val = GeneratorPathUtil.getValueForPath(«attributePath.toAttPathName»Values, ImmutableList.of("«pathList»"));
if (val.isPresent()) {
«IF attributePath.last.type.type»
«attributePath.last.type.expandedTypeToBuilder» builder = val.get();
new IngestMerger().run(builder, «getterChain(attributePath)»);
«setterChain(attributePath)»(builder);
«ELSE»
«setterChain(attributePath)»(val.get());
«ENDIF»
return;
}
'''
def toPathMatchBinary(Iterable tests) '''
ps -> «FOR test : tests SEPARATOR " && "»
GeneratorPathUtil.getPathMatchingValues(ps, «test.paths.last.toPathName»Values).stream().anyMatch(s -> «test.operator.toJava»(s,«toJava(test.rightPath, test.rightLiteral)»))
«ENDFOR»
'''
private def toPathList(SynonymGroup group) {
group.synonymValues.map[synonymPath.map[name].join(".")].join('''", "''')
}
private def setterChain(List path) '''«parentGetters(path)»«setter(path)»'''
private def getterChain(List path) '''«parentGetters(path)»«getter(path.last)»'''
private def parentGetters(List path) {
path.removeLast.join("", ".", ".", [a|getter(a)]);
}
private def attributeGetters(List path) {
path.join("", ".", ".", [a|getter(a)]);
}
private def String getter(ExpandedAttribute att) {
if (att.isMultiple) {
'''getOrCreate«att.name.toFirstUpper»(index)'''
} else {
'''getOrCreate«att.name.toFirstUpper»()'''
}
}
private def setter(
List path) '''«IF path.last.cardinalityIsSingleValue»set«ELSE»add«ENDIF»«path.last.name.toFirstUpper»'''
private def dispatch evaluate(RosettaMapTestExpression expression) {
throw new UnsupportedOperationException("Don't know how to evaluate expression " + expression.class)
}
private def dispatch evaluate(RosettaLiteral expression) {
return expression.stringValue
}
private def dispatch evaluate(RosettaEnumValueReference ref) '''
«ref.enumeration.fullname.trim».«EnumHelper.formatEnumName(ref.value.name)»'''
private def dispatch handleTest(SynonymTest test, String pathList, List attributePath) {
throw new UnsupportedOperationException("Don't know how to generate test for " + test.class)
}
private def dispatch handleTest(SynonymExistsTest test, String pathList, List attributePath) '''«test.path.toPathName»Value != null'''
private def dispatch handleTest(SynonymAbsentTest test, String pathList, List attributePath) '''«test.path.toPathName»Value == null'''
private def dispatch handleTest(extension SynonymBinaryTest test, String pathList, List attributePath)
'''«operator.toJava»(«toJava(leftPath, leftLiteral)»,«toJava(rightPath, rightLiteral)»)'''
private def dispatch handleTest(SynonymPathTest test, String pathList, List attributePath) { return "true" }
private def dispatch handleTest(SynonymRosettaPathTest test, String pathList, List attributePath)
'''GeneratorPathUtil.endsWith(getRosettaPath().«attributePath.map[name].map['addElement("'+it+'")'].join('.')».getParent(), "«test.path.toDotSepDropFirst»")'''
private def dispatch handleTest(SynonymConditionFuncTest test, String pathList, List attributePath) '''
injector.getInstance(«test.funcClassName».class)
.evaluate(getSynonymPathForConditionFunc(getXmlPathsToHere(), "«pathList»"),
formatPathWithNoIndexesAndSeparators(getRosettaPath()),
«IF test.predicatePathEndsWith !== null»getValuesFromConditionPath(«test.predicatePathEndsWith.map['"'+it+'"'].join(', ')»)«ELSE»null«ENDIF»)'''
private def toJava(SynonymValue path, RosettaMapTestExpression literal) {
if(path !== null) path.toPathName + "Value" else literal.handleWhenRight
}
private def toJava(String op) {
switch (op) {
case "=": "areEqual"
case "<>": "!areEqual"
default: throw new UnsupportedOperationException("test operator " + op + " not currently supported")
}
}
private def dispatch CharSequence toDotSep(
RosettaAttributeReference call) '''«call.receiver.toDotSep».«call.attribute.name»'''
private def dispatch CharSequence toDotSep(RosettaDataReference call) '''«call.data.name»'''
private def dispatch CharSequence toDotSepDropFirst(
RosettaAttributeReference call) '''«call.receiver.toDotSepDropFirst»«call.attribute.name».'''
private def dispatch CharSequence toDotSepDropFirst(RosettaDataReference call) ''''''
private dispatch def handleWhenRight(RosettaMapTestExpression right) {
throw new UnsupportedOperationException("Unsupported expression type " + right.class.simpleName)
}
private dispatch def handleWhenRight(RosettaStringLiteral right) '''"«right.value»"'''
private dispatch def handleWhenRight(RosettaBooleanLiteral right) '''Boolean.valueOf(«right.value»)'''
private dispatch def handleWhenRight(RosettaIntLiteral right) '''Integer.valueOf(«right.value»)'''
private dispatch def handleWhenRight(RosettaNumberLiteral right) '''BigDecimal.valueOf(«right.value»)'''
private def generateEnumParsers(SynonymMap mapping, Map generated, String childName,
Map synHashCache) {
if (generated.containsKey(mapping.rosetta.fullname)) {
return;
}
val prevHash = synHashCache.get(mapping.rosetta.fullname)
val newHash = mapping.hashForGeneration
if (prevHash === null || newHash != prevHash) {
var parser = '''
package «childName».«mapping.rosetta.packageName»;
import java.util.Map;
«basicEnumParserImports(mapping)»
public class «mapping.pNameW» extends EnumParseHandler<«mapping.rosettaName»> {
private final Map lookup;
«enumConstructor(mapping)»
@Override
protected «mapping.rosettaName» convert(String value) {
«mapping.rosettaName» enumValue = lookup.get(value);
if (enumValue!=null) return enumValue;
throw new RuntimeException("Could not find matching «mapping.rosetta.name» for [" + value + "]");
}
}
'''
generated.put(mapping.rosetta.fullname, new GenerationResult(false, parser))
synHashCache.put(mapping.rosetta.fullname, newHash)
} else {
generated.put(mapping.rosetta.fullname, new GenerationResult(true, null))
}
}
private def enumConstructor(SynonymMap mapping) '''
public «mapping.rosetta.name»ParseHandler(boolean allowsMultiple, boolean condition, boolean removeHtml) {
this(allowsMultiple, condition, removeHtml, null, null);
}
public «mapping.rosetta.name»ParseHandler(boolean allowsMultiple, boolean condition, boolean removeHtml, String patternMatch, String patternReplace) {
super(allowsMultiple, condition, removeHtml, patternMatch, patternReplace);
ImmutableMap.Builder builder = ImmutableMap.builder();
«FOR attrPathSynGroup : mapping.attributeGroups.sort»
«FOR synonymValue : attrPathSynGroup.getSynonymGroups.flatMap[sp|sp.synonymValues].sortBy[synonymPath.map[name].join]»
builder.put("«synonymValue.synonymPath.map[name].join»", «mapping.rosettaName».«EnumHelper.formatEnumName(attrPathSynGroup.getAttributePath.last.name)»);
«ENDFOR»
«ENDFOR»
lookup = builder.build();
}
'''
private def basicEnumParserImports(SynonymMap mapping) {
val rosettaModel = mapping.rosetta.eContainer as RosettaModel
val imports = newArrayList
imports.addAll("import com.google.common.collect.ImmutableMap",
"import com.regnosys.rosetta.translate.basic.EnumParseHandler",
"import com.regnosys.rosetta.common.translation.Path")
imports.add("import " + rosettaModel.name + "." + mapping.rosetta.name)
return imports.stream.sorted.distinct.collect(Collectors.joining(";\n", "", ";"))
}
static class UniqueFilter implements Predicate {
Set seen = newHashSet
Function propExtract
new(Function propExt) {
this.propExtract = propExt
}
override test(T t) {
return seen.add(propExtract.apply(t))
}
}
def String generateXmlFactory(String packageName, String factoryName, List generatorParamsList) '''
package «packageName»;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.HashMap;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import com.regnosys.rosetta.common.translation.MappingContext;
import com.regnosys.rosetta.common.util.UrlUtils;
import org.xml.sax.SAXException;
import com.google.inject.Injector;
import com.rosetta.model.lib.RosettaModelObject;
import com.regnosys.rosetta.translate.*;
«IF !generatorParamsList.map[topLevelTags].flatten.empty»
import java.util.Arrays;
«ENDIF»
import java.util.function.Function;
public class «factoryName» implements XmlHandlerFactory {
private final Function, HandlerSupplier>> factory;
private final Map, Schema> schemas = new HashMap<>();
private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
«FOR xsdUrl : generatorParamsList.map[schema]»
private static Schema «xsdUrl.urlToStaticVar» = newSchema("«IF xsdUrl.protocol == 'jar'»«Splitter.on('!/').split(xsdUrl.path).last»«ELSE»«StringEscapeUtils.escapeJava(UrlUtils.toPath(xsdUrl).toString)»«ENDIF»");
«ENDFOR»
private final Injector injector;
public «factoryName»(Injector injector) {
this.injector = injector;
this.factory = (mappingContext) -> {
Map, HandlerSupplier> handlerMap = new HashMap<>();
«FOR params : generatorParamsList»
«FOR rmo : params.rosettaEntities»
«IF params.topLevelTags.empty»
handlerMap.put(«rmo.fullname».class, (a, b) -> new «rmo.pFullNameT(params.getChildPackageName)»(injector, mappingContext));
«ELSE»
handlerMap.put(«rmo.fullname».class, (a, b) -> new RootHandler<>("«rmo.name»", new «rmo.pFullNameT(params.getChildPackageName)»(injector, mappingContext), Arrays.asList(«params.topLevelTags.map['"' + it + '"'].join(', ')»)));
«ENDIF»
«ENDFOR»
«ENDFOR»
return handlerMap;
};
«FOR params : generatorParamsList»
«FOR rmo : params.rosettaEntities»
schemas.put(«rmo.fullname».class, «params.schema.urlToStaticVar»);
«ENDFOR»
«ENDFOR»
}
@Override
public Map, HandlerSupplier> getHandlerSuppliers(MappingContext mappingContext) {
return factory.apply(mappingContext);
}
@Override
public Map, Schema> getSchemas() {
return schemas;
}
private static Path getXsdPath(String xsdFilePath) {
Path path = Paths.get(xsdFilePath);
if (Files.exists(path)) {
return path;
}
URL resource = «factoryName».class.getClassLoader().getResource(xsdFilePath);
if (resource != null) {
return UrlUtils.toPath(resource);
}
throw new IngestException("Error reading xsd file - " + xsdFilePath + " could not be found");
}
private static Schema newSchema(String schemaUrl) {
try {
return SCHEMA_FACTORY.newSchema(getXsdPath(schemaUrl).toUri().toURL());
} catch (SAXException | MalformedURLException e) {
throw new IngestException("Failed to create Schema URL for " + schemaUrl, e);
}
}
@Override
public Injector getInjector() {
return injector;
}
}
'''
def String generateJsonFactory(String packageName, String factoryName, List generatorParamsList) '''
package «packageName»;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.HashMap;
import com.google.inject.Injector;
import com.regnosys.rosetta.common.translation.MappingContext;
import com.rosetta.model.lib.RosettaModelObject;
import com.regnosys.rosetta.translate.*;
import com.regnosys.rosetta.translate.basic.*;
«IF !generatorParamsList.map[topLevelTags].flatten.empty»
import java.util.Arrays;
«ENDIF»
import java.util.function.Function;
public class «factoryName» implements JsonHandlerFactory {
private final Function, HandlerSupplier>> factory;
private final Injector injector;
public «factoryName»(Injector injector) {
this.injector = injector;
this.factory = (mappingContext) -> {
Map, HandlerSupplier> handlerMap = new HashMap<>();
«FOR params : generatorParamsList»
«FOR rmo : params.rosettaEntities»
«IF params.topLevelTags.empty»
handlerMap.put(«rmo.fullname».class, (a, b) -> new «rmo.pFullNameT(params.getChildPackageName)»(injector, mappingContext));
«ELSE»
handlerMap.put(«rmo.fullname».class, (a, b) -> new RootHandler<>("«rmo.name»", new «rmo.pFullNameT(params.getChildPackageName)»(injector, mappingContext), Arrays.asList(«params.topLevelTags.map['"' + it + '"'].join(', ')»)));
«ENDIF»
«ENDFOR»
«ENDFOR»
return handlerMap;
};
}
@Override
public Map, HandlerSupplier> getHandlerSuppliers(MappingContext mappingContext) {
return factory.apply(mappingContext);
}
@Override
public Injector getInjector() {
return injector;
}
}
'''
private def urlToStaticVar(URL u) {
UrlUtils.getFileName(u).replaceAll('[-,.]', '_').toUpperCase
}
private def removeLast(SynonymValue synonymValue) {
new SynonymValue(synonymValue.synonymPath.removeLast, synonymValue.mapsTo, synonymValue.isMeta)
}
private def removeLast(AttributeGroup group) {
new AttributeGroup(group.synonymGroups.map[removeLast], group.attributePath.removeLast)
}
private def removeLast(SynonymGroup group) {
new SynonymGroup(group.synonymValues.map[removeLast], group.conditions, group.mapperName, group.formatString, group.patternMatcher, group.patternReplace, group.removeHtml)
}
private def removeLast(List list) {
list.subList(0, list.size - 1)
}
private def penultimateBuilder(List attributes, SynonymMap mapping) {
if (attributes.size >= 2) {
return attributes.get(attributes.size - 2).type.expandedTypeToBuilder
} else {
return mapping.rosetta.toBuilder
}
}
/**
* @param atts - list of attributes represents a path (with attribute name/type for each path element).
* @returns the first attribute that has multiple cardinality
*/
private def firstMultipleBuilder(List attributes, SynonymMap mapping) {
var resAt = attributes.toFirstMultiple;
if (resAt.size >= 1) {
return resAt.last.type.expandedTypeToBuilder
} else {
return mapping.rosetta.toBuilder
}
}
}