com.temenos.interaction.core.hypermedia.LinkToFieldAssociationImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.temenos.interaction.core.hypermedia.LinkToFieldAssociationImpl.java

Source

package com.temenos.interaction.core.hypermedia;

/*
 * #%L
 * interaction-core
 * %%
 * Copyright (C) 2012 - 2016 Temenos Holdings N.V.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

/**
 * Implementation of {@link LinkToFieldAssociation}
 */
public class LinkToFieldAssociationImpl implements LinkToFieldAssociation {

    private static final Pattern COLLECTION_PARAM_PATTERN = Pattern
            .compile("\\{{1}([a-zA-Z0-9]+(\\.[a-zA-Z0-9]+)+)\\}{1}");
    private static final String PARAM_REPLACEMENT_REGEX = "\\((\\d+)\\)";
    private static final Logger logger = LoggerFactory.getLogger(LinkToFieldAssociationImpl.class);
    private Transition transition;
    private String targetFieldName;
    private List<String> transitionCollectionParams;
    private Boolean isCollectionDynamicResourceName;
    private Map<String, Object> transitionProperties;
    private Map<String, Object> normalisedTransitionProperties;

    public LinkToFieldAssociationImpl(Transition transition, Map<String, Object> properties) {
        this.transition = transition;
        targetFieldName = transition.getSourceField();
        transitionCollectionParams = getCollectionParams(transition.getCommand().getUriParameters());
        isCollectionDynamicResourceName = isCollectionDynamicResourceName(transition.getTarget());
        transitionProperties = properties;
        normalisedTransitionProperties = HypermediaTemplateHelper.normalizeProperties(properties);
    }

    @Override
    public boolean isTransitionSupported() {

        if (targetFieldName == null && (!transitionCollectionParams.isEmpty() || isCollectionDynamicResourceName)) {
            logger.error("Cannot generate links for transition " + transition
                    + ". Target field name cannot be null if we have collection parameters or a collection dymamic resource.");
            return false;
        }

        if (isCollectionDynamicResourceName && !StringUtils.equals(getParentNameOfCollectionValue(targetFieldName),
                getParentNameOfDynamicResource())) {
            logger.error("Cannot generate links for transition " + transition
                    + ". Parent of target field name and dynamic resource must be same.");
            return false;
        }

        if (isCollectionDynamicResourceName && !allParametersHaveSameParent(
                ((DynamicResourceState) transition.getTarget()).getResourceLocatorArgs())) {
            logger.error("Cannot generate links for transition " + transition
                    + ". All multivalue fields in the parameter list of the dynamic resource must have the same parent.");
            return false;
        }

        if (!transitionCollectionParams.isEmpty()
                && !allParametersHaveSameParent(transitionCollectionParams.toArray(new String[0]))) {
            logger.error("Cannot generate links for transition " + transition
                    + ". All collection parameters must have the same parent.");
            return false;
        }

        return true;
    }

    @Override
    public List<LinkProperties> getProperties() {

        List<LinkProperties> transitionPropertiesList = new ArrayList<LinkProperties>();

        //Get list of child names, i.e. For AB.CD and AA.ZZ add CD, ZZ to list
        List<String> childParamNames = new ArrayList<String>();
        for (String collectionParam : transitionCollectionParams) {
            childParamNames.add(getChildNameOfCollectionValue(collectionParam));
        }

        if (targetFieldName != null && targetFieldName.contains(".")) // Multivalue target
        {
            createLinkPropertiesForMultivalueTarget(transitionPropertiesList, childParamNames);
        } else // Non multivalue target
        {
            createLinkPropertiesForSingleTarget(transitionPropertiesList, childParamNames);
        }

        logger.debug(
                "Created " + transitionPropertiesList.size() + " properties map(s) for transition " + transition);

        return transitionPropertiesList;
    }

    private void createLinkPropertiesForMultivalueTarget(List<LinkProperties> transitionPropertiesList,
            List<String> childParamNames) {
        List<String> targetFields = extractMatchingFieldsFromTransitionProperties(targetFieldName);
        //If properties does not contain the target, add unresolved target
        if (targetFields.isEmpty()) {
            targetFields.add(targetFieldName);
        }

        String parentTargetFieldName = getParentNameOfCollectionValue(targetFieldName);

        boolean hasSameParent = false;
        int numOfChildren = 0;
        String parentResolvedName = new String();
        if (transitionCollectionParams.size() > 0) {
            String firstCollectionParam = transitionCollectionParams.get(0);
            //Determine if parent of target field and parent of parameters are same 
            hasSameParent = StringUtils.equals(getParentNameOfCollectionValue(firstCollectionParam),
                    parentTargetFieldName);
            if (!hasSameParent) {
                numOfChildren = getNumberOfMultivalueChildren();
                parentResolvedName = getParentOfMultivalueChildren();
            }
        }

        for (String targetField : targetFields) // Generate one or more map of properties for each target field
        {
            List<String> resolvedDynamicResourceFieldNames = getDynamicResourceResolvedFieldName(
                    transition.getTarget(), targetField);
            if (transitionCollectionParams.isEmpty()) // Create one link properties map per target
            {
                LinkProperties linkProps = createLinkProperties(targetField, resolvedDynamicResourceFieldNames,
                        childParamNames, null);
                transitionPropertiesList.add(linkProps);
            } else if (hasSameParent) // Create one link properties map per target
            {
                String parentResolvedTargetFieldName = getParentNameOfCollectionValue(targetField);
                LinkProperties linkProps = createLinkProperties(targetField, resolvedDynamicResourceFieldNames,
                        childParamNames, parentResolvedTargetFieldName);
                transitionPropertiesList.add(linkProps);
            } else {
                // Create multiple properties maps per target. Depends on the number of children in the entity in transition properties
                for (int i = 0; i <= numOfChildren; i++) {
                    String childParentResolvedParamNewIndex = parentResolvedName + "(" + i + ")";
                    LinkProperties linkProps = createLinkProperties(targetField, resolvedDynamicResourceFieldNames,
                            childParamNames, childParentResolvedParamNewIndex);
                    transitionPropertiesList.add(linkProps);
                }
            }
        }
    }

    private void createLinkPropertiesForSingleTarget(List<LinkProperties> transitionPropertiesList,
            List<String> childParamNames) {
        if (transitionCollectionParams.isEmpty()) // Create one link properties map per target
        {
            LinkProperties linkProps = new LinkProperties(targetFieldName, transitionProperties);
            transitionPropertiesList.add(linkProps);
        } else {
            int numOfChildren = getNumberOfMultivalueChildren();
            String parentResolvedName = getParentOfMultivalueChildren();

            // Create multiple properties maps per target. Depends on the number of children in the entity in transition properties
            for (int i = 0; i <= numOfChildren; i++) {
                String childParentResolvedParamNewIndex = parentResolvedName + "(" + i + ")";
                LinkProperties linkProps = createLinkProperties(targetFieldName, null, childParamNames,
                        childParentResolvedParamNewIndex);
                transitionPropertiesList.add(linkProps);
            }
        }
    }

    private LinkProperties createLinkProperties(String targetField, List<String> resolvedDynamicResourceFieldNames,
            List<String> childParamNames, String resolvedParentName) {
        List<String> paramPropertyKeys = new ArrayList<String>();
        if (StringUtils.isNotBlank(resolvedParentName)) {
            paramPropertyKeys = getListOfParamPropertyKeys(childParamNames, resolvedParentName);
        }

        if (!CollectionUtils.isEmpty(resolvedDynamicResourceFieldNames)) {
            paramPropertyKeys.addAll(resolvedDynamicResourceFieldNames);
        }
        LinkProperties linkProps = new LinkProperties(targetField, transitionProperties);
        addEntriesToLinkProperties(linkProps, paramPropertyKeys);
        return linkProps;
    }

    private List<String> getListOfParamPropertyKeys(List<String> childParamNames, String fullyQualifiedParentName) {
        List<String> propertyKeys = new ArrayList<String>();

        for (String childParam : childParamNames) {
            propertyKeys.add(fullyQualifiedParentName + "." + childParam);
        }

        return propertyKeys;
    }

    private List<String> getDynamicResourceResolvedFieldName(ResourceState targetResourceState,
            String targetField) {
        List<String> resolvedFieldNames = new ArrayList<String>();
        if (isCollectionDynamicResourceName) {
            String targetFieldParentName = getParentNameOfCollectionValue(targetField);
            DynamicResourceState dynamicResourceState = (DynamicResourceState) targetResourceState;
            for (String fieldName : dynamicResourceState.getResourceLocatorArgs()) {
                if (fieldName.contains(".")) {
                    resolvedFieldNames.add(targetFieldParentName + "."
                            + getChildNameOfCollectionValue(fieldName).replaceAll("\\}", "")); //Remove enclosed curly brace
                }
            }
        }
        return resolvedFieldNames;
    }

    private int getNumberOfMultivalueChildren() {
        //Find the number of children using the index of the parent in transition properties
        //i.e. if we have A(0).B and A(1).B in the properties map, return 1
        int numChildren = 0;
        for (String collectionParam : transitionCollectionParams) {
            List<String> entityParamFields = extractMatchingFieldsFromTransitionProperties(collectionParam);
            for (String entityParam : entityParamFields) {
                int index = Integer.parseInt(
                        entityParam.substring(entityParam.lastIndexOf("(") + 1, entityParam.lastIndexOf(")")));
                if (index > numChildren) {
                    numChildren = index;
                }
            }
        }
        return numChildren;
    }

    private String getParentOfMultivalueChildren() {
        String collectionParam = transitionCollectionParams.get(0);
        List<String> matchingFields = extractMatchingFieldsFromTransitionProperties(collectionParam);
        String parent = getParentNameOfCollectionValue(matchingFields.get(0));
        return parent.substring(0, parent.lastIndexOf("("));
    }

    private List<String> getCollectionParams(Map<String, String> transitionUriMap) {
        List<String> collectionParams = new ArrayList<String>();
        if (transitionUriMap == null) {
            return collectionParams;
        }

        for (Map.Entry<String, String> entry : transitionUriMap.entrySet()) {
            String parameter = entry.getValue();
            Matcher matcher = COLLECTION_PARAM_PATTERN.matcher(parameter);
            while (matcher.find()) {
                collectionParams.add(matcher.group(1));
            }
        }
        return collectionParams;
    }

    private boolean isCollectionDynamicResourceName(ResourceState target) {
        if (target instanceof DynamicResourceState) {
            String[] resourceLocatorArgs = ((DynamicResourceState) target).getResourceLocatorArgs();
            if (resourceLocatorArgs != null && resourceLocatorArgs.length > 0
                    && resourceLocatorArgs[0].contains(".")) {
                return true;
            }
        }
        return false;
    }

    private String getParentNameOfDynamicResource() {
        String[] resourceLocatorArgs = ((DynamicResourceState) transition.getTarget()).getResourceLocatorArgs();
        String parent = getParentNameOfCollectionValue(resourceLocatorArgs[0]);
        if (parent != null && parent.length() > 1) {
            parent = parent.substring(1); //Remove starting curly brace {
        }
        return parent;
    }

    private List<String> extractMatchingFieldsFromTransitionProperties(String fieldName) {
        List<String> matchingFieldList = new ArrayList<String>();
        for (String key : normalisedTransitionProperties.keySet()) {
            if (StringUtils.equals(fieldName, key.replaceAll(PARAM_REPLACEMENT_REGEX, ""))) {
                matchingFieldList.add(key);
            }
        }

        return matchingFieldList;
    }

    private String getParentNameOfCollectionValue(String value) {
        if (StringUtils.isNotBlank(value) && value.contains(".")) {
            return value.substring(0, value.lastIndexOf("."));
        }
        return null;
    }

    private String getChildNameOfCollectionValue(String value) {
        if (StringUtils.isNotBlank(value) && value.contains(".")) {
            return value.substring(value.lastIndexOf(".") + 1);
        }
        return null;
    }

    private boolean allParametersHaveSameParent(String... parameters) {
        boolean haveSameParent = true;
        List<String> parents = new ArrayList<String>();
        for (String param : parameters) {
            String parent = getParentNameOfCollectionValue(param);
            if (parent == null) {
                //Do nothing. Dealing with non multivalue field
            } else if (parents.isEmpty()) {
                parents.add(parent);
            } else if (!parents.contains(parent)) {
                haveSameParent = false;
                break;
            }
        }

        return haveSameParent;
    }

    private void addEntriesToLinkProperties(LinkProperties linkProps, List<String> keys) {
        Map<String, Object> linkPropertiesMap = linkProps.getTransitionProperties();
        Set<String> unindexedKeys = new HashSet<String>();
        for (String key : keys) {
            Object value = this.normalisedTransitionProperties.get(key);
            String unindexedKey = key.replaceAll(PARAM_REPLACEMENT_REGEX, "");
            unindexedKeys.add(unindexedKey);
            if (value != null && value instanceof String) {
                linkPropertiesMap.put(unindexedKey, value);
            } else {
                linkPropertiesMap.put(unindexedKey, "");
            }
        }

        // Replace Id={A.B.C} with Id={VAL} if A.B.C=VAL is in the linkPropertiesMap
        for (String key : linkPropertiesMap.keySet()) {
            Object value = linkPropertiesMap.get(key);
            if (value instanceof String) {

                String replacementKey = ((String) value).replaceAll("\\{", "").replaceAll("\\}", "");

                if (unindexedKeys.contains(replacementKey)) {
                    String replacementValue = ((String) value).replaceAll(
                            "\\{" + Pattern.quote(replacementKey) + "\\}",
                            linkPropertiesMap.get(replacementKey).toString());
                    linkPropertiesMap.put(key, replacementValue);
                }
            }
        }
    }
}