com.synflow.cx.internal.instantiation.properties.InstancePropertiesChecker.java Source code

Java tutorial

Introduction

Here is the source code for com.synflow.cx.internal.instantiation.properties.InstancePropertiesChecker.java

Source

/*******************************************************************************
 * Copyright (c) 2013-2014 Synflow SAS.
 * 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:
 *    Matthieu Wipliez - initial API and implementation and/or initial documentation
 *******************************************************************************/
package com.synflow.cx.internal.instantiation.properties;

import static com.synflow.core.IProperties.PROP_CLOCKS;

import java.util.Iterator;
import java.util.Map.Entry;

import com.google.common.collect.Iterables;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.synflow.models.dpn.DPN;
import com.synflow.models.dpn.Entity;
import com.synflow.models.dpn.Instance;

/**
 * This class defines a properties checker for an instance.
 * 
 * @author Matthieu Wipliez
 * 
 */
public class InstancePropertiesChecker extends PropertiesChecker {

    private static final String NO_CLOCK = "<no clock>";

    public InstancePropertiesChecker(IJsonErrorHandler handler) {
        super(handler);
    }

    /**
     * Checks properties of <code>instance</code>.
     * 
     * @param instance
     *            IR instance
     */
    public void checkProperties(Instance instance) {
        Specializer specializer = new Specializer(handler);
        specializer.visitArguments(instance);

        updateClocksInstantiated(instance);
    }

    /**
     * This method creates a clocks object from the entity's clocks and the instance's clocks.
     * 
     * @param entityClocks
     *            array of entity's clocks
     * @param instanceClocks
     *            array of instance's clocks
     * @return a clocks object
     */
    private JsonObject makeClocksObject(JsonArray entityClocks, JsonArray instanceClocks) {
        JsonObject clocks = new JsonObject();
        if (entityClocks.size() == 0) {
            // when the entity declares no clocks (combinational)
            // we associate no clocks
            return clocks;
        }

        Iterator<JsonElement> it = instanceClocks.iterator();
        for (JsonElement clock : entityClocks) {
            String clockName = clock.getAsString();
            if (it.hasNext()) {
                clocks.add(clockName, it.next());
            } else {
                if (instanceClocks.size() == 1) {
                    // support repetition of one clock
                    clocks.add(clockName, instanceClocks.get(0));
                } else {
                    // not enough clocks given
                    // this is verified and caught by validateClocks
                    // with a !it.hasNext() test.
                    break;
                }
            }
        }

        while (it.hasNext()) {
            // too many clocks given
            // the NO_CLOCK string is not a valid identifier, only used internally
            clocks.add(NO_CLOCK, it.next());
        }

        return clocks;
    }

    /**
     * Checks and transforms the clocks in the properties of the given instance. Clocks accepts an
     * array and an object. If clocks is an array, this method transforms it to an object and checks
     * it.
     * 
     * @param instance
     */
    private void updateClocksInstantiated(Instance instance) {
        DPN dpn = instance.getDPN();
        JsonArray parentClocks = dpn.getProperties().getAsJsonArray(PROP_CLOCKS);

        Entity entity = instance.getEntity();
        JsonArray entityClocks = entity.getProperties().getAsJsonArray(PROP_CLOCKS);

        // use {clock: 'name'} as a shortcut for {clocks: ['name']}
        JsonObject properties = instance.getProperties();
        applyClockShortcut(properties);

        JsonElement clocks = properties.get(PROP_CLOCKS);
        if (clocks == null) {
            clocks = makeClocksObject(entityClocks, parentClocks);
            properties.add(PROP_CLOCKS, clocks);
        } else {
            if (clocks.isJsonArray()) {
                if (checkClockArray(clocks)) {
                    clocks = makeClocksObject(entityClocks, clocks.getAsJsonArray());
                    properties.add(PROP_CLOCKS, clocks);
                } else {
                    // do not check clocks
                    return;
                }
            } else if (!clocks.isJsonObject()) {
                // do not check clocks
                handler.addError(clocks, "clocks must be an array or an object");
                return;
            }
        }

        validateClocks(clocks.getAsJsonObject(), parentClocks, entityClocks);
    }

    /**
     * Validates the given clocks object.
     * 
     * @param clocks
     *            a clocks object
     * @param parentClocks
     *            a list of parent clocks
     * @param entityClocks
     *            a list of entity clocks
     */
    private void validateClocks(JsonObject clocks, JsonArray parentClocks, JsonArray entityClocks) {
        int size = entityClocks.size();
        int got = 0;

        Iterator<JsonElement> it = entityClocks.iterator();
        for (Entry<String, JsonElement> pair : clocks.entrySet()) {
            String clockName = pair.getKey();
            if (NO_CLOCK.equals(clockName)) {
                // no more clocks after this one => mismatch in number of clocks
                got = clocks.entrySet().size();
                break;
            }

            if (!it.hasNext()) {
                // no more entity clocks => mismatch in number of clocks
                break;
            }

            // check we use a valid entity clock name
            String expected = it.next().getAsString();
            if (!clockName.equals(expected)) {
                handler.addError(clocks,
                        "given clock name '" + clockName + "' does not match entity's clock '" + expected + "'");
            }

            // check value
            JsonElement value = pair.getValue();
            if (value.isJsonPrimitive() && value.getAsJsonPrimitive().isString()) {
                got++;
                if (!Iterables.contains(parentClocks, value)) {
                    handler.addError(value, "given clock name '" + value.getAsString()
                            + "' does not appear in parent's clocks " + parentClocks);
                }
            } else {
                handler.addError(value, "invalid clock value: " + value.toString());
            }
        }

        if (got < size) {
            String msg = "not enough clocks given, expected " + size + " clocks, got " + got;
            handler.addError(clocks, msg);
        } else if (got > size) {
            String msg = "too many clocks given, expected " + size + " clocks, got " + got;
            handler.addError(clocks, msg);
        }
    }

}