fr.obeo.emf.specimen.DirectWriteSpecimenGenerator.java Source code

Java tutorial

Introduction

Here is the source code for fr.obeo.emf.specimen.DirectWriteSpecimenGenerator.java

Source

/*******************************************************************************
 * Copyright (c) 2012 Obeo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Obeo - initial API and implementation
 *     Abel Gmez (AtlanMod) - Additional modifications      
 *******************************************************************************/

package fr.obeo.emf.specimen;

import static com.google.common.collect.Iterables.get;
import static com.google.common.primitives.Primitives.isWrapperType;
import static com.google.common.primitives.Primitives.unwrap;

import java.text.MessageFormat;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.logging.Logger;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.math3.distribution.IntegerDistribution;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;

import com.google.common.base.Optional;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;

import fr.inria.atlanmod.instantiator.neoEMF.IGenerator;
import fr.inria.atlanmod.neoemf.core.NeoEMFEObject;
import fr.obeo.emf.specimen.internal.EPackagesData;

/**
 * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
 * @author <a href="mailto:abel.gomez-llana@inria.fr">Abel Gomez</a>
 * @author <a href="mailto:abel.gomez-llana@inria.fr">Amine Benelallam</a>
 */
public class DirectWriteSpecimenGenerator implements IGenerator {

    public static Logger LOGGER = Logger.getLogger(DirectWriteSpecimenGenerator.class.getName());

    protected Random generator;
    protected ISpecimenConfiguration configuration;
    protected EPackagesData ePackagesData;

    /* inner Variable state */
    protected int currentDepth;
    protected int currentMaxDepth;
    protected int currentObjects;
    protected int goalObjects;

    public DirectWriteSpecimenGenerator(ISpecimenConfiguration configuration, long seed) {
        this.configuration = configuration;
        ePackagesData = new EPackagesData(configuration.ePackages(), configuration.ignoredEClasses());
        generator = new Random(seed);
    }

    public DirectWriteSpecimenGenerator() {
        // Do nothing
    }

    @Override
    public ISpecimenConfiguration getConfig() {
        return configuration;
    }

    public void generate(Resource resource) {

        resource.setModified(true);

        ListMultimap<EClass, String> indexByKind = ArrayListMultimap.create();

        ImmutableSet<EClass> possibleRootEClasses = configuration.possibleRootEClasses();

        currentDepth = 0;
        currentMaxDepth = 0;
        currentObjects = 0;
        goalObjects = configuration.getResourceSizeDistribution().sample();

        while (currentObjects < goalObjects) {
            EClass eClass = configuration.getNextRootEClass(possibleRootEClasses);
            currentMaxDepth = configuration.getDepthDistributionFor(eClass).sample();
            Optional<EObject> generateEObject = generateEObject(eClass, indexByKind);
            if (generateEObject.isPresent()) {
                resource.getContents().add(generateEObject.get());
            }
        }

        LOGGER.info("Generating cross-references");

        int totalEObjects = currentObjects;
        int currentEObject = 0;
        TreeIterator<EObject> eAllContents = resource.getAllContents();
        while (eAllContents.hasNext()) {
            currentEObject++;
            LOGGER.fine(
                    MessageFormat.format("Generating cross references {0} / {1}", currentEObject, totalEObjects));
            EObject eObject = eAllContents.next();
            generateCrossReferences(eObject, indexByKind);
        }

        LOGGER.info(MessageFormat.format("Requested #EObject={0}", goalObjects));

        LOGGER.info(MessageFormat.format("Actual #EObject={0}", ImmutableSet.copyOf(indexByKind.values()).size()));

        for (Map.Entry<EClass, Collection<String>> entry : indexByKind.asMap().entrySet()) {
            // Log number of elements for resolved EClasses
            EClass eClass = entry.getKey();
            if (!eClass.eIsProxy() || (eClass.eIsProxy() && EcoreUtil.resolve(eClass, resource) != eClass)) {
                LOGGER.info(MessageFormat.format("#{0}::{1}={2}", eClass.getEPackage().getNsURI(), eClass.getName(),
                        entry.getValue().size()));
            }
        }

        for (Map.Entry<EClass, Collection<String>> entry : indexByKind.asMap().entrySet()) {
            EClass eClass = entry.getKey();
            if (eClass.eIsProxy() && EcoreUtil.resolve(eClass, resource) == eClass) {
                // Warn about unresolved EClasses
                LOGGER.warning(MessageFormat.format("#{0} (unresolved)={1}", EcoreUtil.getURI(eClass),
                        entry.getValue().size()));
            }
        }

        LOGGER.info(MessageFormat.format("Generation finished for resource ''{0}''", resource.getURI()));
    }

    /**
     * @param eObject
     * @param indexByKind
     */
    protected void generateCrossReferences(EObject eObject, ListMultimap<EClass, String> indexByKind) {
        Iterable<EReference> eAllNonContainment = ePackagesData.eAllNonContainment(eObject.eClass());
        for (EReference eReference : eAllNonContainment) {
            EClass eReferenceType = eReference.getEReferenceType();
            IntegerDistribution distribution = configuration.getDistributionFor(eReference);

            if (eReference.isMany()) {
                @SuppressWarnings("unchecked")
                List<Object> values = (List<Object>) eObject.eGet(eReference);
                int sample = distribution.sample();
                LOGGER.fine(MessageFormat.format("Generating {0} values for EReference ''{1}'' in EObject {2}",
                        sample, eReference.getName(), eObject.toString()));
                for (int i = 0; i < sample; i++) {
                    List<EObject> possibleValues = resolveObjectsOfType(eReferenceType, indexByKind,
                            eObject.eResource());
                    if (!possibleValues.isEmpty()) {
                        final EObject nextEObject = possibleValues.get(generator.nextInt(possibleValues.size()));
                        values.add(nextEObject);
                    }
                }
            } else {
                if (eReference.isRequired() || booleanInDistribution(distribution)) {
                    LOGGER.fine(MessageFormat.format("Generating EReference ''{0}'' in EObject {1}",
                            eReference.getName(), eObject.toString()));
                    List<EObject> possibleValues = resolveObjectsOfType(eReferenceType, indexByKind,
                            eObject.eResource());
                    if (!possibleValues.isEmpty()) {
                        final EObject nextEObject = possibleValues.get(generator.nextInt(possibleValues.size()));
                        eObject.eSet(eReference, nextEObject);
                    }
                }
            }
        }
    }

    protected List<EObject> resolveObjectsOfType(EClass eReferenceType, ListMultimap<EClass, String> indexByKind,
            Resource resource) {
        List<EObject> possibleValues = new LinkedList<EObject>();
        for (String uriFrag : indexByKind.get(eReferenceType)) {
            possibleValues.add(resource.getEObject(uriFrag));
        }
        return possibleValues;
    }

    protected Optional<EObject> generateEObject(EClass eClass, ListMultimap<EClass, String> indexByKind) {
        final EObject eObject;
        currentObjects++;
        LOGGER.fine(MessageFormat.format("Generating EObject {0} / ~{1} (EClass={2})", currentObjects, goalObjects,
                eClass.getName()));
        eObject = createEObject(eClass, indexByKind);
        generateEAttributes(eObject, eClass);
        generateEContainmentReferences(eObject, eClass, indexByKind);
        return Optional.fromNullable(eObject);
    }

    protected EObject createEObject(EClass eClass, ListMultimap<EClass, String> indexByKind) {
        EObject eObject = eClass.getEPackage().getEFactoryInstance().create(eClass);

        indexByKind.put(eClass, ((NeoEMFEObject) eObject).neoemfId());
        for (EClass eSuperType : eClass.getEAllSuperTypes()) {
            indexByKind.put(eSuperType, ((NeoEMFEObject) eObject).neoemfId());
        }

        return eObject;
    }

    /**
     * @param eObject
     * @param eClass
     * @param indexByKind
     */
    protected void generateEContainmentReferences(EObject eObject, EClass eClass,
            ListMultimap<EClass, String> indexByKind) {
        for (EReference eReference : ePackagesData.eAllContainment(eClass)) {
            if (eReference.isRequired() || (currentObjects < goalObjects && currentDepth <= currentMaxDepth)) {
                generateEContainmentReference(eObject, eReference, indexByKind);
            }
        }

    }

    /**
     * @param eObject
     * @param eReference
     * @param indexByKind
     */
    protected void generateEContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, String> indexByKind) {
        currentDepth++;

        ImmutableList<EClass> eAllConcreteSubTypeOrSelf = ePackagesData.eAllConcreteSubTypeOrSelf(eReference);
        ImmutableMultiset<EClass> eAllConcreteSubTypesOrSelf = getEReferenceTypesWithWeight(eReference,
                eAllConcreteSubTypeOrSelf);

        if (!eAllConcreteSubTypesOrSelf.isEmpty()) {
            if (eReference.isMany()) {
                generateManyContainmentReference(eObject, eReference, indexByKind, eAllConcreteSubTypesOrSelf);
            } else {
                generateSingleContainmentReference(eObject, eReference, indexByKind, eAllConcreteSubTypesOrSelf);
            }
        }

        currentDepth--;
    }

    protected void generateSingleContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, String> indexByKind, ImmutableMultiset<EClass> eAllConcreteSubTypesOrSelf) {
        IntegerDistribution distribution = configuration.getDistributionFor(eReference);
        if (eReference.isRequired() || booleanInDistribution(distribution)) {
            LOGGER.fine(MessageFormat.format("Generating EReference ''{0}'' in EObject {1}", eReference.getName(),
                    eObject.toString()));
            int idx = generator.nextInt(eAllConcreteSubTypesOrSelf.size());
            final Optional<EObject> nextEObject = generateEObject(get(eAllConcreteSubTypesOrSelf, idx),
                    indexByKind);
            if (nextEObject.isPresent()) {
                eObject.eSet(eReference, nextEObject.get());
            }
        }
    }

    protected void generateManyContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, String> indexByKind, ImmutableMultiset<EClass> eAllConcreteSubTypesOrSelf) {
        IntegerDistribution distribution = configuration.getDistributionFor(eReference);
        @SuppressWarnings("unchecked")
        List<EObject> values = (List<EObject>) eObject.eGet(eReference);
        int sample = distribution.sample();
        LOGGER.fine(MessageFormat.format("Generating {0} values for EReference ''{1}'' in EObject {2}", sample,
                eReference.getName(), eObject.toString()));
        for (int i = 0; i < sample; i++) {
            int idx = generator.nextInt(eAllConcreteSubTypesOrSelf.size());
            final Optional<EObject> nextEObject = generateEObject(get(eAllConcreteSubTypesOrSelf, idx),
                    indexByKind);
            if (nextEObject.isPresent()) {
                values.add(nextEObject.get());
            }
        }
    }

    protected ImmutableMultiset<EClass> getEReferenceTypesWithWeight(EReference eReference,
            ImmutableList<EClass> eAllSubTypesOrSelf) {
        ImmutableMultiset.Builder<EClass> eAllSubTypesOrSelfWithWeights = ImmutableMultiset.builder();
        for (EClass eClass : eAllSubTypesOrSelf) {
            eAllSubTypesOrSelfWithWeights.addCopies(eClass, configuration.getWeightFor(eReference, eClass));
        }
        return eAllSubTypesOrSelfWithWeights.build();
    }

    /**
     * @param eObject
     * @param eClass
     */
    protected void generateEAttributes(EObject eObject, EClass eClass) {
        for (EAttribute eAttribute : ePackagesData.eAllAttributes(eClass)) {
            generateAttributes(eObject, eAttribute);
        }
    }

    protected void generateAttributes(EObject eObject, EAttribute eAttribute) {
        IntegerDistribution distribution = configuration.getDistributionFor(eAttribute);
        EDataType eAttributeType = eAttribute.getEAttributeType();
        Class<?> instanceClass = eAttributeType.getInstanceClass();
        if (eAttribute.isMany()) {
            generateManyAttribute(eObject, eAttribute, distribution, instanceClass);
        } else {
            generateSingleAttribute(eObject, eAttribute, distribution, instanceClass);
        }
    }

    protected void generateSingleAttribute(EObject eObject, EAttribute eAttribute, IntegerDistribution distribution,
            Class<?> instanceClass) {
        if (eAttribute.isRequired() || booleanInDistribution(distribution)) {
            final Object value;
            EDataType eAttributeType = eAttribute.getEAttributeType();
            if (eAttributeType instanceof EEnum) {
                assert instanceClass == null;
                EEnum eEnum = (EEnum) eAttributeType;
                instanceClass = int.class;
                int randomValue = Math.abs((Integer) nextValue(instanceClass));
                int size = eEnum.getELiterals().size();
                value = eEnum.getELiterals().get(randomValue % size);
            } else {
                value = nextValue(instanceClass);
            }
            eObject.eSet(eAttribute, value);
        }
    }

    protected void generateManyAttribute(EObject eObject, EAttribute eAttribute, IntegerDistribution distribution,
            Class<?> instanceClass) {
        @SuppressWarnings("unchecked")
        List<Object> values = (List<Object>) eObject.eGet(eAttribute);
        for (int i = distribution.getSupportLowerBound(); i < distribution.sample(); i++) {
            final Object value;
            EDataType eAttributeType = eAttribute.getEAttributeType();
            if (eAttributeType instanceof EEnum) {
                assert instanceClass == null;
                EEnum eEnum = (EEnum) eAttributeType;
                instanceClass = int.class;
                int randomValue = Math.abs((Integer) nextValue(instanceClass));
                int size = eEnum.getELiterals().size();
                value = eEnum.getELiterals().get(randomValue % size);
            } else {
                value = nextValue(instanceClass);
            }
            values.add(value);
        }
    }

    protected Object nextValue(Class<?> instanceClass) {
        final Object value;
        if (instanceClass.isPrimitive() || isWrapperType(instanceClass)) {
            value = nextPrimitive(unwrap(instanceClass));
        } else {
            value = nextObject(instanceClass);
        }
        return value;
    }

    /**
     * @param instanceClass
     */
    protected Object nextObject(Class<?> instanceClass) {
        if (instanceClass == String.class) {
            return RandomStringUtils.random(configuration.getValueDistributionFor(instanceClass).sample(), 0, 0,
                    true, true, null, generator);
        } else {
            LOGGER.warning(MessageFormat.format("Do not know how to randomly generate ''{0}'' object",
                    instanceClass.getName()));
        }
        return null;
    }

    /**
     * @param eObject
     * @param eAttribute
     * @param instanceClass
     */
    protected Object nextPrimitive(Class<?> instanceClass) {
        if (instanceClass == boolean.class) {
            return generator.nextBoolean();
        } else if (instanceClass == byte.class) {
            byte[] buff = new byte[1];
            generator.nextBytes(buff);
            return buff[0];
        } else if (instanceClass == char.class) {
            char nextChar = (char) generator.nextInt();
            return nextChar;
        } else if (instanceClass == double.class) {
            return generator.nextDouble();
        } else if (instanceClass == float.class) {
            return generator.nextFloat();
        } else if (instanceClass == int.class) {
            return generator.nextInt();
        } else if (instanceClass == long.class) {
            return generator.nextLong();
        } else if (instanceClass == short.class) {
            short nextShort = (short) generator.nextInt();
            return nextShort;
        } else {
            throw new IllegalArgumentException();
        }
    }

    protected boolean booleanInDistribution(IntegerDistribution distribution) {
        int sample = distribution.sample();
        return sample <= distribution.getNumericalMean();
    }

}