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

Java tutorial

Introduction

Here is the source code for fr.obeo.emf.specimen.SpecimenGenerator.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
 *******************************************************************************/
package fr.obeo.emf.specimen;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.math3.distribution.IntegerDistribution;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
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.Iterators;
import com.google.common.collect.ListMultimap;

import fr.obeo.emf.specimen.internal.EPackagesData;
import fr.obeo.emf.specimen.internal.Gpw;

import static com.google.common.collect.Lists.newArrayList;

import static com.google.common.collect.Maps.newHashMap;

import static com.google.common.collect.Iterables.get;

import static com.google.common.primitives.Primitives.isWrapperType;
import static com.google.common.primitives.Primitives.unwrap;

/**
 * @author <a href="mailto:mikael.barbero@obeo.fr">Mikael Barbero</a>
 * 
 */
public class SpecimenGenerator {

    private final Random generator;
    private final ISpecimenConfiguration c;
    private final EPackagesData ePackagesData;

    /* inner Variable state */
    private int currentDepth;
    private int currentMaxDepth;

    public SpecimenGenerator(ISpecimenConfiguration configuration) {
        c = configuration;
        ePackagesData = new EPackagesData(c.ePackages(), c.ignoredEClasses());
        generator = new Random();
    }

    public List<EObject> generate(ResourceSet resourceSet) {
        List<EObject> ret = newArrayList();
        ListMultimap<EClass, EObject> indexByKind = ArrayListMultimap.create();

        currentDepth = 0;
        currentMaxDepth = 0;

        for (EClass eClass : c.possibleRootEClasses()) {
            currentMaxDepth = c.getDepthDistributionFor(eClass).sample();
            int nbInstance = c.getRootDistributionFor(eClass).sample();
            for (int i = 0; i < nbInstance; i++) {
                System.out.println("Generating root " + eClass.getName() + " " + i + "/" + nbInstance);
                Optional<EObject> generateEObject = generateEObject(eClass, indexByKind);
                if (generateEObject.isPresent()) {
                    ret.add(generateEObject.get());
                }
            }
        }

        System.out.println("Generating XRef");

        for (EObject eObjectRoot : ret) {
            TreeIterator<EObject> eAllContents = eObjectRoot.eAllContents();
            while (eAllContents.hasNext()) {
                EObject eObject = eAllContents.next();
                generateCrossReferences(eObject, indexByKind);
            }
        }

        Map<EClass, Integer> resourcesSize = newHashMap();
        for (EClass eClass : c.possibleRootEClasses()) {
            setNextResourceSizeForType(resourcesSize, eClass);
        }

        System.out.println("Generating resources");

        for (EObject eObjectRoot : ret) {
            TreeIterator<EObject> eAllContents = eObjectRoot.eAllContents();
            createResource(resourceSet, resourcesSize, eAllContents, eObjectRoot, ret);
            while (eAllContents.hasNext()) {
                EObject eObject = eAllContents.next();
                createResource(resourceSet, resourcesSize, eAllContents, eObject, ret);
            }
        }

        System.out.println("#EObject=" + ImmutableSet.copyOf(indexByKind.values()).size());
        System.out.println("#Resource=" + resourceSet.getResources().size());

        for (Map.Entry<EClass, Collection<EObject>> entry : indexByKind.asMap().entrySet()) {
            EClass eClass = entry.getKey();
            System.out.println("#" + eClass.getEPackage().getNsURI() + "::" + entry.getKey().getName() + "="
                    + entry.getValue().size());
        }

        return ret;
    }

    private void createResource(ResourceSet resourceSet, Map<EClass, Integer> resourcesSize,
            TreeIterator<EObject> eAllContents, EObject eObject, List<EObject> ret) {
        TreeIterator<EObject> properContents = EcoreUtil.getAllProperContents(eObject, true);
        int allProperContentsSize = Iterators.size(properContents);
        EClass eClass = eObject.eClass();
        Integer integer = resourcesSize.get(eClass);
        if (integer != null && allProperContentsSize <= integer.intValue()) {
            createResource(resourceSet, eObject);
            setNextResourceSizeForType(resourcesSize, eClass);
            eAllContents.prune();
        } else if (eObject.eContainer() == null) {
            List<EObject> precedingSibling = ret.subList(0, ret.indexOf(eObject));
            TreeIterator<Object> allPrecedingSiblingContents = EcoreUtil.getAllProperContents(precedingSibling,
                    true);
            int allPrecedingSiblingContentsSize = Iterators.size(allPrecedingSiblingContents);
            if (integer != null && allPrecedingSiblingContentsSize >= integer.intValue()) {
                createResource(resourceSet, eObject);
                setNextResourceSizeForType(resourcesSize, eClass);
                eAllContents.prune();
            }
        }
    }

    private void setNextResourceSizeForType(Map<EClass, Integer> resourcesSize, EClass eClass) {
        IntegerDistribution sizeDistribution = c.getResourceSizeDistribution(eClass);
        int desiredSize = sizeDistribution.sample();
        resourcesSize.put(eClass, desiredSize);
    }

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

            if (eReference.isMany()) {
                List<Object> values = (List<Object>) eObject.eGet(eReference);
                int sample = distribution.sample();
                for (int i = 0; i < sample; i++) {
                    List<EObject> possibleValues = indexByKind.get(eReferenceType);
                    if (!possibleValues.isEmpty()) {
                        final EObject nextEObject = possibleValues.get(generator.nextInt(possibleValues.size()));
                        values.add(nextEObject);
                    }
                }
            } else {
                if (booleanInDistribution(distribution)) {
                    List<EObject> possibleValues = indexByKind.get(eReferenceType);
                    if (!possibleValues.isEmpty()) {
                        final EObject nextEObject = possibleValues.get(generator.nextInt(possibleValues.size()));
                        eObject.eSet(eReference, nextEObject);
                    }
                }
            }
        }
    }

    private Optional<EObject> generateEObject(EClass eClass, ListMultimap<EClass, EObject> indexByKind) {
        final EObject eObject;
        if (currentDepth <= currentMaxDepth) {
            eObject = createEObject(eClass, indexByKind);
            generateEAttributes(eObject, eClass);
            generateEContainmentReferences(eObject, eClass, indexByKind);
        } else {
            eObject = null;
        }
        return Optional.fromNullable(eObject);
    }

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

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

        return eObject;
    }

    /**
     * @param eObject
     * @return
     */
    private Resource createResource(ResourceSet resourceSet, EObject root) {
        Resource resource = resourceSet.createResource(URI.createURI(Gpw.generate(16) + ".xmi"));
        resource.getContents().add(root);
        return resource;
    }

    /**
     * @param eObject
     * @param eClass
     * @param indexByKind
     */
    private void generateEContainmentReferences(EObject eObject, EClass eClass,
            ListMultimap<EClass, EObject> indexByKind) {
        for (EReference eReference : ePackagesData.eAllContainment(eClass)) {
            generateEContainmentReference(eObject, eReference, indexByKind);
        }

    }

    /**
     * @param eObject
     * @param eReference
     * @param indexByKind
     */
    private void generateEContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, EObject> 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--;
    }

    private void generateSingleContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, EObject> indexByKind, ImmutableMultiset<EClass> eAllConcreteSubTypesOrSelf) {
        IntegerDistribution distribution = c.getDistributionFor(eReference);
        if (booleanInDistribution(distribution)) {
            int idx = generator.nextInt(eAllConcreteSubTypesOrSelf.size());
            final Optional<EObject> nextEObject = generateEObject(get(eAllConcreteSubTypesOrSelf, idx),
                    indexByKind);
            if (nextEObject.isPresent()) {
                eObject.eSet(eReference, nextEObject.get());
            }
        }
    }

    private void generateManyContainmentReference(EObject eObject, EReference eReference,
            ListMultimap<EClass, EObject> indexByKind, ImmutableMultiset<EClass> eAllConcreteSubTypesOrSelf) {
        IntegerDistribution distribution = c.getDistributionFor(eReference);
        List<EObject> values = (List<EObject>) eObject.eGet(eReference);
        int sample = distribution.sample();
        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());
            }
        }
    }

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

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

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

    private void generateSingleAttribute(EObject eObject, EAttribute eAttribute, IntegerDistribution distribution,
            Class<?> instanceClass) {
        if (booleanInDistribution(distribution)) {
            final Object value = nextValue(instanceClass);
            eObject.eSet(eAttribute, value);
        }
    }

    private void generateManyAttribute(EObject eObject, EAttribute eAttribute, IntegerDistribution distribution,
            Class<?> instanceClass) {
        List<Object> values = (List<Object>) eObject.eGet(eAttribute);
        for (int i = distribution.getSupportLowerBound(); i < distribution.sample(); i++) {
            final Object value = nextValue(instanceClass);
            values.add(value);
        }
    }

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

    /**
     * @param instanceClass
     */
    private Object nextObject(Class<?> instanceClass) {
        if (instanceClass == String.class) {
            return Gpw.generate(generator.nextInt(24) + 1);
        } else {
            log("Do not know how to randomly generate " + instanceClass.getName() + " object");
        }
        return null;
    }

    /**
     * @param string
     */
    private void log(String string) {
        System.out.println(string);
    }

    /**
     * @param eObject
     * @param eAttribute
     * @param instanceClass
     */
    private 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();
        }
    }

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