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

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

Go to download

Cristal-ise is a description-driven software platform originally developed to track the construction of the CMS ECAL detector of the LHC at CERN. This is its core library, known as the kernel, which manages business objects called Items. Items are entirely configured from data, called descriptions, held in other Items. Every change of a state in an Item is a consequence of an execution of an activity in that Item's lifecycle, meaning that Cristal-ise applications are completely traceable, even in their design. It also supports extensive versioning of Item description data, giving the system a high level of flexibility.

There is a newer version: 6.0.0
Show newest version
/**
 * 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