hu.bme.mit.sette.common.model.snippet.SnippetContainer.java Source code

Java tutorial

Introduction

Here is the source code for hu.bme.mit.sette.common.model.snippet.SnippetContainer.java

Source

/*
 * SETTE - Symbolic Execution based Test Tool Evaluator
 *
 * SETTE is a tool to help the evaluation and comparison of symbolic execution
 * based test input generator tools.
 *
 * Budapest University of Technology and Economics (BME)
 *
 * Authors: Lajos Cseppent <lajos.cseppento@inf.mit.bme.hu>, Zoltn Micskei
 * <micskeiz@mit.bme.hu>
 *
 * Copyright 2014
 *
 * 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 hu.bme.mit.sette.common.model.snippet;

import hu.bme.mit.sette.annotations.SetteNotSnippet;
import hu.bme.mit.sette.annotations.SetteSnippetContainer;
import hu.bme.mit.sette.common.snippets.JavaVersion;
import hu.bme.mit.sette.common.util.SetteAnnotationUtils;
import hu.bme.mit.sette.common.util.reflection.AnnotationMap;
import hu.bme.mit.sette.common.validator.AbstractValidator;
import hu.bme.mit.sette.common.validator.GeneralValidator;
import hu.bme.mit.sette.common.validator.exceptions.ValidatorException;
import hu.bme.mit.sette.common.validator.reflection.ClassType;
import hu.bme.mit.sette.common.validator.reflection.ClassValidator;
import hu.bme.mit.sette.common.validator.reflection.ConstructorValidator;
import hu.bme.mit.sette.common.validator.reflection.FieldValidator;
import hu.bme.mit.sette.common.validator.reflection.MethodValidator;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import javax.lang.model.type.NullType;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

/**
 * Represents a snippet container (which is a Java class).
 */
public final class SnippetContainer implements Comparable<SnippetContainer> {
    /** The Java class of the snippet container. */
    private final Class<?> javaClass;

    /** The category . */
    private final String category;

    /** The goal. */
    private final String goal;

    /** The required Java version. */
    private final JavaVersion requiredJavaVersion;

    /** The code snippets. */
    private final Map<String, Snippet> snippets;

    /** The corresponding snippet input factory container. */
    private final SnippetInputFactoryContainer inputFactoryContainer;

    /**
     * Instantiates a new snippet container.
     *
     * @param pJavaClass
     *            the Java class
     * @param classLoader
     *            the class loader for loading snippet project classes
     * @throws ValidatorException
     *             if validation has failed
     */
    SnippetContainer(final Class<?> pJavaClass, final ClassLoader classLoader) throws ValidatorException {
        Validate.notNull(pJavaClass, "The Java class must not be null");
        javaClass = pJavaClass;

        // start validation
        GeneralValidator validator = new GeneralValidator(SnippetContainer.class);

        // validate class and get container annotation
        SetteSnippetContainer containerAnnotation = validateClass(validator);
        // validate fields
        validateFields(validator);
        // validate constructor
        validateConstructor(validator);
        // validate methods and get snippet methods
        Map<String, Method> snippetMethods = validateMethods(validator);

        // save annotation data to fields
        category = containerAnnotation.category();
        goal = containerAnnotation.goal();
        requiredJavaVersion = containerAnnotation.requiredJavaVersion();

        // add snippet methods
        snippets = new HashMap<>();

        for (Method method : snippetMethods.values()) {
            if (method != null) {
                // the method is a snippet method
                try {
                    // create and add snippet object
                    Snippet snippet = new Snippet(this, method, classLoader);
                    snippets.put(method.getName(), snippet);
                } catch (ValidatorException e) {
                    validator.addChild(e.getValidator());
                }
            }
        }

        // input factory container
        SnippetInputFactoryContainer inputFactCont = null;
        if (!containerAnnotation.inputFactoryContainer().equals(NullType.class)) {
            // input factory container is present
            try {
                // create input factory container
                inputFactCont = new SnippetInputFactoryContainer(this, containerAnnotation.inputFactoryContainer(),
                        classLoader);
            } catch (ValidatorException e) {
                validator.addChild(e.getValidator());
            }
        }

        // set input factory container
        inputFactoryContainer = inputFactCont;

        validator.validate();
    }

    /**
     * Validates the class and its annotations.
     *
     * @param validator
     *            a validator
     * @return the {@link SetteSnippetContainer} annotation
     */
    private SetteSnippetContainer validateClass(final AbstractValidator<?> validator) {
        // check: "public final class", no superclass, interface, declared
        // class, exactly one constructor
        ClassValidator v = new ClassValidator(javaClass);
        v.type(ClassType.REGULAR_CLASS);
        v.withModifiers(Modifier.PUBLIC | Modifier.FINAL);
        v.withoutModifiers(Modifier.ABSTRACT);
        v.synthetic(false);
        v.superclass(Object.class).interfaceCount(0).memberClassCount(0);
        v.declaredConstructorCount(1);

        // check: only @SetteSnippetContainer
        SetteSnippetContainer containerAnn = null;

        AnnotationMap classAnns = SetteAnnotationUtils.getSetteAnnotations(javaClass);

        containerAnn = (SetteSnippetContainer) classAnns.get(SetteSnippetContainer.class);

        if (containerAnn == null) {
            v.addException(
                    "The Java class must have the annotation @" + SetteSnippetContainer.class.getSimpleName());
        } else {
            if (classAnns.size() != 1) {
                v.addException("The Java class must not have any " + "SETTE annotations other than @"
                        + SetteSnippetContainer.class.getSimpleName());
            }

            if (StringUtils.isBlank(containerAnn.category())) {
                v.addException(
                        "The category in @" + SetteSnippetContainer.class.getSimpleName() + " must not be blank");
            }

            if (StringUtils.isBlank(containerAnn.goal())) {
                v.addException(
                        "The goal in @" + SetteSnippetContainer.class.getSimpleName() + " must not be blank");
            }

            if (containerAnn.requiredJavaVersion() == null) {
                v.addException("The reqired Java version in @" + SetteSnippetContainer.class.getSimpleName()
                        + " must not be null");
            }
        }

        validator.addChildIfInvalid(v);

        return containerAnn;
    }

    /**
     * Validates the fields of the class.
     *
     * @param validator
     *            a validator
     */
    private void validateFields(final AbstractValidator<?> validator) {
        // check: only constant ("public static final") or synthetic fields
        for (Field field : javaClass.getDeclaredFields()) {
            if (field.isSynthetic()) {
                // skip for synthetic fields (e.g. switch for enum generates
                // synthetic methods and fields)
                continue;
            }

            FieldValidator v = new FieldValidator(field);
            v.withModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
            v.withoutModifiers(Modifier.SYNCHRONIZED | Modifier.TRANSIENT | Modifier.VOLATILE);

            validator.addChildIfInvalid(v);
        }
    }

    /**
     * Validates the constructor of the class.
     *
     * @param validator
     *            a validator
     */
    private void validateConstructor(final AbstractValidator<?> validator) {
        if (javaClass.getDeclaredConstructors().length != 1) {
            // constructor count is validated with the class
            return;
        }

        Constructor<?> constructor = javaClass.getDeclaredConstructors()[0];
        ConstructorValidator v = new ConstructorValidator(constructor);
        v.withModifiers(Modifier.PRIVATE).parameterCount(0);
        v.synthetic(false);

        // check: constructor throws new
        // UnsupportedOperationException("Static class")

        Throwable exception = null;
        try {
            // call the private ctor
            constructor.setAccessible(true);
            constructor.newInstance();
        } catch (Exception e) {
            exception = e.getCause();
        } finally {
            // restore visibility
            constructor.setAccessible(false);
        }

        if (exception == null || !exception.getClass().equals(UnsupportedOperationException.class)
                || !exception.getMessage().equals("Static class")) {
            v.addException("The constructor must throw an " + "UnsupportedOperationException with "
                    + "the message \"Static class\"");
        }

        validator.addChildIfInvalid(v);
    }

    /**
     * Validates methods of the class.
     *
     * @param validator
     *            a validator
     * @return a map containing the snippet methods by their name
     */
    private Map<String, Method> validateMethods(final AbstractValidator<?> validator) {
        // check: only "[public|private] static" or synthetic methods
        Map<String, Method> snippetMethods = new HashMap<String, Method>();

        for (Method method : javaClass.getDeclaredMethods()) {
            if (method.isSynthetic()) {
                // skip synthetic methods
                continue;
            }

            MethodValidator v = new MethodValidator(method);

            if (snippetMethods.get(method.getName()) != null) {
                v.addException("The method must have a unique name");
            }

            int methodModifiers = method.getModifiers();

            if (!Modifier.isPublic(methodModifiers) && !Modifier.isPrivate(methodModifiers)) {
                v.addException("The method must be public or private");
            }

            v.withModifiers(Modifier.STATIC);
            v.withoutModifiers(Modifier.ABSTRACT | Modifier.FINAL | Modifier.NATIVE | Modifier.SYNCHRONIZED);

            AnnotationMap methodAnns = SetteAnnotationUtils.getSetteAnnotations(method);

            if (Modifier.isPublic(methodModifiers)) {
                if (methodAnns.get(SetteNotSnippet.class) == null) {
                    // should be snippet, validated by Snippet class and added
                    // later
                    snippetMethods.put(method.getName(), method);
                } else {
                    // not snippet
                    snippetMethods.put(method.getName(), null);

                    if (methodAnns.size() != 1) {
                        v.addException("The method must not have " + "any other SETTE annotations "
                                + "if it is not a snippet.");
                    }
                }
            } else {
                // method is private
                if (methodAnns.size() != 0) {
                    v.addException("The method must not have " + "any SETTE annotations");
                }
            }

            validator.addChildIfInvalid(v);
        }

        return snippetMethods;
    }

    /**
     * Gets the Java class of the snippet container.
     *
     * @return the Java class of the snippet container
     */
    public Class<?> getJavaClass() {
        return javaClass;
    }

    /**
     * Gets the category .
     *
     * @return the category
     */
    public String getCategory() {
        return category;
    }

    /**
     * Gets the goal.
     *
     * @return the goal
     */
    public String getGoal() {
        return goal;
    }

    /**
     * Gets the required Java version.
     *
     * @return the required Java version
     */
    public JavaVersion getRequiredJavaVersion() {
        return requiredJavaVersion;
    }

    /**
     * Gets the code snippets.
     *
     * @return the code snippets
     */
    public Map<String, Snippet> getSnippets() {
        return Collections.unmodifiableMap(snippets);
    }

    /**
     * Gets the corresponding snippet input factory container.
     *
     * @return the corresponding snippet input factory container
     */
    public SnippetInputFactoryContainer getInputFactoryContainer() {
        return inputFactoryContainer;
    }

    /** The {@link Comparator} for the class. */
    public static final Comparator<SnippetContainer> COMPARATOR;

    static {
        COMPARATOR = new Comparator<SnippetContainer>() {
            @Override
            public int compare(final SnippetContainer o1, final SnippetContainer o2) {
                if (o1 == null) {
                    return -1;
                } else if (o2 == null) {
                    return 1;
                } else {
                    return o1.javaClass.getName().compareTo(o2.javaClass.getName());
                }
            }
        };
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    @Override
    public int compareTo(final SnippetContainer o) {
        return SnippetContainer.COMPARATOR.compare(this, o);
    }
}