fr.inria.atlanmod.instantiator.neoEMF.GenericMetamodelConfig.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.atlanmod.instantiator.neoEMF.GenericMetamodelConfig.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Abel Gmez (AtlanMod) 
 * 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:
 *     Abel Gmez (AtlanMod) - Additional modifications      
 *******************************************************************************/

package fr.inria.atlanmod.instantiator.neoEMF;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.apache.commons.lang3.Range;
import org.apache.commons.math3.distribution.AbstractIntegerDistribution;
import org.apache.commons.math3.distribution.IntegerDistribution;
import org.apache.commons.math3.distribution.UniformIntegerDistribution;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
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 com.google.common.collect.ImmutableSet;

import fr.obeo.emf.specimen.ISpecimenConfiguration;

/**
 * @author <a href="mailto:abel.gomez-llana@inria.fr">Abel Gomez</a>
 * @author <A href="mailto:amine.benelallam@inria.fr">Amine Benelallam</a>
 *
 */
public class GenericMetamodelConfig implements ISpecimenConfiguration {

    public static final int DEFAULT_AVERAGE_PROPERTIES_SIZE = 8;

    public static final int DEFAULT_AVERAGE_REFERENCES_SIZE = 8;

    public static final int DEFAULT_AVERAGE_VALUES_LENGTH = 24; // minimal size encouraged by HBase

    public static final float DEFAULT_PROPERTIES_DEVIATION = 0.1f;

    public static final float DEFAULT_REFERENCES_DEVIATION = 0.1f;

    public static final float DEFAULT_VALUES_DEVIATION = 0.1f;

    protected final Random random;

    protected final long seed;

    protected final Resource metamodelResource;

    protected final Range<Integer> elementsRange;

    protected Range<Integer> propertiesRange = Range.between(
            Math.round(DEFAULT_AVERAGE_PROPERTIES_SIZE * (1 - DEFAULT_PROPERTIES_DEVIATION)),
            Math.round(DEFAULT_AVERAGE_PROPERTIES_SIZE * (1 + DEFAULT_PROPERTIES_DEVIATION)));

    protected Range<Integer> referencesRange = Range.between(
            Math.round(DEFAULT_AVERAGE_REFERENCES_SIZE * (1 - DEFAULT_REFERENCES_DEVIATION)),
            Math.round(DEFAULT_AVERAGE_REFERENCES_SIZE * (1 + DEFAULT_REFERENCES_DEVIATION)));

    protected Range<Integer> valuesRange = Range.between(
            Math.round(DEFAULT_AVERAGE_VALUES_LENGTH * (1 - DEFAULT_VALUES_DEVIATION)),
            Math.round(DEFAULT_AVERAGE_VALUES_LENGTH * (1 + DEFAULT_VALUES_DEVIATION)));

    protected Map<Object, IntegerDistribution> distributions = new HashMap<Object, IntegerDistribution>();

    /**
     * Creates a new {@link GenericMetamodelConfig}
     * 
     * @param metamodelResource
     *            The resource containing the metamodel for which the
     *            {@link SpecimenGenerator} will create instances
     * 
     * @param elementsRange
     *            The minimum and maximum size allowed for resources created
     *            using this {@link ISpecimenConfiguration}
     * @param seed
     *            The <code>seed</code> for the generator of {@link Random}
     *            numbers. Useful to create models in a deterministic way.
     */
    public GenericMetamodelConfig(Resource metamodelResource, Range<Integer> elementsRange, long seed) {
        super();
        this.metamodelResource = metamodelResource;
        this.elementsRange = elementsRange;
        this.seed = seed;
        this.random = new Random(seed);
    }

    /**
     * Creates a new {@link GenericMetamodelConfig} using
     * {@link System#currentTimeMillis()} as the <code>seed</code>
     * 
     * @param resource
     *            The resource containing the metamodel for which the
     *            {@link SpecimenGenerator} will create instances
     */
    public GenericMetamodelConfig(Resource resource, Range<Integer> elementsRange) {
        this(resource, elementsRange, System.currentTimeMillis());
    }

    /**
     * Return the {@link Resource} of the metamodel for which this
     * {@link ISpecimenConfiguration} will be used
     * 
     * @return the metamodel {@link Resource}
     */
    public Resource getMetamodelResource() {
        return metamodelResource;
    }

    /**
     * Returns the seed used for the {@link Random} generator
     * 
     * @return the seed
     */
    public long getSeed() {
        return seed;
    }

    /**
     * Returns the minimum and maximum size allowed for resources created using
     * this {@link ISpecimenConfiguration}
     * 
     * @return The {@link Range}
     */
    public Range<Integer> getElementsRange() {
        return elementsRange;
    }

    /**
     * Returns the minimum and maximum number of properties allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @return The {@link Range}
     */
    public Range<Integer> getPropertiesRange() {
        return propertiesRange;
    }

    /**
     * Sets the minimum and maximum number of properties allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @param valuesRange
     *            The {@link Range} with minimum and maximum values
     */
    public void setPropertiesRange(Range<Integer> propertiesRange) {
        this.propertiesRange = propertiesRange;
    }

    /**
     * Sets the minimum and maximum number of properties allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @param min
     *            The minimum
     * @param max
     *            The maximum
     */
    public void setPropertiesRange(int min, int max) {
        this.propertiesRange = Range.between(min, max);
    }

    /**
     * Returns the minimum and maximum number of references allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @return The {@link Range}
     */
    public Range<Integer> getReferencesRange() {
        return referencesRange;
    }

    /**
     * Sets the minimum and maximum number of references allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @param valuesRange
     *            The {@link Range} with minimum and maximum values
     */
    public void setReferencesRange(Range<Integer> referencesRange) {
        this.referencesRange = referencesRange;
    }

    /**
     * Sets the minimum and maximum number of references allowed for
     * {@link EObject}s created using this {@link ISpecimenConfiguration}
     * 
     * @param min
     *            The minimum
     * @param max
     *            The maximum
     */
    public void setReferencesRange(int min, int max) {
        this.referencesRange = Range.between(min, max);
    }

    /**
     * Returns the minimum and maximum length of non-fixed-sized properties (e.g.,
     * {@link String}s, arrays, etc).
        
     * @return The {@link Range}
     */
    public Range<Integer> getValuesRange() {
        return valuesRange;
    }

    /**
     * Sets the minimum and maximum length of non-fixed-sized properties (e.g.,
     * {@link String}s, arrays, etc).
     * 
     * @param valuesRange
     *            The {@link Range} with minimum and maximum values
     */
    public void setValuesRange(Range<Integer> valuesRange) {
        this.valuesRange = valuesRange;
    }

    /**
     * Sets the minimum and maximum length of non-fixed-sized properties (e.g.,
     * {@link String}s, arrays, etc).
     * 
     * @param min
     *            The minimum
     * @param max
     *            The maximum
     */
    public void setValuesRange(int min, int max) {
        this.valuesRange = Range.between(min, max);
    }

    @Override
    public ImmutableSet<EPackage> ePackages() {
        Set<EPackage> ePackages = new HashSet<EPackage>();
        for (Iterator<EObject> it = metamodelResource.getAllContents(); it.hasNext();) {
            EObject eObject = (EObject) it.next();
            if (eObject instanceof EPackage) {
                ePackages.add((EPackage) eObject);
            }
        }
        return ImmutableSet.copyOf(ePackages);
    }

    @Override
    public ImmutableSet<EClass> possibleRootEClasses() {
        List<EClass> eClasses = new LinkedList<EClass>();
        // creating a subtypes map
        Map<EClass, Set<EClass>> eSubtypesMap = computeSubtypesMap();

        // Eclasses.filter( instance of EClass && not abstract && not interface) 
        for (Iterator<EObject> it = metamodelResource.getAllContents(); it.hasNext();) {
            EObject eObject = (EObject) it.next();
            if (eObject instanceof EClass) {
                EClass eClass = (EClass) eObject;
                if (!eClass.isAbstract() && !eClass.isInterface()) {
                    eClasses.add(eClass);
                }
            }
        }

        //copying the list of eClasses 
        List<EClass> result = new LinkedList<EClass>(eClasses);
        //      Collections.copy(result , eClasses);

        // iterating eClasses and removing elements (along with subtypes) being 
        // subject to a container reference 
        for (EClass cls : eClasses) {
            for (EReference cont : cls.getEAllContainments()) {
                Set<EClass> list = eSubtypesClosure(eSubtypesMap, (EClass) cont.getEType());
                if (list.size() == 0) {
                    result.remove((EClass) cont.getEType());
                } else {
                    result.removeAll(list);
                }
            }
        }

        return ImmutableSet.copyOf(result);
    }

    @SuppressWarnings("unchecked")
    private Set<EClass> eSubtypesClosure(Map<EClass, Set<EClass>> eSubtypesMap, EClass eType) {
        Set<EClass> result = new LinkedHashSet<EClass>();
        if (!eSubtypesMap.containsKey(eType)) {
            return Collections.EMPTY_SET;
        } else {
            result.addAll(eSubtypesMap.get(eType));
            for (EClass eSubType : eSubtypesMap.get(eType)) {
                if (!eSubType.equals(eType))
                    result.addAll(eSubtypesClosure(eSubtypesMap, eSubType));
            }
        }
        return result;
    }

    private Map<EClass, Set<EClass>> computeSubtypesMap() {
        Map<EClass, Set<EClass>> result = new HashMap<EClass, Set<EClass>>();
        TreeIterator<EObject> iter = metamodelResource.getAllContents();

        for (EObject ecls = null; iter.hasNext();) {
            ecls = iter.next();
            if (ecls instanceof EClass) {
                EClass clazz = (EClass) ecls;
                for (EClass cls : clazz.getEAllSuperTypes()) {
                    if (result.containsKey(cls)) {
                        result.get(cls).add(clazz);
                    } else {
                        Set<EClass> list = new HashSet<EClass>();
                        list.add(cls);
                        list.add(clazz);
                        result.put(cls, list);
                    }
                }
            }
        }

        return result;
    }

    /**
     * Returns whether instances of this {@link EClass} need a container, i.e.,
     * they have a <code>required</code> <code>container</code>
     * {@link EReference} (see {@link EReference#isRequired()} and
     * {@link EReference#isContainer()})
     * 
     * @param eClass
     *            The {@link EClass}
     * @return Whether the {@link EClass} needs a <code>container</code>
     */
    private static boolean needsContainer(EClass eClass) {
        for (EReference eReference : eClass.getEAllReferences()) {
            if (eReference.isContainer() && eReference.isRequired()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public ImmutableSet<EClass> ignoredEClasses() {
        Set<EClass> eClasses = new HashSet<EClass>();
        for (Iterator<EObject> it = metamodelResource.getAllContents(); it.hasNext();) {
            EObject eObject = (EObject) it.next();
            if (eObject instanceof EClass) {
                EClass eClass = (EClass) eObject;
                if (eClass.isAbstract() || eClass.isInterface()) {
                    // Abstract EClasses and Interfaces can't be instantiated
                    eClasses.add(eClass);
                }
            }
        }
        return ImmutableSet.copyOf(eClasses);
    }

    @Override
    public IntegerDistribution getResourceSizeDistribution() {
        IntegerDistribution distribution = distributions.get(metamodelResource);
        if (distribution == null) {
            distribution = new UniformIntegerDistribution(elementsRange.getMinimum(), elementsRange.getMaximum());
            distribution.reseedRandomGenerator(random.nextLong());
            distributions.put(metamodelResource, distribution);
        }
        return distribution;
    }

    @Override
    public int getWeightFor(EReference eReference, EClass eClass) {
        return 1;
    }

    @Override
    public IntegerDistribution getDistributionFor(EAttribute eAttribute) {
        IntegerDistribution distribution = distributions.get(eAttribute);

        if (distribution == null) {
            if (!eAttribute.isMany()) {
                distribution = new UniformIntegerDistribution(propertiesRange.getMinimum(),
                        propertiesRange.getMaximum());
                distribution.reseedRandomGenerator(random.nextLong());
            } else {
                final int upperBound = eAttribute.getUpperBound() == EAttribute.UNBOUNDED_MULTIPLICITY
                        ? Integer.MAX_VALUE
                        : eAttribute.getUpperBound();
                // TODO fix lowerBound when it is equal to 
                int lowerBound = upperBound == eAttribute.getLowerBound() ? 0 : eAttribute.getLowerBound();
                int min = Math.max(Math.min(propertiesRange.getMinimum(), upperBound), lowerBound);
                int max = Math.min(propertiesRange.getMaximum(), upperBound);
                if (min == max) {
                    return new MonoValuedIntegerDistribution(min);
                } else {
                    distribution = new UniformIntegerDistribution(min, max);
                    distribution.reseedRandomGenerator(random.nextLong());
                }
            }
            distributions.put(eAttribute, distribution);
        }
        return distribution;
    }

    @Override
    public IntegerDistribution getValueDistributionFor(Class<?> clazz) {
        IntegerDistribution distribution = distributions.get(clazz);
        if (distribution == null) {
            distribution = new UniformIntegerDistribution(valuesRange.getMinimum(), valuesRange.getMaximum());
            distribution.reseedRandomGenerator(random.nextLong());
            distributions.put(clazz, distribution);
        }
        return distribution;
    }

    @Override
    public IntegerDistribution getDistributionFor(EReference eReference) {
        IntegerDistribution distribution = distributions.get(eReference);

        if (distribution == null) {
            if (!eReference.isMany()) {
                distribution = new UniformIntegerDistribution(propertiesRange.getMinimum(),
                        propertiesRange.getMaximum());
                distribution.reseedRandomGenerator(random.nextLong());
            } else {
                final int upperBound = eReference.getUpperBound() == EAttribute.UNBOUNDED_MULTIPLICITY
                        ? Integer.MAX_VALUE
                        : eReference.getUpperBound();
                // TODO fix lowerBound when it is equal to 
                int lowerBound = upperBound == eReference.getLowerBound() ? 0 : eReference.getLowerBound();
                int min = Math.max(Math.min(propertiesRange.getMinimum(), upperBound), lowerBound);
                int max = Math.min(propertiesRange.getMaximum(), upperBound);
                if (min == max) {
                    return new MonoValuedIntegerDistribution(min);
                } else {
                    distribution = new UniformIntegerDistribution(min, max);
                    distribution.reseedRandomGenerator(random.nextLong());
                }
            }
            distributions.put(eReference, distribution);
        }
        return distribution;
    }

    @Override
    public IntegerDistribution getDepthDistributionFor(EClass eClass) {
        IntegerDistribution distribution = distributions.get(eClass);
        if (distribution == null) {
            distribution = new UniformIntegerDistribution(referencesRange.getMinimum(),
                    referencesRange.getMaximum());
            distribution.reseedRandomGenerator(random.nextLong());
            distributions.put(eClass, distribution);
        }
        return distribution;
    }

    @Override
    public EClass getNextRootEClass(ImmutableSet<EClass> rootEClasses) {
        if (rootEClasses.size() == 1)
            return rootEClasses.asList().get(0);

        IntegerDistribution distribution = distributions.get(rootEClasses);
        if (distribution == null) {
            distribution = new UniformIntegerDistribution(0, rootEClasses.size() - 1);
            distribution.reseedRandomGenerator(random.nextLong());
            distributions.put(rootEClasses, distribution);
        }

        return rootEClasses.asList().get(distribution.sample());
    }

    @SuppressWarnings({ "deprecation", "serial" })
    public class MonoValuedIntegerDistribution extends AbstractIntegerDistribution {

        private int value;

        public MonoValuedIntegerDistribution(int value) {
            this.value = value;
        }

        @Override
        public double probability(int x) {

            throw new UnsupportedOperationException();
        }

        @Override
        public double cumulativeProbability(int x) {
            throw new UnsupportedOperationException();
        }

        @Override
        public double getNumericalMean() {
            throw new UnsupportedOperationException();
        }

        @Override
        public double getNumericalVariance() {
            throw new UnsupportedOperationException();
        }

        @Override
        public int getSupportLowerBound() {
            // TODO Auto-generated method stub
            return value;
        }

        @Override
        public int getSupportUpperBound() {
            // TODO Auto-generated method stub
            return value;
        }

        @Override
        public boolean isSupportConnected() {
            throw new UnsupportedOperationException();
        }

    }
}