fr.inria.atlanmod.instantiator.SpecimenGenerator.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.atlanmod.instantiator.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.inria.atlanmod.instantiator;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import jline.TerminalFactory;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
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.EPackage;
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.xmi.impl.XMIResourceImpl;

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.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.Primitives;

import fr.inria.atlanmod.instantiator.exceptions.GenerationException;
import fr.inria.atlanmod.instantiator.impl.DefaultModelGenerator;
import fr.inria.atlanmod.instantiator.internal.EPackagesData;
import fr.inria.atlanmod.instantiator.internal.Gpw;
import fr.inria.atlanmod.instantiator.util.UniformLongDistribution;

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

    private static final String METAMODEL = "m";
    private static final String METAMODEL_LONG = "metamodel";
    private static final String ADDITIONAL_METAMODEL = "a";
    private static final String ADDITIONAL_METAMODEL_LONG = "additional-metamodel";
    private static final String OUTPUT_DIR = "o";
    private static final String OUTPUT_DIR_LONG = "output-dir";
    private static final String N_MODELS = "n";
    private static final String N_MODELS_LONG = "number-models";
    private static final String SIZE = "s";
    private static final String SIZE_LONG = "size";
    private static final String SEED = "e";
    private static final String SEED_LONG = "seed";

    private static class OptionComarator<T extends Option> implements Comparator<T> {
        private static final String OPTS_ORDER = "maonse";

        @Override
        public int compare(T o1, T o2) {
            return OPTS_ORDER.indexOf(o1.getOpt()) - OPTS_ORDER.indexOf(o2.getOpt());
        }
    }

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

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

    public static void main(String[] args) throws GenerationException, IOException {

        Options options = new Options();

        configureOptions(options);

        CommandLineParser parser = new GnuParser();

        try {
            CommandLine commandLine = parser.parse(options, args);

            String metamodel = commandLine.getOptionValue(METAMODEL);
            DefaultModelGenerator modelGen = new DefaultModelGenerator(URI.createFileURI(metamodel));

            if (commandLine.hasOption(ADDITIONAL_METAMODEL)) {
                for (String additionalMetamodel : commandLine.getOptionValues(ADDITIONAL_METAMODEL)) {
                    URI additionalMetamodelUri = URI.createFileURI(additionalMetamodel);
                    Resource resource = new XMIResourceImpl(additionalMetamodelUri);
                    resource.load(Collections.emptyMap());
                    registerPackages(resource);
                }
            }

            if (commandLine.hasOption(OUTPUT_DIR)) {
                String outDir = commandLine.getOptionValue(OUTPUT_DIR);
                modelGen.setSamplesPath(Paths.get(outDir));
            } else {
                modelGen.setSamplesPath(Paths.get("."));
            }
            if (commandLine.hasOption(N_MODELS)) {
                int models = ((Number) commandLine.getParsedOptionValue(N_MODELS)).intValue();
                modelGen.setSetSize(new int[] { models });
            } else {
                modelGen.setSetSize(new int[] { 1 });
            }
            if (commandLine.hasOption(SIZE)) {
                long size = ((Number) commandLine.getParsedOptionValue(SIZE)).longValue();
                modelGen.setModelsSize(new long[] { size });
            } else {
                modelGen.setModelsSize(new long[] { 1000 });
            }
            if (commandLine.hasOption(SEED)) {
                long seed = ((Number) commandLine.getParsedOptionValue(SEED)).longValue();
                modelGen.setSeed(seed);
            } else {
                modelGen.setSeed(System.currentTimeMillis());
            }
            modelGen.runGeneration();
        } catch (ParseException e) {
            System.err.println(e.getLocalizedMessage());
            HelpFormatter formatter = new HelpFormatter();
            formatter.setOptionComparator(new OptionComarator<Option>());
            try {
                formatter.setWidth(Math.max(TerminalFactory.get().getWidth(), 80));
            } catch (Throwable t) {
                // Nothing to do...
            }
            ;
            formatter.printHelp("java -jar <this-file.jar>", options, true);
        }
    }

    /**
     * Configures the program options
     *
     * @param options
     */
    private static void configureOptions(Options options) {
        Option metamodelOpt = OptionBuilder.create(METAMODEL);
        metamodelOpt.setLongOpt(METAMODEL_LONG);
        metamodelOpt.setArgName("path_to_metamodel.ecore");
        metamodelOpt.setDescription("Ecore metamodel");
        metamodelOpt.setArgs(1);
        metamodelOpt.setRequired(true);

        Option additionalMetamodelOpt = OptionBuilder.create(ADDITIONAL_METAMODEL);
        additionalMetamodelOpt.setLongOpt(ADDITIONAL_METAMODEL_LONG);
        additionalMetamodelOpt.setArgName("path_to_metamodel.ecore");
        additionalMetamodelOpt.setDescription("Additional ecore metamodel(s) that need to be registered");
        additionalMetamodelOpt.setArgs(Option.UNLIMITED_VALUES);

        Option outDirOpt = OptionBuilder.create(OUTPUT_DIR);
        outDirOpt.setLongOpt(OUTPUT_DIR_LONG);
        outDirOpt.setArgName("path_to_output.dir");
        outDirOpt.setDescription("Output directory (defaults to working dir)");
        outDirOpt.setArgs(1);

        Option nModelsOpt = OptionBuilder.create(N_MODELS);
        nModelsOpt.setLongOpt(N_MODELS_LONG);
        nModelsOpt.setArgName("models");
        nModelsOpt.setDescription("Number of generated models (defaults to 1)");
        nModelsOpt.setType(Number.class);
        nModelsOpt.setArgs(1);

        Option sizeOption = OptionBuilder.create(SIZE);
        sizeOption.setLongOpt(SIZE_LONG);
        sizeOption.setArgName("size");
        sizeOption.setDescription("Models' size (defaults to 1000)");
        sizeOption.setType(Number.class);
        sizeOption.setArgs(1);

        Option seedOption = OptionBuilder.create(SEED);
        seedOption.setLongOpt(SEED_LONG);
        seedOption.setArgName("seed");
        seedOption.setDescription("Seed number (random by default)");
        seedOption.setType(Number.class);
        seedOption.setArgs(1);

        options.addOption(metamodelOpt);
        options.addOption(additionalMetamodelOpt);
        options.addOption(outDirOpt);
        options.addOption(nModelsOpt);
        options.addOption(sizeOption);
        options.addOption(seedOption);
    }

    /**
     * Registers the packages
     * @param resource
     */
    public static void registerPackages(Resource resource) {
        EObject eObject = resource.getContents().get(0);
        if (eObject instanceof EPackage) {
            EPackage p = (EPackage) eObject;
            EPackage.Registry.INSTANCE.put(p.getNsURI(), p);
        }
    }

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

        if (c.getSeed() != 0L) {
            generator = new Random(c.getSeed());
        } else {
            generator = new Random();
        }
    }

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

        currentDepth = 0;
        currentMaxDepth = 0;

        for (EClass eClass : c.possibleRootEClasses()) {
            currentMaxDepth = c.getDepthDistributionFor(eClass).sample();
            long nbInstance = c.getRootDistributionFor(eClass).sample();
            for (int i = 0; i < nbInstance; i++) {
                log(MessageFormat.format("Generating EObject {0} out of {1}", i + 1, nbInstance));
                Optional<EObject> generateEObject = generateEObject(eClass, indexByKind);
                if (generateEObject.isPresent()) {
                    ret.add(generateEObject.get());
                }
            }
        }

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

        for (int i = 0; i < ret.size(); i++) {
            EObject eObjectRoot = ret.get(i);
            log(MessageFormat.format("Generating cross references for EObject {0} out of {1}", i + 1, ret.size()));
            TreeIterator<EObject> eAllContents = eObjectRoot.eAllContents();
            while (eAllContents.hasNext()) {
                EObject eObject = eAllContents.next();
                generateCrossReferences(eObject, indexByKind);
            }
        }

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

        return ret;
    }

    private void setNextResourceSizeForType(Map<EClass, Long> resourcesSize, EClass eClass) {
        UniformLongDistribution sizeDistribution = c.getResourceSizeDistribution(eClass);
        long 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();
            UniformLongDistribution distribution = c.getDistributionFor(eReference);
            if (eReference.isMany()) {
                @SuppressWarnings("unchecked")
                List<Object> values = (List<Object>) eObject.eGet(eReference);
                long sample;
                do {
                    sample = distribution.sample();
                } while (sample < eReference.getLowerBound());
                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 (eReference.getLowerBound() != 0 || booleanInDistribution(distribution)) {// eReference.getLowerBound()
                    // ==
                    // 1
                    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
     * @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);
        // System.out.println(eReference.getName());
        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) {
        // DONE look if the lowerbound is 1
        UniformLongDistribution distribution = c.getDistributionFor(eReference);
        if (eReference.getLowerBound() != 0 || booleanInDistribution(distribution)) {// eReference.getLowerBound()
            // ==
            // 1
            int idx = generator.nextInt(eAllConcreteSubTypesOrSelf.size());
            final Optional<EObject> nextEObject = generateEObject(Iterables.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) {
        // DONE look if the lowerbound is 1
        UniformLongDistribution distribution = c.getDistributionFor(eReference);
        @SuppressWarnings("unchecked")
        List<EObject> values = (List<EObject>) eObject.eGet(eReference);
        long sample;
        do {
            sample = distribution.sample();
        } while (sample < eReference.getLowerBound());
        for (int i = 0; i < sample; i++) {
            int idx = generator.nextInt(eAllConcreteSubTypesOrSelf.size());
            final Optional<EObject> nextEObject = generateEObject(Iterables.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) {
        UniformLongDistribution distribution = c.getDistributionFor(eAttribute);
        EDataType eAttributeType = eAttribute.getEAttributeType();
        Class<?> instanceClass = eAttributeType.getInstanceClass();
        // System.out.println(eAttribute.getName());
        if (eAttribute.isMany()) {
            generateManyAttribute(eObject, eAttribute, distribution, instanceClass);
        } else {
            generateSingleAttribute(eObject, eAttribute, distribution, instanceClass);
        }
    }

    private void generateSingleAttribute(EObject eObject, EAttribute eAttribute,
            UniformLongDistribution distribution, Class<?> instanceClass) {
        boolean bool = booleanInDistribution(distribution);
        // DONE look if the lowerbound is 1
        if (eAttribute.getLowerBound() != 0 || bool) {// eAttribute.getLowerBound()
            // == 1
            final Object value = nextValue(instanceClass);
            eObject.eSet(eAttribute, value);
        }
    }

    private void generateManyAttribute(EObject eObject, EAttribute eAttribute, UniformLongDistribution distribution,
            Class<?> instanceClass) {
        // DONE look if the lowerbound is 1
        @SuppressWarnings("unchecked")
        List<Object> values = (List<Object>) eObject.eGet(eAttribute);
        long lowerbound;
        do {
            lowerbound = distribution.sample();
        } while (lowerbound < eAttribute.getLowerBound());
        for (int i = 0; i < lowerbound; i++) {
            final Object value = nextValue(instanceClass);
            values.add(value);
        }
    }

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

    /**
     * @param instanceClass
     */
    private Object nextObject(Class<?> instanceClass) {
        if (instanceClass == String.class) {
            return Gpw.generate(generator.nextInt(24) + 1);
        } else if (Number.class.isAssignableFrom(instanceClass)) {
            try {
                Method method = instanceClass.getMethod("valueOf", long.class);
                return method.invoke(null, Gpw.generate(generator.nextInt(24) + 1).hashCode());
            } catch (IllegalAccessException e) {
                log(e.getLocalizedMessage());
            } catch (NoSuchMethodException e) {
                log(e.getLocalizedMessage());
            } catch (SecurityException e) {
                log(e.getLocalizedMessage());
            } catch (IllegalArgumentException e) {
                log(e.getLocalizedMessage());
            } catch (InvocationTargetException e) {
                log(e.getLocalizedMessage());
            }
        } else {
            log("Do not know how to randomly generate " + instanceClass.getName() + " object");
        }
        return null;
    }

    /**
     * @param string
     */
    private void log(String string) {
        Logger.getGlobal().log(Level.INFO, 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(UniformLongDistribution distribution) {
        long sample = distribution.sample();
        // System.out.println(sample < distribution.getNumericalMean());
        return sample < distribution.getNumericalMean();
    }
}