it.unibo.alchemist.language.EnvironmentBuilder.java Source code

Java tutorial

Introduction

Here is the source code for it.unibo.alchemist.language.EnvironmentBuilder.java

Source

/*
 * Copyright (C) 2010-2014, Danilo Pianini and contributors
 * listed in the project's pom.xml file.
 * 
 * This file is part of Alchemist, and is distributed under the terms of
 * the GNU General Public License, with a linking exception, as described
 * in the file LICENSE in the Alchemist distribution's top directory.
 */
package it.unibo.alchemist.language;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java8.util.Optional;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.danilopianini.lang.PrimitiveUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import org.apache.commons.math3.random.RandomGenerator;
import it.unibo.alchemist.model.implementations.times.DoubleTime;
import it.unibo.alchemist.model.interfaces.Action;
import it.unibo.alchemist.model.interfaces.Concentration;
import it.unibo.alchemist.model.interfaces.Condition;
import it.unibo.alchemist.model.interfaces.Environment;
import it.unibo.alchemist.model.interfaces.LinkingRule;
import it.unibo.alchemist.model.interfaces.Molecule;
import it.unibo.alchemist.model.interfaces.Position;
import it.unibo.alchemist.model.interfaces.Reaction;
import it.unibo.alchemist.model.interfaces.Time;
import it.unibo.alchemist.model.interfaces.TimeDistribution;

/**
 * @param <T>
 *            concentration type
 */
public final class EnvironmentBuilder<T> {

    private static final Logger L = LoggerFactory.getLogger(EnvironmentBuilder.class);
    private static final String DEFAULT_PACKAGE = "it.unibo.alchemist.";
    private static final String LINKINGRULES_DEFAULT_PACKAGE = DEFAULT_PACKAGE
            + "model.implementations.linkingrules.";
    private static final String NAME = "name";
    private static final String REACTIONS_DEFAULT_PACKAGE = DEFAULT_PACKAGE + "model.implementations.reactions.";
    private static final String TEXT = "#text";
    private static final String TYPE = "type";
    private static final Class<?>[] TYPES = new Class<?>[] { List.class, Integer.TYPE, Double.TYPE, Boolean.TYPE,
            Character.TYPE, Byte.TYPE, Short.TYPE, Long.TYPE, Float.TYPE };

    private Class<?> concentrationClass;
    private final Random internalRandom = new Random();
    private Class<?> positionClass;
    private RandomGenerator random;
    private int seed;
    private Environment<T> result;
    private final InputStream xmlFile;

    /**
     * Builds a new XML interpreter.
     * 
     * @param xmlStream
     *            the input stream to interpret
     */
    private EnvironmentBuilder(final InputStream xmlStream) {
        super();
        xmlFile = xmlStream;
    }

    private Action<T> buildAction(final Node son, final Map<String, Object> env) throws InstantiationException,
            IllegalAccessException, InvocationTargetException, ClassNotFoundException {
        return buildK(son, env, "it.unibo.alchemist.model.implementations.actions.");
    }

    private T buildConcentration(final Node son, final Map<String, Object> subenv)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        if (concentrationClass != null) {
            final String args = son.getNodeValue();
            final ArrayList<String> arguments = new ArrayList<String>(1);
            if (!args.isEmpty()) {
                arguments.add(args);
            }
            final List<Constructor<Concentration<T>>> list = unsafeExtractConstructors(concentrationClass);
            return tryToBuild(list, arguments, subenv, random).getContent();
        }
        L.error("concentration class not yet defined");
        return null;
    }

    private Condition<T> buildCondition(final Node son, final Map<String, Object> env)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        return buildK(son, env, "it.unibo.alchemist.model.implementations.conditions.");
    }

    private TimeDistribution<T> buildTimeDist(final Node son, final Map<String, Object> env)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        return buildK(son, env, "it.unibo.alchemist.model.implementations.timedistributions.");
    }

    private <K> K buildK(final Node son, final Map<String, Object> env, final String defaultPackage)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        final Node typeNode = attributes.getNamedItem(TYPE);
        String type;
        if (typeNode == null) {
            type = "";
        } else {
            type = typeNode.getNodeValue();
            type = type.contains(".") ? type : defaultPackage + type;
        }
        return coreOperations(env, son, type, random);
    }

    /**
     * Actually builds the environment given the AST built in the constructor.
     * 
     * @throws InstantiationException
     *             malformed XML
     * @throws IllegalAccessException
     *             malformed XML
     * @throws InvocationTargetException
     *             malformed XML
     * @throws ClassNotFoundException
     *             your classpath does not include all the classes you are using
     * @throws IOException
     *             if there is an error reading the file
     * @throws SAXException
     *             if the XML is not correctly formatted
     * @throws ParserConfigurationException
     *             should not happen.
     */
    private void buildEnvironment()
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException, SAXException, IOException, ParserConfigurationException {
        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        final DocumentBuilder builder = factory.newDocumentBuilder();
        final Document doc = builder.parse(xmlFile);
        L.debug("Starting processing");
        random = null;
        final Node root = doc.getFirstChild();
        if (root.getNodeName().equals("environment") && doc.getChildNodes().getLength() == 1) {
            final NamedNodeMap atts = root.getAttributes();
            String type = atts.getNamedItem(TYPE).getNodeValue();
            type = type.contains(".") ? type : "it.unibo.alchemist.model.implementations.environments." + type;
            result = coreOperations(new ConcurrentHashMap<String, Object>(), root, type, null);
            synchronized (result) {
                final Node nameNode = atts.getNamedItem(NAME);
                final String name = nameNode == null ? "" : nameNode.getNodeValue();
                final Map<String, Object> env = new ConcurrentHashMap<String, Object>();
                env.put("ENV", result);
                if (!name.equals("")) {
                    env.put(name, result);
                }
                final NodeList children = root.getChildNodes();
                for (int i = 0; i < children.getLength(); i++) {
                    final Node son = children.item(i);
                    final String kind = son.getNodeName();
                    L.debug(kind);
                    if (!kind.equals(TEXT)) {
                        final Node sonNameAttr = son.getAttributes().getNamedItem(NAME);
                        final String sonName = sonNameAttr == null ? "" : sonNameAttr.getNodeValue();
                        Object sonInstance = null;
                        if (kind.equals("molecule")) {
                            sonInstance = buildMolecule(son, env);
                        } else if (kind.equals("concentration")) {
                            if (concentrationClass == null) {
                                setConcentration(son);
                            }
                        } else if (kind.equals("position")) {
                            if (positionClass == null) {
                                setPosition(son);
                            }
                        } else if (kind.equals("random")) {
                            setRandom(son, env);
                        } else if (kind.equals("linkingrule")) {
                            result.setLinkingRule(buildLinkingRule(son, env));
                        } else if (kind.equals("condition")) {
                            sonInstance = buildCondition(son, env);
                        } else if (kind.equals("action")) {
                            sonInstance = buildAction(son, env);
                        } else if (kind.equals("reaction")) {
                            sonInstance = buildReaction(son, env);
                        } else if (kind.equals("node")) {
                            final it.unibo.alchemist.model.interfaces.Node<T> node = buildNode(son, env);
                            final Position pos = buildPosition(son, env);
                            sonInstance = node;
                            result.addNode(node, pos);
                        } else if (kind.equals("time")) {
                            sonInstance = buildTime(son, env);
                        }
                        if (sonInstance != null) {
                            env.put(sonName, sonInstance);
                        }
                    }
                }
                /*
                 * This operation forces a reset to the random generator. It
                 * ensures that if the user reloads the same random seed she
                 * passed in the specification, the simulation will still be
                 * reproducible.
                 */
                random.setSeed(seed);
            }
        } else {
            L.error("XML does not contain one and one only environment.");
        }
    }

    private LinkingRule<T> buildLinkingRule(final Node rootLinkingRule, final Map<String, Object> env)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        final NamedNodeMap attributes = rootLinkingRule.getAttributes();
        // final Node nameNode = attributes.getNamedItem(NAME);
        final String name = "LINKINGRULE";
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : LINKINGRULES_DEFAULT_PACKAGE + type;
        final LinkingRule<T> res = coreOperations(env, rootLinkingRule, type, random);
        env.put(name, res);
        return res;
    }

    private Molecule buildMolecule(final Node son, final Map<String, Object> env) throws InstantiationException,
            IllegalAccessException, InvocationTargetException, ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : "it.unibo.alchemist.model.implementations.molecules." + type;
        return coreOperations(env, son, type, random);
    }

    private it.unibo.alchemist.model.interfaces.Node<T> buildNode(final Node rootNode,
            final Map<String, Object> env) throws InstantiationException, IllegalAccessException,
            InvocationTargetException, ClassNotFoundException {
        final NamedNodeMap attributes = rootNode.getAttributes();
        final Node nameNode = attributes.getNamedItem(NAME);
        final String name = nameNode == null ? "" : nameNode.getNodeValue();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : "it.unibo.alchemist.model.implementations.nodes." + type;
        final it.unibo.alchemist.model.interfaces.Node<T> res = coreOperations(env, rootNode, type, random);
        if (res == null) {
            L.error("Failed to build " + type);
        }
        env.put(name, res);
        env.put("NODE", res);
        final NodeList children = rootNode.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            final Node son = children.item(i);
            final String kind = son.getNodeName();
            if (!kind.equals(TEXT)) {
                final Node sonNameAttr = son.getAttributes().getNamedItem(NAME);
                final String sonName = sonNameAttr == null ? "" : sonNameAttr.getNodeValue();
                String objType = null;
                Object sonInstance = null;
                if (kind.equals("condition")) {
                    final Condition<T> cond = buildCondition(son, env);
                    sonInstance = cond;
                    objType = "CONDITION";
                } else if (kind.equals("action")) {
                    final Action<T> act = buildAction(son, env);
                    sonInstance = act;
                    objType = "ACTION";
                } else if (kind.equals("content")) {
                    final NamedNodeMap moleculesMap = son.getAttributes();
                    for (int j = 0; j < moleculesMap.getLength(); j++) {
                        final Node molNode = moleculesMap.item(j);
                        final String molName = molNode.getNodeName();
                        L.debug("checking molecule " + molName);
                        if (env.containsKey(molName)) {
                            L.debug(molName + " found");
                            final Object molObj = env.get(molName);
                            if (molObj instanceof Molecule) {
                                L.debug(molName + " matches in environment");
                                final Molecule mol = (Molecule) molObj;
                                final T conc = buildConcentration(molNode, env);
                                L.debug(molName + " concentration: " + conc);
                                sonInstance = conc;
                                res.setConcentration(mol, conc);
                            } else {
                                L.warn(molObj + "(class " + molObj.getClass().getCanonicalName()
                                        + " is not subclass of Molecule!");
                            }
                        } else {
                            L.warn("molecule " + molName + " is not yet defined.");
                        }
                    }
                } else if (kind.equals("reaction")) {
                    final Reaction<T> reaction = buildReaction(son, env);
                    res.addReaction(reaction);
                    sonInstance = reaction;
                    objType = "REACTION";
                } else if (kind.equals("time")) {
                    sonInstance = buildTime(son, env);
                } else if (kind.equals("timedistribution")) {
                    sonInstance = buildTimeDist(son, env);
                    objType = "TIMEDIST";
                }
                updateEnv(sonName, objType, sonInstance, env);
            }
        }
        env.remove(name);
        env.remove("NODE");
        return res;
    }

    private void updateEnv(final String name, final String curElem, final Object elem,
            final Map<String, Object> env) {
        if (elem != null) {
            if (name != null) {
                env.put(name, elem);
            }
            if (curElem != null) {
                env.put(curElem, elem);
            }
        }
    }

    private Position buildPosition(final Node son, final Map<String, Object> env)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        if (positionClass == null) {
            L.error("position class not yet defined.");
        } else {
            final NamedNodeMap attributes = son.getAttributes();
            final Node posNode = attributes.getNamedItem("position");
            if (posNode == null) {
                L.warn("a node has no position!");
            } else {
                final String args = posNode.getNodeValue();
                final StringTokenizer tk = new StringTokenizer(args, " ,;");
                final ArrayList<String> arguments = new ArrayList<String>();
                while (tk.hasMoreElements()) {
                    arguments.add(tk.nextToken());
                }
                arguments.trimToSize();
                final List<Constructor<Position>> list = unsafeExtractConstructors(positionClass);
                return tryToBuild(list, arguments, env, random);
            }
        }
        return null;
    }

    private Reaction<T> buildReaction(final Node rootReact, final Map<String, Object> env)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        final NamedNodeMap attributes = rootReact.getAttributes();
        final Node nameNode = attributes.getNamedItem(NAME);
        final String name = nameNode == null ? "" : nameNode.getNodeValue();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : REACTIONS_DEFAULT_PACKAGE + type;
        final Reaction<T> res = coreOperations(env, rootReact, type, random);
        if (!name.equals("")) {
            env.put(name, res);
        }
        env.put("REACTION", res);
        populateReaction(env, res, rootReact);
        return res;
    }

    private Time buildTime(final Node son, final Map<String, Object> env) throws InstantiationException,
            IllegalAccessException, InvocationTargetException, ClassNotFoundException {
        return buildTime(son, env, random);
    }

    private void populateReaction(final Map<String, Object> subenv, final Reaction<T> res, final Node rootReact)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        final NodeList children = rootReact.getChildNodes();
        final ArrayList<Condition<T>> conditions = new ArrayList<Condition<T>>();
        final ArrayList<Action<T>> actions = new ArrayList<Action<T>>();
        for (int i = 0; i < children.getLength(); i++) {
            final Node son = children.item(i);
            final String kind = son.getNodeName();
            if (!kind.equals(TEXT)) {
                final Node sonNameAttr = son.getAttributes().getNamedItem(NAME);
                final String sonName = sonNameAttr == null ? "" : sonNameAttr.getNodeValue();
                Object sonInstance = null;
                if (kind.equals("condition")) {
                    final Condition<T> cond = buildCondition(son, subenv);
                    conditions.add(cond);
                    sonInstance = cond;
                } else if (kind.equals("action")) {
                    final Action<T> act = buildAction(son, subenv);
                    actions.add(act);
                    sonInstance = act;
                }
                if (sonInstance != null) {
                    subenv.put(sonName, sonInstance);
                }
            }
        }
        conditions.trimToSize();
        actions.trimToSize();
        if (!conditions.isEmpty()) {
            res.setConditions(conditions);
        }
        if (!actions.isEmpty()) {
            res.setActions(actions);
        }
    }

    private void setConcentration(final Node son) throws ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : "it.unibo.alchemist.model.implementations.concentrations." + type;
        concentrationClass = Class.forName(type);
        L.debug("Concentration type set to " + concentrationClass);
    }

    private void setPosition(final Node son) throws ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        if (!type.contains(".")) {
            type = "it.unibo.alchemist.model.implementations.positions." + type;
        }
        positionClass = Class.forName(type);
        L.debug("Position type set to " + positionClass);
    }

    private void setRandom(final Node son, final Map<String, Object> env) throws InstantiationException,
            IllegalAccessException, InvocationTargetException, ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        String seed = attributes.getNamedItem("seed").getNodeValue();
        /*
         * This workaround ensures compatibility with pre-PVeStA integration
         * XMLs generated with the SAPERE DSL.
         */
        if (type.equals("cern.jet.random.engine.MersenneTwister")) {
            type = "org.apache.commons.math3.random.MersenneTwister";
        }
        type = type.contains(".") ? type : "org.apache.commons.math3.random." + type;
        seed = seed.equalsIgnoreCase("RANDOM") ? Integer.toString(internalRandom.nextInt()) : seed;
        final List<String> params = new ArrayList<>(1);
        params.add(seed);
        final Class<?> randomEngineClass = Class.forName(type);
        final List<Constructor<RandomGenerator>> consList = unsafeExtractConstructors(randomEngineClass);
        random = tryToBuild(consList, params, env, null);
        this.seed = Integer.parseInt(seed);
    }

    /**
     * Sets a new seed for the random engine. Thread unsafe. Handle with care.
     * 
     * @param seed
     *            the new random engine seed
     */
    public void setRandomGeneratorSeed(final int seed) {
        random.setSeed(seed);
    }

    private static Time buildTime(final Node son, final Map<String, Object> env, final RandomGenerator random)
            throws InstantiationException, IllegalAccessException, InvocationTargetException,
            ClassNotFoundException {
        final NamedNodeMap attributes = son.getAttributes();
        String type = attributes.getNamedItem(TYPE).getNodeValue();
        type = type.contains(".") ? type : "it.unibo.alchemist.model.implementations.times." + type;
        return (Time) coreOperations(env, son, type, random);
    }

    @SuppressWarnings("unchecked")
    private static <E> E coreOperations(final Map<String, Object> environment, final Node root, final String type,
            final RandomGenerator random) throws InstantiationException, IllegalAccessException,
            InvocationTargetException, ClassNotFoundException {
        final NamedNodeMap atts = root.getAttributes();
        final Node nameNode = atts.getNamedItem(NAME);
        final String name = nameNode == null ? "" : nameNode.getNodeValue();
        if (!name.equals("") && atts.getLength() == 1 && environment.containsKey(name)) {
            return (E) environment.get(name);
        }
        final Class<?> objClass = (Class<?>) Class.forName(type);
        final List<Constructor<E>> consList = unsafeExtractConstructors(objClass);
        final ArrayList<String> params = new ArrayList<String>();
        int index = 0;
        for (Node param = atts.getNamedItem("p0"); param != null; param = atts.getNamedItem("p" + (++index))) {
            params.add(param.getNodeValue());
        }
        params.trimToSize();
        final E res = tryToBuild(consList, params, environment, random);
        environment.put(name, res);
        return res;
    }

    private static Optional<Number> extractNumber(final String n) {
        long resl = 0;
        double resd = 0;
        boolean isDouble = false;
        try {
            resl = Long.parseLong(n);
        } catch (final NumberFormatException e) {
            try {
                isDouble = true;
                resd = Double.parseDouble(n);
            } catch (final NumberFormatException nested) {
                return Optional.empty();
            }
        }
        if (isDouble) {
            if (resd % 1 == 0 && resd < Long.MAX_VALUE) {
                return Optional.of((long) resd);
            }
            return Optional.of(resd);
        }
        return Optional.of(resl);
    }

    @SuppressWarnings("unchecked")
    private static Object parseAndCreate(final Class<?> clazz, final String val, final Map<String, Object> env,
            final RandomGenerator random)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        if (clazz.isAssignableFrom(RandomGenerator.class) && val.equalsIgnoreCase("random")) {
            L.debug("Random detected! Class " + clazz.getSimpleName() + ", param: " + val);
            if (random == null) {
                L.error("Random instatiation required, but RandomGenerator not yet defined.");
            }
            return random;
        }
        if (clazz.isPrimitive() || PrimitiveUtils.classIsWrapper(clazz)) {
            L.debug(val + " is a primitive or a wrapper: " + clazz);
            if ((clazz.isAssignableFrom(Boolean.TYPE) || clazz.isAssignableFrom(Boolean.class))
                    && (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"))) {
                return Boolean.parseBoolean(val);
            }
            /*
             * If Number is in clazz's hierarchy
             */
            if (PrimitiveUtils.classIsNumber(clazz)) {
                final Optional<Number> num = extractNumber(val);
                if (num.isPresent()) {
                    final Optional<Number> castNum = PrimitiveUtils.castIfNeeded(clazz, num.get());
                    /*
                     * If method requires Object or unsupported Number, return
                     * what was parsed.
                     */
                    return castNum.orElse(num.get());
                }
            }
            if (Character.TYPE.equals(clazz) || Character.class.equals(clazz)) {
                return val.charAt(0);
            }
        }
        if (List.class.isAssignableFrom(clazz) && val.startsWith("[") && val.endsWith("]")) {
            final List<Constructor<List<?>>> l = unsafeExtractConstructors(clazz);
            @SuppressWarnings("rawtypes")
            final List list = tryToBuild(l, new ArrayList<String>(0), env, random);
            final StringTokenizer strt = new StringTokenizer(val.substring(1, val.length() - 1), ",; ");
            while (strt.hasMoreTokens()) {
                final String sub = strt.nextToken();
                final Object o = tryToParse(sub, env, random);
                if (o == null) {
                    L.debug("WARNING: list elemnt skipped: " + sub);
                } else {
                    list.add(o);
                }
            }
            return list;
        }
        L.debug(val + " is not a primitive: " + clazz + ". Searching it in the environment...");
        final Object o = env.get(val);
        if (o != null && clazz.isInstance(o)) {
            return o;
        }
        if (Time.class.isAssignableFrom(clazz)) {
            return new DoubleTime(Double.parseDouble(val));
        }
        if (clazz.isAssignableFrom(String.class)) {
            L.debug("String detected! Passing " + val + " back.");
            return val;
        }
        L.debug(val + " not found or class not compatible, unable to go further.");
        return null;
    }

    private static <E> E tryToBuild(final List<Constructor<E>> consList, final List<String> params,
            final Map<String, Object> env, final RandomGenerator random)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        for (final Constructor<E> c : consList) {
            L.debug("Trying to build with constructor " + c);
            final Class<?>[] args = c.getParameterTypes();
            if (args.length == params.size()) {
                L.debug("Parameters number matches (" + args.length + ").");
                final Object[] finalArgs = new Object[args.length];
                int i = 0;
                boolean success = true;
                for (; i < args.length && success; i++) {
                    final String paramVal = params.get(i);
                    final Class<?> paramClass = args[i];
                    finalArgs[i] = parseAndCreate(paramClass, paramVal, env, random);
                    if (!paramVal.equals("null") && finalArgs[i] == null) {
                        L.debug("Unable to use this constructor.");
                        success = false;
                    }
                }
                if (success && i == args.length) {
                    final E result = c.newInstance(finalArgs);
                    // L.debug("Created object " + result);
                    return result;
                }
            }
        }
        throw new IllegalArgumentException("no compatible constructor find for " + params);
    }

    private static Object tryToParse(final String val, final Map<String, Object> env, final RandomGenerator random)
            throws InstantiationException, IllegalAccessException, InvocationTargetException {
        for (final Class<?> clazz : TYPES) {
            final Object result = parseAndCreate(clazz, val, env, random);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static <Klazz> List<Constructor<Klazz>> unsafeExtractConstructors(final Class<?> clazz) {
        final Constructor<?>[] constructors = clazz.getConstructors();
        final List<Constructor<Klazz>> list = new ArrayList<Constructor<Klazz>>(constructors.length);
        for (final Constructor<?> c : constructors) {
            list.add((Constructor<Klazz>) c);
        }
        return list;
    }

    private static <T> Future<Result<T>> build(final EnvironmentBuilder<T> builder) {
        final ExecutorService ex = Executors.newSingleThreadExecutor();
        final Future<Result<T>> result = ex.submit(() -> {
            builder.buildEnvironment();
            return Result.build(builder.result, builder.random);
        });
        ex.shutdown();
        return result;
    }

    /**
     * @param xml
     *            the stream to process
     * @param <T>
     *            the concentration type
     * 
     * @return a {@link Future} result containing an {@link Environment}
     */
    public static <T> Future<Result<T>> build(final InputStream xml) {
        return build(new EnvironmentBuilder<>(xml));
    }

    /**
     * @param <T>
     */
    public static final class Result<T> implements Serializable {

        private static final long serialVersionUID = -8733706297447819226L;
        private final Environment<T> env;
        @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "All the random engines provided by Apache are Serializable")
        private final RandomGenerator rng;

        private Result(final Environment<T> environment, final RandomGenerator random) {
            env = environment;
            rng = random;
        }

        /**
         * @return the {@link Environment}
         */
        public Environment<T> getEnvironment() {
            return env;
        }

        /**
         * @return the {@link RandomGenerator} used internally
         */
        public RandomGenerator getRandomGenerator() {
            return rng;
        }

        private static <T> Result<T> build(final Environment<T> environment, final RandomGenerator random) {
            return new Result<>(environment, random);
        }

    }

}