All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cristalise.kernel.lifecycle.instance.stateMachine.StateMachine Maven / Gradle / Ivy

/**
 * This file is part of the CRISTAL-iSE kernel.
 * Copyright (c) 2001-2015 The CRISTAL Consortium. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * http://www.fsf.org/licensing/licenses/lgpl.html
 */
package org.cristalise.kernel.lifecycle.instance.stateMachine;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.cristalise.kernel.collection.CollectionArrayList;
import org.cristalise.kernel.common.AccessRightsException;
import org.cristalise.kernel.common.InvalidDataException;
import org.cristalise.kernel.common.InvalidTransitionException;
import org.cristalise.kernel.common.ObjectNotFoundException;
import org.cristalise.kernel.lifecycle.instance.Activity;
import org.cristalise.kernel.lookup.AgentPath;
import org.cristalise.kernel.lookup.ItemPath;
import org.cristalise.kernel.process.Gateway;
import org.cristalise.kernel.process.resource.BuiltInResources;
import org.cristalise.kernel.utils.DescriptionObject;
import org.cristalise.kernel.utils.FileStringUtility;
import org.cristalise.kernel.utils.Logger;

public class StateMachine implements DescriptionObject {
    public String   name;
    public Integer  version;
    public ItemPath itemPath;

    private ArrayList                   states;
    private ArrayList              transitions;
    private final HashMap      stateCodes;
    private final HashMap transitionCodes;

    State   initialState;
    int     initialStateCode;
    boolean isCoherent = false;

    public StateMachine() {
        states = new ArrayList();
        transitions = new ArrayList();
        stateCodes = new HashMap();
        transitionCodes = new HashMap();
    }

    public StateMachine(String name, Integer version) {
        this();
        this.name = name;
        this.version = version;
    }

    /**
     * Stores the next State id. -1 means that the value was not initialized yet
     * (e.g. after unmarshall from xml)
     */
    private int nextStateId = -1;

    /**
     * Stores the next Transition id. -1 means that the value was not
     * initialized yet (e.g. after unmarshall from xml)
     */
    private int nextTransId = -1;

    /**
     * Computes the next State id. When loaded from XML, the next id calculated
     * from the existing States
     *
     * @return the next state id
     */
    private int getNextStateId() {
        if (nextStateId == -1) {
            for (State s : states) {
                if (s.id > nextStateId) nextStateId = s.id;
            }
            nextStateId++;
        }
        return nextStateId++;
    }

    /**
     * Computes the next Transition id. When loaded from XML, the next id
     * calculated from the existing Transitions
     *
     * @return the next state id
     */
    private int getNextTransId() {
        if (nextTransId == -1) {
            for (Transition t : transitions) {
                if (t.id > nextTransId) nextTransId = t.id;
            }
            nextTransId++;
        }
        return nextTransId++;
    }

    /**
     * Factory method to create a new State for the given name.
     * It does NOT check whether the name exists or not
     *
     * @param name the name of the State
     * @return the new State
     */
    public State createState(String name) {
        State newState = new State(getNextStateId(), name);
        states.add(newState);
        Logger.msg(5, "StateMachine.createState() - created:" + name + " id:" + newState.id);
        return newState;
    }

    /**
     * Factory method to create a new Transition for the given name.
     * It does NOT check whether the name exists or not
     *
     * @param name  the name of the Transition
     * @return the new Transition
     */
    public Transition createTransition(String name) {
        Transition newTrans = new Transition(getNextTransId(), name);
        transitions.add(newTrans);
        Logger.msg(5, "StateMachine.createTransition() - created:" + name + " id:" + newTrans.id);
        return newTrans;
    }

    public void setStates(ArrayList newStates) {
        this.states = newStates;
        validate();
    }

    public void setTransitions(ArrayList newTransitions) {
        this.transitions = newTransitions;
        validate();
    }

    public boolean validate() {
        stateCodes.clear();
        transitionCodes.clear();
        isCoherent = true;

        Logger.msg(5, "StateMachine.validate() - name:'" + name + "'");

        for (State state : states) {
            Logger.debug(8, "State     : " + state);
            stateCodes.put(state.getId(), state);
        }

        if (stateCodes.containsKey(initialStateCode)) initialState = stateCodes.get(initialStateCode);
        else isCoherent = false;

        for (Transition trans : transitions) {
            Logger.debug(8, "Transition: " + trans);
            transitionCodes.put(trans.getId(), trans);
            isCoherent = isCoherent && trans.resolveStates(stateCodes);
        }
        return isCoherent;
    }

    public ArrayList getStates() {
        return states;
    }

    public ArrayList getTransitions() {
        return transitions;
    }

    public State getInitialState() {
        return initialState;
    }

    public void setInitialState(State initialState) {
        this.initialState = initialState;
        initialStateCode = initialState.getId();
    }

    public int getInitialStateCode() {
        return initialStateCode;
    }

    public void setInitialStateCode(int initialStateCode) {
        this.initialStateCode = initialStateCode;
        initialState = stateCodes.get(initialStateCode);
        if (initialState == null) isCoherent = false;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public Integer getVersion() {
        return version;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setVersion(Integer version) {
        this.version = version;
    }

    @Override
    public ItemPath getItemPath() {
        return itemPath;
    }

    @Override
    public void setItemPath(ItemPath path) {
        itemPath = path;
    }

    @Override
    public String getItemID() {
        if (itemPath == null || itemPath.getUUID() == null) return "";
        return itemPath.getUUID().toString();
    }

    public Transition getTransition(int transitionID) {
        return transitionCodes.get(transitionID);
    }

    public Transition getTransition(String name) {
        for (Transition t : transitions) {
            if (t.getName().equals(name)) return t;
        }
        return null;
    }

    /**
     * Helper method to get transition ID by name
     *
     * @param name the name of the Transition
     * @return the integer ID associated with the Transition name. Returns -1 in
     *         case the name does not exist
     */
    public int getTransitionID(String name) {
        Transition t = getTransition(name);
        return (t == null) ? -1 : t.getId();
    }

    /**
     * Helper method to get transition ID by name
     *
     * @param transName the name of the Transaction
     * @return the ID matching the name
     * @throws InvalidDataException the name was not found
     */
    public int getValidTransitionID(String transName) throws InvalidDataException {
        int id =  getTransitionID(transName);

        if(id == -1)
            throw new InvalidDataException("Transition name '"+transName+"' was not found in StateMachine '"+getName()+"'");
        else
            return id;
    }

    public State getState(int stateID) {
        return stateCodes.get(stateID);
    }

    public State getState(String name) {
        for (State s : states) {
            if (s.getName().equals(name)) return s;
        }
        return null;
    }

    @Override
    public CollectionArrayList makeDescCollections() {
        return new CollectionArrayList();
    }

    public Map getPossibleTransitions(Activity act, AgentPath agent)
            throws ObjectNotFoundException, InvalidDataException
    {
        HashMap returnList = new HashMap();
        State currentState = getState(act.getState());

        for (Transition possTrans: currentState.getPossibleTransitions().values()) {
            try {
                if (possTrans.isEnabled(act)) {
                    returnList.put(possTrans, possTrans.getPerformingRole(act, agent) );
                }
                else Logger.msg(7, "StetMachine.getPossibleTransitions() - DISABLED trans:"+possTrans+" act:"+act.getName());
            }
            catch (AccessRightsException ex) {
                Logger.msg(5, "StetMachine.getPossibleTransitions() - trans:"+possTrans+" not possible for "+agent.getAgentName()+" exception:" + ex.getMessage());
                if (Logger.doLog(8)) Logger.error(ex);
            }
        }
        return returnList;
    }

    public State traverse(Activity act, Transition transition, AgentPath agent)
            throws InvalidTransitionException, AccessRightsException, ObjectNotFoundException, InvalidDataException
    {
        State currentState = getState(act.getState());

        if (transition.originState.equals(currentState)) {
            transition.getPerformingRole(act, agent);
            return transition.targetState;
        }
        else throw new InvalidTransitionException("Transition '" + transition + "' not valid from state '" + currentState.getName() + "'");
    }

    public boolean isCoherent() {
        return isCoherent;
    }

    public int getErrorTransitionIdForState(int id) {
        return getState(id).getErrorTansitionId();
    }

    @Override
    public void export(Writer imports, File dir, boolean shallow) throws IOException, InvalidDataException {
        String smXML;
        String typeCode = BuiltInResources.STATE_MACHINE_RESOURCE.getTypeCode();
        String fileName = getName() + (getVersion() == null ? "" : "_" + getVersion()) + ".xml";

        try {
            smXML = Gateway.getMarshaller().marshall(this);
        }
        catch (Exception e) {
            Logger.error(e);
            throw new InvalidDataException("Couldn't marshall state machine " + getName());
        }

        FileStringUtility.string2File(new File(new File(dir, typeCode), fileName), smXML);

        if (imports == null) return;

        if (Gateway.getProperties().getBoolean("Resource.useOldImportFormat", false)) {
            imports.write("boot/" + typeCode + "/" + fileName
                    + "\n");
        }
        else {
            imports.write("\n");
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy