com.jkoolcloud.tnt4j.streams.matchers.StringMatcher.java Source code

Java tutorial

Introduction

Here is the source code for com.jkoolcloud.tnt4j.streams.matchers.StringMatcher.java

Source

/*
 * Copyright 2014-2018 JKOOL, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jkoolcloud.tnt4j.streams.matchers;

import java.lang.reflect.Method;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;

import com.jkoolcloud.tnt4j.streams.utils.StreamsResources;

/**
 * Data string value match expression evaluation based on use of {@link org.apache.commons.lang3.StringUtils} provided
 * methods.
 *
 * @version $Revision: 1 $
 *
 * @see org.apache.commons.lang3.StringUtils
 */
public class StringMatcher implements Matcher {

    private static StringMatcher instance;

    private StringMatcher() {
    }

    /**
     * Returns instance of string matcher to be used by {@link com.jkoolcloud.tnt4j.streams.matchers.Matchers} facade.
     *
     * @return default instance of string matcher
     */
    static synchronized StringMatcher getInstance() {
        if (instance == null) {
            instance = new StringMatcher();
        }

        return instance;
    }

    /**
     * Returns whether this matcher can evaluate expression for data in the specified format.
     * <p>
     * This matcher supports the following class types (and all classes extending/implementing any of these):
     * <ul>
     * <li>{@link java.lang.String}</li>
     * </ul>
     *
     * @param data
     *            data object whose class is to be verified
     * @return {@code true} if this matcher can evaluate expression for data in the specified format, {@code false} -
     *         otherwise
     */
    @Override
    public boolean isDataClassSupported(Object data) {
        return String.class.isInstance(data);
    }

    /**
     * Checks expression by evaluating {@link StringUtils} class provided methods on data string.
     *
     * @param expression
     *            expression to check - it must contain method name from {@link StringUtils} class, and arguments. First
     *            method parameter is always <tt>data</tt> value, so only subsequent parameters shall be defined in
     *            expression e.g. {@code "contains(PAYMENT)"}, {@code "isEmpty()"}. To invert evaluation value start
     *            expression definition with {@code '!'} char.
     * @param data
     *            data string to evaluate expression against
     * @return {@code true} if {@link StringUtils} method returned {@code true} (or in case of compare returns
     *         {@code 0}), {@code false} - otherwise
     * @throws java.lang.Exception
     *             if there is no specified method found in {@link org.apache.commons.lang3.StringUtils} class or
     *             <tt>expression</tt> can't be evaluated for other reasons
     */
    @Override
    public boolean evaluate(String expression, Object data) throws Exception {
        boolean invert = expression.charAt(0) == '!';
        String methodName = expression.substring(invert ? 1 : 0, expression.indexOf("(")); // NON-NLS
        String[] arguments = expression.substring(expression.indexOf("(") + 1, expression.lastIndexOf(")"))
                .split(","); // NON-NLS
        Object[] convertedArguments = new Object[arguments.length];
        Method method = null;

        boolean hasNoArguments = hasNoArguments(arguments);
        if (hasNoArguments) {
            method = StringUtils.class.getDeclaredMethod(methodName, CharSequence.class);
        } else {
            method = findMatchingMethodAndConvertArgs(methodName, arguments, convertedArguments,
                    StringUtils.class.getDeclaredMethods());
        }
        if (method != null) {
            if (hasNoArguments) {
                boolean result = returnBoolean(method.invoke(null, String.valueOf(data)));
                return invert ? !result : result;
            } else {
                // Arrays.
                Object[] predefinedArgs = { String.valueOf(data) };
                Object[] allArgs = ArrayUtils.addAll(predefinedArgs, convertedArguments);
                boolean result = returnBoolean(method.invoke(null, allArgs));
                return invert ? !result : result;
            }
        } else {
            throw new RuntimeException(StreamsResources.getStringFormatted(StreamsResources.RESOURCE_BUNDLE_NAME,
                    "StringMatcher.no.such.method", methodName));
        }
    }

    private static Method findMatchingMethodAndConvertArgs(Object methodName, String[] arguments,
            Object[] convertedArguments, Method[] methods) {
        for (Method method : methods) {
            if (method.getName().equals(methodName) && method.getParameterTypes().length == arguments.length + 1) {
                boolean methodMatch = true;
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 1; i < parameterTypes.length; i++) {
                    Class<?> parameterType = parameterTypes[i];
                    if (CharSequence.class.isAssignableFrom(parameterType)) {
                        convertedArguments[i - 1] = arguments[i - 1];
                    } else {
                        try {
                            if (parameterType.isPrimitive()) {
                                parameterType = ClassUtils.primitiveToWrapper(parameterType);
                            }
                            Method converterMethod = parameterType.getMethod("valueOf", String.class);
                            convertedArguments[i - 1] = converterMethod.invoke(null, arguments[i - 1]);
                        } catch (Exception e) {
                            methodMatch = false;
                            break;
                        }
                    }
                }

                if (methodMatch) {
                    return method;
                }
            }
        }
        return null;
    }

    private static boolean returnBoolean(Object result) {
        if (Integer.class.isInstance(result)) {
            return new Integer(0).equals(result);
        }
        if (Boolean.class.isInstance(result)) {
            return (boolean) result;
        }
        return false;
    }

    private static boolean hasNoArguments(String[] arguments) {
        return ArrayUtils.isEmpty(arguments) || (arguments.length == 1 && arguments[0].isEmpty());
    }
}