com.lexicalscope.fluentreflection.FluentMethodImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.lexicalscope.fluentreflection.FluentMethodImpl.java

Source

package com.lexicalscope.fluentreflection;

/*
 * Copyright 2011 Tim Wood
 *
 * 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.
 */

import static ch.lambdaj.Lambda.*;
import static com.lexicalscope.fluentreflection.FluentReflection.object;
import static com.lexicalscope.fluentreflection.ReflectionMatchers.compatibleWith;
import static com.lexicalscope.fluentreflection.Visibility.visibilityFromModifiers;
import static java.lang.String.format;
import static java.lang.System.arraycopy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hamcrest.Matcher;

import com.google.inject.TypeLiteral;

final class FluentMethodImpl extends AbstractFluentAnnotated implements FluentMethod {
    private final ReflectedTypeFactory reflectedTypeFactory;
    private final TypeLiteral<?> typeLiteral;
    private final Method method;

    public FluentMethodImpl(final ReflectedTypeFactory reflectedTypeFactory, final TypeLiteral<?> typeLiteral,
            final Method method) {
        super(reflectedTypeFactory, method);
        this.reflectedTypeFactory = reflectedTypeFactory;
        this.typeLiteral = typeLiteral;
        this.method = method;
    }

    @Override
    public String name() {
        return method.getName();
    }

    @Override
    public List<FluentClass<?>> args() {
        final List<FluentClass<?>> result = new ArrayList<FluentClass<?>>();
        result.addAll(convert(typeLiteral.getParameterTypes(method),
                new ConvertTypeLiteralToReflectedType(reflectedTypeFactory)));
        return result;
    }

    @Override
    public int indexOfArg(final Matcher<? super FluentClass<?>> matcher) {
        final List<FluentClass<?>> args = args();
        for (int i = 0; i < args.size(); i++) {
            if (matcher.matches(args.get(i))) {
                return i;
            }
        }
        throw new IndexOutOfBoundsException("No argment matching " + matcher);
    }

    @Override
    public int argCount() {
        return method.getParameterTypes().length;
    }

    @Override
    public FluentClass<?> declarer() {
        return reflectedTypeFactory.reflect(typeLiteral);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public FluentObject<?> call(final Object... args) {
        final Object object = callRaw(args);
        if (object == null && typeLiteral.getReturnType(method) != null) {
            return reflectedTypeFactory.reflect(typeLiteral.getReturnType(method), null);
        }
        return reflectedTypeFactory.reflect((Class) object.getClass(), object);
    }

    private Object callRaw(final Object... args) {
        if (isStatic()) {
            return invokeMethod(null, args);
        } else {
            if (args.length < 1) {
                throw new IllegalArgumentException(
                        "target instance must be specified as first argument when calling " + method);
            } else if (args[0] == null) {
                return null;
            }

            final Object[] remainingArguments = new Object[args.length - 1];
            arraycopy(args, 1, remainingArguments, 0, args.length - 1);
            return invokeMethod(args[0], remainingArguments);
        }
    }

    private Object invokeMethod(final Object instance, final Object[] arguments) {
        try {
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            return method.invoke(instance, arguments);
        } catch (final IllegalArgumentException e) {
            throw new IllegalArgumentRuntimeException(e, method, instance, arguments);
        } catch (final IllegalAccessException e) {
            throw new IllegalAccessRuntimeException(e, method);
        } catch (final InvocationTargetException e) {
            throw new InvocationTargetRuntimeException(e, method);
        }
    }

    @Override
    public boolean isStatic() {
        return Modifier.isStatic(method.getModifiers());
    }

    @Override
    public boolean isFinal() {
        return Modifier.isFinal(method.getModifiers());
    }

    @Override
    public FluentClass<?> type() {
        final TypeLiteral<?> returnType = typeLiteral.getReturnType(method);
        if (returnType == null) {
            return null;
        }
        return reflectedTypeFactory.reflect(returnType);
    }

    @Override
    public String property() {
        final String name = name();

        if (name.length() > 2) {
            if (name.length() > 3) {
                if (name.startsWith("get") || name.startsWith("set")) {
                    return initialLowerCase(name.substring(3));
                }
            }
            if (name.startsWith("is")) {
                return initialLowerCase(name.substring(2));
            }
        }
        return method.getName();
    }

    private String initialLowerCase(final String substring) {
        return substring.substring(0, 1).toLowerCase() + substring.substring(1);
    }

    @Override
    public Method member() {
        return method;
    }

    @Override
    public Visibility visibility() {
        return visibilityFromModifiers(method.getModifiers());
    }

    private List<TypeVariable<Method>> typeParameters() {
        return Arrays.asList(method.getTypeParameters());
    }

    @Override
    public String toString() {
        final String visibility;
        if (visibility().toString().isEmpty()) {
            visibility = visibility().toString();
        } else {
            visibility = visibility().toString() + " ";
        }

        final String staticModifier;
        if (isStatic()) {
            staticModifier = "static ";
        } else {
            staticModifier = "";
        }

        final String finalModifier;
        if (isFinal()) {
            finalModifier = "final ";
        } else {
            finalModifier = "";
        }

        final String typeParameters;
        if (typeParameters().isEmpty()) {
            typeParameters = "";
        } else {
            typeParameters = "<" + joinFrom(typeParameters(), ", ").toString() + "> ";
        }

        final String arguments;
        if (argCount() > 0) {
            arguments = joinFrom(args(), ", ").toString();
        } else {
            arguments = "";
        }

        return format("%s%s%s%s%s %s(%s)", visibility, staticModifier, finalModifier, typeParameters, type(),
                method.getName(), arguments);
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj != null && this.getClass().equals(obj.getClass())) {
            final FluentMethodImpl that = (FluentMethodImpl) obj;
            return new EqualsBuilder().append(this.method, that.method).append(this.typeLiteral, that.typeLiteral)
                    .isEquals();
        }
        return false;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(method).append(typeLiteral).toHashCode();
    }

    @Override
    public FluentMethod rebind(final Object receiver) {
        return object(receiver).method(compatibleWith(this));
    }
}