therian.Operation.java Source code

Java tutorial

Introduction

Here is the source code for therian.Operation.java

Source

/*
 *  Copyright the original author or authors.
 *
 *  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 therian;

import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.reflect.TypeUtils;

import therian.util.Types;

/**
 * Some operation; note that these are not intended for use on multiple threads. A concrete {@link Operation} class
 * should have its {@code RESULT} type parameter fully bound.
 *
 * @param <RESULT> result type
 */
public abstract class Operation<RESULT> {
    private static final TypeVariable<?> TYPE_VARIABLE_RESULT = Operation.class.getTypeParameters()[0];

    private static final Map<Class<?>, Boolean> VALID_INFO = new HashMap<>();

    private static boolean init(Class<?> type) {
        final boolean valid;
        synchronized (type) {
            if (VALID_INFO.containsKey(type)) {
                valid = VALID_INFO.get(type).booleanValue();
            } else if (Modifier.isAbstract(type.getModifiers())) {
                valid = true;
            } else {
                final Type resultType = TypeUtils.unrollVariables(TypeUtils.getTypeArguments(type, Operation.class),
                        TYPE_VARIABLE_RESULT);
                valid = !TypeUtils.containsTypeVariables(resultType);
                Validate.isTrue(valid, "%s does not fully bind type parameter %s from %s", type,
                        TYPE_VARIABLE_RESULT.getName(), Operation.class);
                VALID_INFO.put(type, Boolean.valueOf(valid));
            }
        }

        final Class<?> parent = type.getSuperclass();
        if (!Operation.class.equals(parent)) {
            init(parent.asSubclass(Operation.class));
        }
        return valid;
    }

    {
        @SuppressWarnings("unchecked")
        final Class<? extends Operation<?>> c = (Class<? extends Operation<?>>) getClass();
        Validate.isTrue(init(c), "Invalid %s: %s", Operation.class.getName(), c);
    }

    private boolean successful;
    private RESULT result;

    /**
     * Get the result. Default implementation throws {@link OperationException} if the operation was unsuccessful.
     *
     * @return RESULT
     */
    public RESULT getResult() {
        if (!isSuccessful()) {
            throw new OperationException(this, "result unavailable");
        }
        return result;
    }

    /**
     * Set the result of this {@link Operation}.
     *
     * @param result to set
     */
    public void setResult(RESULT result) {
        this.result = result;
    }

    public boolean isSuccessful() {
        return successful;
    }

    public void setSuccessful(boolean successful) {
        this.successful = successful;
    }

    /**
     * Learn whether {@code operator} seems to implement {@code this}.
     *
     * @param operator to check
     * @return boolean
     */
    public boolean matches(Operator<?> operator) {
        final Type expectedType = TypeUtils.unrollVariables(
                TypeUtils.getTypeArguments(operator.getClass(), Operator.class),
                Operator.class.getTypeParameters()[0]);

        if (!TypeUtils.isInstance(this, expectedType)) {
            return false;
        }

        final Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(expectedType, Operation.class);
        for (Class<?> c : ClassUtils.hierarchy(TypeUtils.getRawType(expectedType, operator.getClass()))) {
            if (c.equals(Operation.class)) {
                break;
            }
            for (TypeVariable<?> var : c.getTypeParameters()) {
                Type type = Types.resolveAt(this, var, typeArguments);
                if (type == null || typeArguments == null) {
                    continue;
                }
                if (type instanceof Class<?> && ((Class<?>) type).isPrimitive()) {
                    type = ClassUtils.primitiveToWrapper((Class<?>) type);
                }
                if (!TypeUtils.isAssignable(type, TypeUtils.unrollVariables(typeArguments, var))) {
                    return false;
                }
            }
        }
        return true;
    }

}