org.cristalise.kernel.lifecycle.instance.stateMachine.StateMachine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cristalise-kernel Show documentation
Show all versions of cristalise-kernel Show documentation
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.
/**
* 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