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.
package com.paypal.butterfly.extensions.api;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.paypal.butterfly.extensions.api.exception.TransformationDefinitionException;
import com.paypal.butterfly.extensions.api.exception.TransformationUtilityException;
/**
* Transformation utility to perform multiple transformation operations. Multiple transformation operations
* are defined based on an operation template and two other factors, that could be applied
* exclusively or together. They are:
*
*
Multiple files: multiple operations are defined based on multiple files specified as a
* list, held as one or more transformation context attribute, and set via {@link #setFiles(String...)}
*
Multiple configurations: multiple operations are defined based on different configurations,
* set via {@link #setProperties(String, String)}
*
*
* In other words, there are two possible ways to define multiple operations: multiple
* files, or multiple configurations (different property values). It is also possible
* to combine both, resulting in multiple operations to be executed against multiple files and
* with multiple configurations.
*
* Important: when running against multiple files, any path set to this operation,
* either relative or absolute, will be ignored, and set later at transformation time based on
* the dynamically set multiple files. When running with multiple configurations, the properties set
* during transformation time will override any value that could have been set during definition time
*
* @author facarvalho
*/
// TODO Analyze if CopyFiles and DeleteFiles could be removed due to this utility
public class MultipleOperations extends TransformationUtility implements TransformationUtilityParent {
private static final Logger logger = LoggerFactory.getLogger(MultipleOperations.class);
private static final String DESCRIPTION = "Perform operation %s against multiple files";
// Array of transformation context attributes that hold list of Files
// which the transformation operation should perform against.
// If more than one attribute is specified, all list of files will be
// combined into a single one.
private String[] filesAttributes;
// The operation Java bean property name to be set during transformation time,
// whose value will differentiate each operation
private String propertyName;
// The name of the transformation context attribute that holds a collection of
// values to be each set individually (as the property value) to each operation of
// a set of multiple operations. These values are set right before execution. If
// the transformation context attribute value is not a collection, then it will be
// used as a single value to be set to one single operation, instead of multiple
private String propertyAttribute;
// This is the setter method in the template operation to be used to set the
// property in case of multiple configurations
private Method propertySetter;
// A template of transformation operation to be performed against all
// specified files
private TransformationOperation templateOperation;
// Actual operations to performed against all specified files
private List operations;
/**
* Utility to perform multiple transformation operations. Multiple transformation operations
* are defined based on an operation template and two other factors, that could be applied
* exclusively or together. They are:
*
*
Multiple files: multiple operations are defined based on multiple files specified as a
* list, held as one or more transformation context attribute, and set via {@link #setFiles(String...)}
*
Multiple configurations: multiple operations are defined based on different configurations,
* set via {@link #setProperties(String, String)}
*
*
* In other words, there are two possible ways to define multiple operations: multiple
* files, or multiple configurations (different property values). It is also possible
* to combine both, resulting in multiple operations to be executed against multiple files and
* with multiple configurations.
*
* Important: when running against multiple files, any path set to this operation,
* either relative or absolute, will be ignored, and set later at transformation time based on
* the dynamically set multiple files. When running with multiple configurations, the properties set
* during transformation time will override any value that could have been set during definition time
*/
public MultipleOperations() {
}
/**
* Utility to perform multiple transformation operations. Multiple transformation operations
* are defined based on an operation template and two other factors, that could be applied
* exclusively or together. They are:
*
*
Multiple files: multiple operations are defined based on multiple files specified as a
* list, held as one or more transformation context attribute, and set via {@link #setFiles(String...)}
*
Multiple configurations: multiple operations are defined based on different configurations,
* set via {@link #setProperties(String, String)}
*
*
* In other words, there are two possible ways to define multiple operations: multiple
* files, or multiple configurations (different property values). It is also possible
* to combine both, resulting in multiple operations to be executed against multiple files and
* with multiple configurations.
*
* Important: when running against multiple files, any path set to this operation,
* either relative or absolute, will be ignored, and set later at transformation time based on
* the dynamically set multiple files. When running with multiple configurations, the properties set
* during transformation time will override any value that could have been set during definition time
*
* @param templateOperation a template of transformation operation to be performed
* against all specified files
*/
public MultipleOperations(TransformationOperation templateOperation) {
setOperationTemplate(templateOperation);
}
/**
* Sets one or more transformation context attributes that hold list of Files
* which the transformation operations should perform against.
* If more than one attribute is specified, all list of files will be
* combined into a single one
*
* @param filesAttributes one or more transformation context attributes that hold list
* of Files which the transformation operation should perform
* against
* @return this transformation utility object
*/
public MultipleOperations setFiles(String... filesAttributes) {
this.filesAttributes = filesAttributes;
return this;
}
/**
* This setter is similar to {@link TransformationUtility#set(String, String)}, however it is more powerful, because
* it allows setting, during transformation time, different properties values for each operation of a
* {@link MultipleOperations}.
*
* That being said, calling this method will only make a difference if this operation is executed as the
* template operation for a multiple operations utility. That usually can be done by adding it to a
* transformation template via {@link TransformationTemplate#addMultiple(TransformationOperation, String...)}
*
* @param propertyName the operation Java bean property name to be set during transformation time
* @param propertyAttribute the name of the transformation context attribute that holds a {@link Set} of
* values to be each set individually (as the property value) to each operation of
* a set of multiple operations. These values are set right before execution. If
* the transformation context attribute value is not a {@link Set}, then a
* {@link com.paypal.butterfly.extensions.api.exception.TransformationUtilityException}
* will be thrown right before execution
* @return this instance
*/
public final MultipleOperations setProperties(String propertyName, String propertyAttribute) {
checkForBlankString("propertyName", propertyName);
checkForBlankString("propertyAttribute", propertyAttribute);
this.propertyName = propertyName;
this.propertyAttribute = propertyAttribute;
return this;
}
/**
* Sets the template of transformation operation to be performed against all specified files.
*
* Important: any path set to this operation, either relative
* or absolute, will be ignored, and set later at transformation time based on
* the dynamically set multiple files
*
* @param templateOperation the template of transformation operation to be performed against
* all specified files
* @return this transformation utility object
*/
public MultipleOperations setOperationTemplate(TransformationOperation templateOperation) {
templateOperation.relative(null);
templateOperation.absolute(null);
this.templateOperation = templateOperation;
return this;
}
@Override
public MultipleOperations setName(String name) {
templateOperation.setName(String.format("%s-%s-TEMPLATE_OPERATION", name, templateOperation.getSimpleClassName()));
return super.setName(name);
}
/**
* Return an array containing the name of transformation context attributes
* that hold the list of files the operations should be performed against
*
* @return an array containing the name of transformation context attributes
* that hold the list of files the operations should be performed against
*/
public String[] getFilesAttributes() {
return Arrays.copyOf(filesAttributes, filesAttributes.length);
}
public TransformationOperation getTemplateOperation() {
return templateOperation;
}
@Override
public String getDescription() {
return String.format(DESCRIPTION, templateOperation.getSimpleClassName());
}
public void setPropertySetter() {
String methodName = String.format("set%s%s", propertyName.substring(0, 1).toUpperCase(), propertyName.substring(1));
for(Method method : templateOperation.getClass().getMethods()) {
if(method.getName().equals(methodName)) {
propertySetter = method;
return;
}
}
String exceptionMessage = String.format("%s is not a valid property", propertyName);
throw new TransformationDefinitionException(exceptionMessage);
}
@Override
protected TUExecutionResult execution(File transformedAppFolder, TransformationContext transformationContext) {
Collection files;
Set allFiles = new HashSet<>();
for(String attribute: filesAttributes) {
files = (Collection) transformationContext.get(attribute);
if (files != null) {
allFiles.addAll(files);
}
}
boolean multipleFiles = true;
if (allFiles.size() == 0) {
if (wasFileExplicitlySet()) {
// This means this multiple operation is not supposed to be executed
// based on multiple files, but based on multiple configuration
// Because of that, the single file the multiple operations should
// run against is defined as usual
allFiles.add(getAbsoluteFile(transformedAppFolder, transformationContext));
multipleFiles = false;
} else {
if (logger.isDebugEnabled()) {
logger.debug("Multiple operation %s has NO file to perform against, it will result in ZERO transformation operations", getName());
}
operations = new ArrayList();
String message = String.format("Multiple operation %s resulted in 0 operations based on %s", getName(), templateOperation.getSimpleClassName());
return TUExecutionResult.value(this, getChildren()).setDetails(message);
}
}
boolean multipleConfigurations = false;
Set propertyValues = null;
if (propertyName != null) {
Object propertyValuesObj = transformationContext.get(propertyAttribute);
if (!(propertyValuesObj instanceof Set)) {
String exceptionMessage = String.format("Transformation context attribute %s does not contain a java.util.Set object", propertyAttribute);
TransformationUtilityException tue = new TransformationUtilityException(exceptionMessage);
return TUExecutionResult.error(this, tue);
}
propertyValues = (Set) propertyValuesObj;
if (propertyValues.size() == 0) {
logger.warn("Transformation context attribute %s contains an empty java.util.Set object, so it will be ignored", propertyAttribute);
} else {
multipleConfigurations = true;
setPropertySetter();
}
}
if (logger.isDebugEnabled()) {
logger.debug("Multiple files in {} is set to {}", getName(), multipleFiles);
logger.debug("Multiple configurations in {} is set to {}", getName(), multipleConfigurations);
}
TransformationOperation operation;
operations = new ArrayList<>();
int order = 1;
try {
for(File file : allFiles) {
if (!multipleConfigurations) {
operation = createClone(order, transformedAppFolder, file, transformationContext);
operations.add(operation);
order++;
} else {
Object[] propertyValuesArray = propertyValues.toArray();
for (Object propertyValue : propertyValuesArray) {
operation = createClone(order, transformedAppFolder, file, transformationContext);
propertySetter.invoke(operation, propertyValue);
operations.add(operation);
order++;
}
}
}
} catch (InvocationTargetException | IllegalAccessException e) {
String exceptionMessage = String.format("It was not possible to set property %s, in object of type %s, during multiple operation multiple configuration setting pre-execution", propertyName, templateOperation.getClass().getName());
TransformationUtilityException tue = new TransformationUtilityException(exceptionMessage, e);
return TUExecutionResult.error(this, tue);
} catch (IllegalStateException e) {
String exceptionMessage = String.format("Illegal file set to multiple operations");
TransformationUtilityException tue = new TransformationUtilityException(exceptionMessage, e);
return TUExecutionResult.error(this, tue);
}
String message = null;
if(logger.isDebugEnabled()) {
message = String.format("Multiple operation %s resulted in %d operations based on %s", getName(), operations.size(), templateOperation.getSimpleClassName());
}
return TUExecutionResult.value(this, getChildren()).setDetails(message);
}
private TransformationOperation createClone(int order, File transformedAppFolder, File targetFile, TransformationContext transformationContext) {
TransformationOperation operation = (TransformationOperation) templateOperation.copy();
operation.setParent(this, order);
String relativePath = TransformationUtility.getRelativePath(transformedAppFolder, targetFile);
if (targetFile.getAbsolutePath().equals(relativePath)) {
// This means the target file path is not within the transformed application folder.
// This can only happen if the target file is in the baseline application (in case the
// transformation template is blank)
if (getTransformationTemplate().isBlank()) {
File baselineApplicationFolder = (File) transformationContext.get(TransformationTemplate.BASELINE);
relativePath = TransformationUtility.getRelativePath(baselineApplicationFolder, targetFile);
if (targetFile.getAbsolutePath().equals(relativePath)) {
throw new IllegalStateException("Illegal attempt to manipulate a file outside of transformed application and baseline application folders");
} else {
operation.absolute(TransformationTemplate.BASELINE, relativePath);
}
} else {
throw new IllegalStateException("Illegal attempt to manipulate a file outside of transformed application folder");
}
} else {
operation.relative(relativePath);
}
return operation;
}
/**
* Returns all {@link TransformationOperation} instances generated out of the
* transformation operation template.
* Those instances though are not accessible during transformation definition time,
* neither before this multiple operations is performed.
* Actually, the purpose of a multiple operation is exactly to generate
* all children transformation operation instances, which get them executed behind the scenes
* by the transformation engine.
*
* @return the generated multiple transformation operation instances, based on the specified
* transformation operation template
*/
@Override
public List getChildren() {
if (operations == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(operations);
}
}