it.unibo.alchemist.language.protelis.MethodCall.java Source code

Java tutorial

Introduction

Here is the source code for it.unibo.alchemist.language.protelis.MethodCall.java

Source

/*
 * Copyright (C) 2010-2015, Danilo Pianini and contributors
 * listed in the project's pom.xml file.
 * 
 * This file is part of Alchemist, and is distributed under the terms of
 * the GNU General Public License, with a linking exception, as described
 * in the file LICENSE in the Alchemist distribution's top directory.
 */
package it.unibo.alchemist.language.protelis;

import it.unibo.alchemist.language.protelis.datatype.Field;
import it.unibo.alchemist.language.protelis.datatype.Fields;
import it.unibo.alchemist.language.protelis.interfaces.AnnotatedTree;
import it.unibo.alchemist.language.protelis.java7.functionals.BiFunction;
import it.unibo.alchemist.language.protelis.java7.streams.Stream;
import it.unibo.alchemist.language.protelis.util.ReflectionUtils;
import it.unibo.alchemist.language.protelis.vm.ExecutionContext;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.ArrayUtils;

/**
 * @author Danilo Pianini
 *
 */
public class MethodCall extends AbstractAnnotatedTree<Object> {

    private static final long serialVersionUID = -2299070628855971997L;
    private transient Method method;
    private final boolean fieldComposable;
    private final boolean ztatic;

    /**
     * @param m
     *            the method to call
     * @param branch
     *            the Protelis sub-programs
     * @param isStatic
     *            if false, the first branch must contain the AnnotatedTree whose
     *            annotation will contain the object on which the method will be
     *            invoked
     */
    public MethodCall(final Method m, final List<AnnotatedTree<?>> branch, final boolean isStatic) {
        super(branch);
        Objects.requireNonNull(m, "No compatible method found.");
        method = m;
        ztatic = isStatic;

        boolean found = false;
        for (Class<?> clazz : method.getParameterTypes()) {
            if (Field.class.isAssignableFrom(clazz)) {
                found = true;
                break;
            }
        }
        fieldComposable = !found;
    }

    @Override
    public void eval(final ExecutionContext context) {
        projectAndEval(context);
        // Obtain target and arguments
        final Object target = ztatic ? null : getBranch(0).getAnnotation();
        final Stream<?> s = getBranchesAnnotationStream();
        final Object[] args = ztatic ? s.toArray() : s.skip(1).toArray();
        /*
         * Check if any of the parameters is a field
         */
        if (fieldComposable) {
            final boolean fieldTarget = target == null ? false : Field.class.isAssignableFrom(target.getClass());

            List<Integer> list = new ArrayList<>();
            for (Object o : args) {
                if (Field.class.isAssignableFrom(o.getClass())) {
                    list.add(ArrayUtils.indexOf(args, o));
                }
            }
            final int[] fieldIndexes = new int[list.size()];
            for (Integer i : list) {
                fieldIndexes[i] = list.get(i);
            }
            // End

            if (fieldTarget || fieldIndexes.length > 0) {
                setAnnotation(Fields.apply(new BiFunction<Object, Object[], Object>() {
                    @Override
                    public Object apply(Object actualT, Object[] actualA) {
                        return ReflectionUtils.invokeMethod(method, actualT, actualA);
                    }
                }, fieldTarget, fieldIndexes, target, args));
                return;
            }
        }
        setAnnotation(ReflectionUtils.invokeMethod(method, target, args));
    }

    /**
     * @return the method return type
     */
    public Class<?> getReturnType() {
        return method.getReturnType();
    }

    @Override
    public MethodCall copy() {
        return new MethodCall(method, deepCopyBranches(), ztatic);
    }

    @Override
    protected void asString(final StringBuilder sb, final int i) {
        sb.append(method.getName());
        sb.append('/');
        // TODO: Workaround for different Method API
        sb.append(method.getParameterTypes().length);
        sb.append(" (");
        fillBranches(sb, i, ',');
        sb.append(')');
    }

    private void writeObject(final ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(method.getDeclaringClass());
        out.writeUTF(method.getName());
        out.writeObject(method.getParameterTypes());
    }

    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        final Class<?> declaringClass = (Class<?>) in.readObject();
        final String methodName = in.readUTF();
        final Class<?>[] parameterTypes = (Class<?>[]) in.readObject();
        try {
            method = declaringClass.getMethod(methodName, parameterTypes);
        } catch (Exception e) {
            throw new IOException(String.format("Error occurred resolving deserialized method '%s.%s'",
                    declaringClass.getSimpleName(), methodName), e);
        }
    }

}