org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.rest.workflow.api.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper.WalkerCallbackAdapter;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ISO8601DateFormat;
import org.apache.commons.beanutils.ConversionException;
import org.apache.commons.beanutils.ConvertUtils;

/**
 * Query walker that adds all properties for "equals" comparison to a map. If an
 * unsupported property or comparison operation is encountered, an
 * {@link InvalidArgumentException} is thrown unless the method
 * {@link #handleUnmatchedComparison(int, String, String)} returns true (default
 * implementation returns false).
 * 
 * @author Frederik Heremans
 * @author Tijs Rademakers
 */
public class MapBasedQueryWalker extends WalkerCallbackAdapter {
    private Set<String> supportedEqualsParameters;

    private Set<String> supportedMatchesParameters;

    private Set<String> supportedGreaterThanParameters;

    private Set<String> supportedGreaterThanOrEqualParameters;

    private Set<String> supportedLessThanParameters;

    private Set<String> supportedLessThanOrEqualParameters;

    private Map<String, String> equalsProperties;

    private Map<String, String> matchesProperties;

    private Map<String, String> greaterThanProperties;

    private Map<String, String> greaterThanOrEqualProperties;

    private Map<String, String> lessThanProperties;

    private Map<String, String> lessThanOrEqualProperties;

    private List<QueryVariableHolder> variableProperties;

    private boolean variablesEnabled;

    private NamespaceService namespaceService;

    private DictionaryService dictionaryService;

    public MapBasedQueryWalker(Set<String> supportedEqualsParameters, Set<String> supportedMatchesParameters) {
        this.supportedEqualsParameters = supportedEqualsParameters;
        this.supportedMatchesParameters = supportedMatchesParameters;
        this.equalsProperties = new HashMap<String, String>();
        this.matchesProperties = new HashMap<String, String>();
    }

    public void setSupportedGreaterThanParameters(Set<String> supportedGreaterThanParameters) {
        this.supportedGreaterThanParameters = supportedGreaterThanParameters;
        if (greaterThanProperties == null) {
            greaterThanProperties = new HashMap<String, String>();
        }
    }

    public void setSupportedGreaterThanOrEqualParameters(Set<String> supportedGreaterThanOrEqualParameters) {
        this.supportedGreaterThanOrEqualParameters = supportedGreaterThanOrEqualParameters;
        if (greaterThanOrEqualProperties == null) {
            greaterThanOrEqualProperties = new HashMap<String, String>();
        }
    }

    public void setSupportedLessThanParameters(Set<String> supportedLessThanParameters) {
        this.supportedLessThanParameters = supportedLessThanParameters;
        if (lessThanProperties == null) {
            lessThanProperties = new HashMap<String, String>();
        }
    }

    public void setSupportedLessThanOrEqualParameters(Set<String> supportedLessThanOrEqualParameters) {
        this.supportedLessThanOrEqualParameters = supportedLessThanOrEqualParameters;
        if (lessThanOrEqualProperties == null) {
            lessThanOrEqualProperties = new HashMap<String, String>();
        }
    }

    public void enableVariablesSupport(NamespaceService namespaceService, DictionaryService dictionaryService) {
        variablesEnabled = true;
        if (namespaceService == null) {
            throw new IllegalArgumentException("namespace service can't be null");
        }
        if (dictionaryService == null) {
            throw new IllegalArgumentException("dictionary service can't be null");
        }
        this.namespaceService = namespaceService;
        this.dictionaryService = dictionaryService;
        variableProperties = new ArrayList<QueryVariableHolder>();
    }

    public List<QueryVariableHolder> getVariableProperties() {
        return variableProperties;
    }

    @Override
    public void matches(String property, String value, boolean negated) {
        if (negated) {
            throw new InvalidArgumentException("Cannot use negated matching for property: " + property);
        }
        if (variablesEnabled && property.startsWith("variables/")) {
            processVariable(property, value, WhereClauseParser.MATCHES);
        } else if (supportedMatchesParameters != null && supportedMatchesParameters.contains(property)) {
            matchesProperties.put(property, value);
        } else {
            throw new InvalidArgumentException("Cannot use matching for property: " + property);
        }
    }

    @Override
    public void comparison(int type, String propertyName, String propertyValue) {
        boolean throwError = false;

        if (variablesEnabled && propertyName.startsWith("variables/")) {
            processVariable(propertyName, propertyValue, type);
        } else {
            if (type == WhereClauseParser.EQUALS) {
                if (supportedEqualsParameters != null && supportedEqualsParameters.contains(propertyName)) {
                    equalsProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else if (type == WhereClauseParser.MATCHES) {
                if (supportedMatchesParameters != null && supportedMatchesParameters.contains(propertyName)) {
                    matchesProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else if (type == WhereClauseParser.GREATERTHAN) {
                if (supportedGreaterThanParameters != null
                        && supportedGreaterThanParameters.contains(propertyName)) {
                    greaterThanProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else if (type == WhereClauseParser.GREATERTHANOREQUALS) {
                if (supportedGreaterThanOrEqualParameters != null
                        && supportedGreaterThanOrEqualParameters.contains(propertyName)) {
                    greaterThanOrEqualProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else if (type == WhereClauseParser.LESSTHAN) {
                if (supportedLessThanParameters != null && supportedLessThanParameters.contains(propertyName)) {
                    lessThanProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else if (type == WhereClauseParser.LESSTHANOREQUALS) {
                if (supportedLessThanOrEqualParameters != null
                        && supportedLessThanOrEqualParameters.contains(propertyName)) {
                    lessThanOrEqualProperties.put(propertyName, propertyValue);
                } else {
                    throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
                }
            } else {
                throwError = !handleUnmatchedComparison(type, propertyName, propertyValue);
            }
        }

        if (throwError) {
            throw new InvalidArgumentException("framework.exception.InvalidProperty",
                    new Object[] { propertyName, propertyValue, WhereClauseParser.tokenNames[type] });
        }
    }

    public String getProperty(String propertyName, int type) {
        if (type == WhereClauseParser.EQUALS) {
            return equalsProperties.get(propertyName);
        } else if (type == WhereClauseParser.MATCHES) {
            return matchesProperties.get(propertyName);
        } else if (type == WhereClauseParser.GREATERTHAN && greaterThanProperties != null) {
            return greaterThanProperties.get(propertyName);
        } else if (type == WhereClauseParser.GREATERTHANOREQUALS && greaterThanOrEqualProperties != null) {
            return greaterThanOrEqualProperties.get(propertyName);
        } else if (type == WhereClauseParser.LESSTHAN && lessThanProperties != null) {
            return lessThanProperties.get(propertyName);
        } else if (type == WhereClauseParser.LESSTHANOREQUALS && lessThanOrEqualProperties != null) {
            return lessThanOrEqualProperties.get(propertyName);
        } else {
            throw new IllegalArgumentException("type " + type + " is not supported");
        }
    }

    /**
     * Get the property value, converted to the requested type.
     * 
     * @param propertyName name of the parameter
     * @param type int
     * @param returnType type of object to return
     * @return the converted parameter value. Null, if the property has no
     *         value.
     * @throws IllegalArgumentException when no conversion for the given
     *             returnType is available or if returnType is null.
     * @throws InvalidArgumentException when conversion to the given type was
     *             not possible due to an error while converting
     */
    @SuppressWarnings("unchecked")
    public <T extends Object> T getProperty(String propertyName, int type, Class<T> returnType) {
        if (returnType == null) {
            throw new IllegalArgumentException("ReturnType cannot be null");
        }
        try {
            Object result = null;
            String stringValue = getProperty(propertyName, type);
            if (stringValue != null) {
                result = ConvertUtils.convert(stringValue, returnType);
                if ((result instanceof String) && (!returnType.equals(String.class))) {
                    // If a string is returned, no converter has been found (for non-String return type)
                    throw new IllegalArgumentException(
                            "Unable to convert parameter to type: " + returnType.getName());
                }
            }
            return (T) result;
        } catch (ConversionException ce) {
            // Conversion failed, wrap in Illegal
            throw new InvalidArgumentException("Query property value for '" + propertyName + "' should be a valid "
                    + returnType.getSimpleName());
        }
    }

    @Override
    public void and() {
        // We don't need to do anything in this method. However, overriding the
        // method indicates that AND is
        // supported. OR is not supported at the same time.
    }

    protected void processVariable(String propertyName, String propertyValue, int type) {
        String localPropertyName = propertyName.replaceFirst("variables/", "");
        Object actualValue = null;
        DataTypeDefinition dataTypeDefinition = null;
        // variable scope global is default
        String scopeDef = "global";

        // look for variable scope
        if (localPropertyName.contains("local/")) {
            scopeDef = "local";
            localPropertyName = localPropertyName.replaceFirst("local/", "");
        }

        if (localPropertyName.contains("global/")) {
            localPropertyName = localPropertyName.replaceFirst("global/", "");
        }

        // look for variable type definition
        if ((propertyValue.contains("_") || propertyValue.contains(":")) && propertyValue.contains(" ")) {
            int indexOfSpace = propertyValue.indexOf(' ');
            if ((propertyValue.contains("_") && indexOfSpace > propertyValue.indexOf("_"))
                    || (propertyValue.contains(":") && indexOfSpace > propertyValue.indexOf(":"))) {
                String typeDef = propertyValue.substring(0, indexOfSpace);
                try {
                    QName dataType = QName.createQName(typeDef.replace('_', ':'), namespaceService);
                    dataTypeDefinition = dictionaryService.getDataType(dataType);
                    propertyValue = propertyValue.substring(indexOfSpace + 1);
                } catch (Exception e) {
                    throw new ApiException(
                            "Error translating propertyName " + propertyName + " with value " + propertyValue);
                }
            }
        }

        if (dataTypeDefinition != null
                && "java.util.Date".equalsIgnoreCase(dataTypeDefinition.getJavaClassName())) {
            // fix for different ISO 8601 Date format classes in Alfresco (org.alfresco.util and Spring Surf)
            actualValue = ISO8601DateFormat.parse(propertyValue);
        } else if (dataTypeDefinition != null) {
            actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, propertyValue);
        } else {
            actualValue = propertyValue;
        }

        variableProperties.add(new QueryVariableHolder(localPropertyName, type, actualValue, scopeDef));
    }

    /**
     * Called when unsupported property is encountered or comparison operator
     * other than equals.
     * 
     * @return true, if the comparison is handles successfully. False, if an
     *         exception should be thrown because the comparison can't be
     *         handled.
     */
    protected boolean handleUnmatchedComparison(int type, String propertyName, String propertyValue) {
        return false;
    }

    public class QueryVariableHolder implements Serializable {
        private static final long serialVersionUID = 1L;

        private String propertyName;
        private int operator;
        private Object propertyValue;
        private String scope;

        public QueryVariableHolder() {
        }

        public QueryVariableHolder(String propertyName, int operator, Object propertyValue, String scope) {
            this.propertyName = propertyName;
            this.operator = operator;
            this.propertyValue = propertyValue;
            this.scope = scope;
        }

        public String getPropertyName() {
            return propertyName;
        }

        public void setPropertyName(String propertyName) {
            this.propertyName = propertyName;
        }

        public int getOperator() {
            return operator;
        }

        public void setOperator(int operator) {
            this.operator = operator;
        }

        public Object getPropertyValue() {
            return propertyValue;
        }

        public void setPropertyValue(Object propertyValue) {
            this.propertyValue = propertyValue;
        }

        public String getScope() {
            return scope;
        }

        public void setScope(String scope) {
            this.scope = scope;
        }

        public boolean isGlobalScope() {
            return "global".equals(scope);
        }
    }
}