Skip to content

Commit

Permalink
added custom groovy binding script (#12)
Browse files Browse the repository at this point in the history
* added custom groovy binding script

* created Attribute Binder abstraction; different attribute binder for different cases

* moved bindRequest type check into AttributeBindRequest

* added description to the custom binding script in the UI
  • Loading branch information
mariodavid committed Apr 7, 2018
1 parent a393ac1 commit 7fc9b0d
Show file tree
Hide file tree
Showing 23 changed files with 514 additions and 194 deletions.
3 changes: 2 additions & 1 deletion modules/core/db/init/hsql/10.create-db.sql
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ create table DDCDI_IMPORT_ATTRIBUTE_MAPPER (
DELETE_TS timestamp,
DELETED_BY varchar(50),
--
CONFIGURATION_ID varchar(36) not null,
ENTITY_ATTRIBUTE varchar(255) not null,
FILE_COLUMN_NUMBER integer not null,
FILE_COLUMN_ALIAS varchar(255),
CONFIGURATION_ID varchar(36) not null,
CUSTOM_ATTRIBUTE_BIND_SCRIPT longvarchar,
--
primary key (ID)
)^
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table DDCDI_IMPORT_ATTRIBUTE_MAPPER add column CUSTOM_ATTRIBUTE_BIND_SCRIPT longvarchar ;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.chile.core.model.MetaPropertyPath
import com.haulmont.cuba.core.entity.Entity
import de.diedavids.cuba.dataimport.data.SimpleDataLoader
import de.diedavids.cuba.dataimport.service.MultipleAssociationValuesFoundException
import groovy.util.logging.Slf4j


@Slf4j
class AssociationAttributeBinder implements AttributeBinder {

SimpleDataLoader simpleDataLoader

@Override
void bindAttribute(Entity entity, AttributeBindRequest bindRequest) {
try {
def value = handleAssociationAttribute(bindRequest.importEntityPropertyPath, bindRequest.rawValue, entity)
entity.setValueEx(bindRequest.entityAttributePath, value)
}
catch (MultipleAssociationValuesFoundException e) {
e.dataRow = bindRequest.dataRow
e.propertyPath = bindRequest.importEntityPropertyPath
log.warn("Multiple associations found for data row: [${e.dataRow}] and attribute: ${e.propertyPath} with value ${e.value}. Found associations: ${e.allResults}. Will be ignored.")
}
}

private void handleAssociationAttribute(MetaPropertyPath path, String rawValue, Entity entity) {
def propertyPathFromAssociation = path.path.drop(1)
def propertyPath = propertyPathFromAssociation.join('.')
def associationJavaType = path.metaProperties[0].javaType
def associationProperty = path.metaProperties[0].name

def associationValue = loadAssociationValue(associationJavaType, propertyPath, rawValue)
entity.setValueEx(associationProperty, associationValue)
}

private loadAssociationValue(Class<?> associationJavaType, String propertyPath, String rawValue) {
def allResults = simpleDataLoader.loadAllByProperty(associationJavaType, propertyPath, rawValue)

if (allResults.size() > 1) {
throw new MultipleAssociationValuesFoundException(value: rawValue, allResults: allResults)
} else {
return allResults.first()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.chile.core.model.MetaClass
import com.haulmont.chile.core.model.MetaProperty
import com.haulmont.chile.core.model.MetaPropertyPath
import com.haulmont.cuba.core.global.Metadata
import de.diedavids.cuba.dataimport.dto.DataRow
import de.diedavids.cuba.dataimport.entity.ImportAttributeMapper
import de.diedavids.cuba.dataimport.entity.ImportConfiguration

class AttributeBindRequest {

private static final String PATH_SEPARATOR = '.'

ImportConfiguration importConfiguration
DataRow dataRow
ImportAttributeMapper importAttributeMapper

Metadata metadata


String getRawValue() {
((String) dataRow[importAttributeMapper.fileColumnAlias]).trim()
}

String getImportEntityClassName() {
importConfiguration.entityClass
}

String getEntityAttributePath() {
importAttributeMapper.entityAttribute - (importEntityClassName + PATH_SEPARATOR)
}

MetaClass getImportEntityMetaClass() {
metadata.getClass(importEntityClassName)
}

MetaPropertyPath getImportEntityPropertyPath() {
importEntityMetaClass.getPropertyPath(entityAttributePath)
}

MetaProperty getMetaProperty() {
importEntityMetaClass.getPropertyNN(entityAttributePath)
}

boolean isCustomScriptBindingRequest() {
importAttributeMapper.customAttributeBindScript
}

boolean isAssociationBindingRequest() {
importEntityPropertyPath.metaProperties.size() > 1
}

boolean isDatatypeBindingRequest() {
metaProperty.type == MetaProperty.Type.DATATYPE
}

boolean isEnumBindingRequest() {
metaProperty.type == MetaProperty.Type.ENUM
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.entity.Entity

interface AttributeBinder {

void bindAttribute(Entity entity, AttributeBindRequest bindRequest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.global.DataManager
import com.haulmont.cuba.core.global.Scripting
import de.diedavids.cuba.dataimport.data.SimpleDataLoader
import org.springframework.stereotype.Component

import javax.inject.Inject

@Component
class AttributeBinderFactory {

@Inject
SimpleDataLoader simpleDataLoader

@Inject
Scripting scripting

@Inject
DataManager dataManager

AttributeBinder createAttributeBinderFromBindingRequest(AttributeBindRequest bindRequest) {

if (bindRequest.customScriptBindingRequest) {
return new CustomScriptAttributeBinder(
scripting: scripting,
dataManager: dataManager
)
} else if (bindRequest.isAssociationBindingRequest()) {
return new AssociationAttributeBinder(
simpleDataLoader: simpleDataLoader
)
} else if (bindRequest.isDatatypeBindingRequest()) {
return new DatatypeAttributeBinder()
} else if (bindRequest.isEnumBindingRequest()) {
return new EnumAttributeBinder()
}

throw new IllegalStateException("No valid Attribute binder for AttributeBindRequest: $bindRequest found")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.entity.Entity
import com.haulmont.cuba.core.global.DataManager
import com.haulmont.cuba.core.global.Scripting
import groovy.util.logging.Slf4j

@Slf4j
class CustomScriptAttributeBinder implements AttributeBinder {

Scripting scripting

DataManager dataManager

@Override
void bindAttribute(Entity entity, AttributeBindRequest bindRequest) {
Binding binding = createScriptBinding(bindRequest)
tryToExecuteCustomScriptAndSetValue(bindRequest, binding, entity)
}

private void tryToExecuteCustomScriptAndSetValue(AttributeBindRequest bindRequest, Binding binding, Entity entity) {
try {
def value = getValue(bindRequest, binding)
entity.setValueEx(bindRequest.entityAttributePath, value)
}
catch (Exception e) {
log.error("Error while executing custom binding script: ${e.getClass()}", e)
}
}

private Binding createScriptBinding(AttributeBindRequest bindRequest) {
new Binding(
importConfiguration: bindRequest.importConfiguration,
importAttributeMapper: bindRequest.importAttributeMapper,
dataRow: bindRequest.dataRow,
entityAttribute: bindRequest.entityAttributePath,
dataManager: dataManager,
rawValue: bindRequest.rawValue
)
}

private Object getValue(AttributeBindRequest bindRequest, Binding binding) {
scripting.evaluateGroovy(bindRequest.importAttributeMapper.customAttributeBindScript, binding)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.entity.Entity
import de.diedavids.cuba.dataimport.dto.DataRow
import de.diedavids.cuba.dataimport.entity.ImportConfiguration
import groovy.util.logging.Slf4j

import java.text.SimpleDateFormat

@Slf4j
class DatatypeAttributeBinder implements AttributeBinder {


@Override
void bindAttribute(Entity entity, AttributeBindRequest bindRequest) {
entity.setValueEx(bindRequest.entityAttributePath, getValue(bindRequest))
}

private getValue(AttributeBindRequest bindRequest) {
switch (bindRequest.metaProperty.javaType) {
case Integer: return getIntegerValue(bindRequest.rawValue, bindRequest.dataRow)
case Double: return getDoubleValue(bindRequest.rawValue, bindRequest.dataRow)
case Date: return getDateValue(bindRequest.importConfiguration, bindRequest.rawValue)
case Boolean: return getBooleanValue(bindRequest.importConfiguration, bindRequest.rawValue, bindRequest.dataRow)
case String: return getStringValue(bindRequest.rawValue)
}
}

private Integer getIntegerValue(String rawValue, DataRow dataRow) {
try {
return Integer.parseInt(rawValue)
}
catch (NumberFormatException e) {
log.warn("Number could not be read: '$rawValue' in [$dataRow]. Will be ignored.")
}
}

@SuppressWarnings('BooleanMethodReturnsNull')
private Boolean getBooleanValue(ImportConfiguration importConfiguration, String rawValue, DataRow dataRow) {

def customBooleanTrueValue = importConfiguration.booleanTrueValue
def customBooleanFalseValue = importConfiguration.booleanFalseValue

if (customBooleanTrueValue || customBooleanFalseValue) {
if (customBooleanTrueValue.equalsIgnoreCase(rawValue)) {
return true
}

return customBooleanFalseValue.equalsIgnoreCase(rawValue) ? false : null
}

try {
return Boolean.parseBoolean(rawValue)
}
catch (NumberFormatException e) {
log.warn("Number could not be read: '$rawValue' in [$dataRow]. Will be ignored.")
}
}

private String getStringValue(String rawValue) {
rawValue
}

@SuppressWarnings('SimpleDateFormatMissingLocale')
private Date getDateValue(ImportConfiguration importConfiguration, String rawValue) {
SimpleDateFormat formatter = new SimpleDateFormat(importConfiguration.dateFormat)
formatter.parse(rawValue)
}

private Double getDoubleValue(String rawValue, DataRow dataRow) {
try {
return Double.parseDouble(rawValue)
}
catch (NumberFormatException e) {
log.warn("Number could not be read: '$rawValue' in [$dataRow]. Will be ignored.")
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.entity.Entity
import com.haulmont.cuba.core.global.Metadata
import de.diedavids.cuba.dataimport.dto.DataRow
import de.diedavids.cuba.dataimport.entity.ImportAttributeMapper
import de.diedavids.cuba.dataimport.entity.ImportConfiguration
import groovy.util.logging.Slf4j
import org.springframework.stereotype.Component

import javax.inject.Inject

@Slf4j
@Component(EntityBinder.NAME)
class EntityBinderImpl implements EntityBinder {

@Inject
Metadata metadata

@Inject
AttributeBinderFactory attributeBinderFactory


@Override
Entity bindAttributes(ImportConfiguration importConfiguration, DataRow dataRow, Entity entity) {

importConfiguration.importAttributeMappers.each { ImportAttributeMapper importAttributeMapper ->
bindAttribute(importConfiguration, dataRow, entity, importAttributeMapper)
}

entity
}

private void bindAttribute(ImportConfiguration importConfiguration, DataRow dataRow, Entity entity, ImportAttributeMapper importAttributeMapper) {

AttributeBindRequest bindRequest = createAttributeBindRequest(importConfiguration, dataRow, importAttributeMapper)
AttributeBinder binder = attributeBinderFactory.createAttributeBinderFromBindingRequest(bindRequest)

binder.bindAttribute(entity, bindRequest)

}

AttributeBindRequest createAttributeBindRequest(ImportConfiguration importConfiguration, DataRow dataRow, ImportAttributeMapper importAttributeMapper) {
new AttributeBindRequest(
importConfiguration: importConfiguration,
importAttributeMapper: importAttributeMapper,
dataRow: dataRow,
metadata: metadata
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.diedavids.cuba.dataimport.binding

import com.haulmont.cuba.core.entity.Entity
import groovy.util.logging.Slf4j

@Slf4j
class EnumAttributeBinder implements AttributeBinder {

@Override
void bindAttribute(Entity entity, AttributeBindRequest bindRequest) {
entity.setValueEx(bindRequest.entityAttributePath, getValue(bindRequest))
}

private getValue(AttributeBindRequest bindRequest) {
bindRequest.metaProperty.javaType.fromId(bindRequest.rawValue)
}

}
Loading

0 comments on commit 7fc9b0d

Please sign in to comment.