📄 scxmlsemanticsimpl.java
字号:
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.scxml.semantics;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.scxml.Context;
import org.apache.commons.scxml.ErrorReporter;
import org.apache.commons.scxml.Evaluator;
import org.apache.commons.scxml.EventDispatcher;
import org.apache.commons.scxml.NotificationRegistry;
import org.apache.commons.scxml.PathResolver;
import org.apache.commons.scxml.SCInstance;
import org.apache.commons.scxml.SCXMLExpressionException;
import org.apache.commons.scxml.SCXMLHelper;
import org.apache.commons.scxml.SCXMLSemantics;
import org.apache.commons.scxml.Step;
import org.apache.commons.scxml.TriggerEvent;
import org.apache.commons.scxml.invoke.Invoker;
import org.apache.commons.scxml.invoke.InvokerException;
import org.apache.commons.scxml.model.Action;
import org.apache.commons.scxml.model.Finalize;
import org.apache.commons.scxml.model.History;
import org.apache.commons.scxml.model.Initial;
import org.apache.commons.scxml.model.Invoke;
import org.apache.commons.scxml.model.ModelException;
import org.apache.commons.scxml.model.OnEntry;
import org.apache.commons.scxml.model.OnExit;
import org.apache.commons.scxml.model.Parallel;
import org.apache.commons.scxml.model.Param;
import org.apache.commons.scxml.model.Path;
import org.apache.commons.scxml.model.SCXML;
import org.apache.commons.scxml.model.State;
import org.apache.commons.scxml.model.Transition;
import org.apache.commons.scxml.model.TransitionTarget;
/**
* <p>This class encapsulates a particular SCXML semantics, that is, a
* particular semantic interpretation of Harel Statecharts, which aligns
* mostly with W3C SCXML July 5 public draft (that is, UML 1.5). However,
* certain aspects are taken from STATEMATE.</p>
*
* <p>Specific semantics can be created by subclassing this class.</p>
*/
public class SCXMLSemanticsImpl implements SCXMLSemantics, Serializable {
/**
* Serial version UID.
*/
private static final long serialVersionUID = 1L;
/**
* SCXML Logger for the application.
*/
private Log appLog = LogFactory.getLog(SCXMLSemantics.class);
/**
* The TransitionTarget comparator.
*/
private TransitionTargetComparator targetComparator =
new TransitionTargetComparator();
/**
* Current document namespaces are saved under this key in the parent
* state's context.
*/
private static final String NAMESPACES_KEY = "_ALL_NAMESPACES";
/**
* Suffix for error event that are triggered in reaction to invalid data
* model locations.
*/
private static final String ERR_ILLEGAL_ALLOC = ".error.illegalalloc";
/**
* @param input
* SCXML state machine
* @return normalized SCXML state machine, pseudo states are removed, etc.
* @param errRep
* ErrorReporter callback
*/
public SCXML normalizeStateMachine(final SCXML input,
final ErrorReporter errRep) {
//it is a no-op for now
return input;
}
/**
* @param input
* SCXML state machine [in]
* @param targets
* a set of initial targets to populate [out]
* @param entryList
* a list of States and Parallels to enter [out]
* @param errRep
* ErrorReporter callback [inout]
* @param scInstance
* The state chart instance [in]
* @throws ModelException
* in case there is a fatal SCXML object model problem.
*/
public void determineInitialStates(final SCXML input, final Set targets,
final List entryList, final ErrorReporter errRep,
final SCInstance scInstance)
throws ModelException {
TransitionTarget tmp = input.getInitialTarget();
if (tmp == null) {
errRep.onError(ErrorConstants.NO_INITIAL,
"SCXML initialstate is missing!", input);
} else {
targets.add(tmp);
determineTargetStates(targets, errRep, scInstance);
//set of ALL entered states (even if initialState is a jump-over)
Set onEntry = SCXMLHelper.getAncestorClosure(targets, null);
// sort onEntry according state hierarchy
Object[] oen = onEntry.toArray();
onEntry.clear();
Arrays.sort(oen, getTTComparator());
// we need to impose reverse order for the onEntry list
List entering = Arrays.asList(oen);
Collections.reverse(entering);
entryList.addAll(entering);
}
}
/**
* Executes all OnExit/Transition/OnEntry transitional actions.
*
* @param step
* provides EntryList, TransitList, ExitList gets
* updated its AfterStatus/Events
* @param stateMachine
* state machine - SCXML instance
* @param evtDispatcher
* the event dispatcher - EventDispatcher instance
* @param errRep
* error reporter
* @param scInstance
* The state chart instance
* @throws ModelException
* in case there is a fatal SCXML object model problem.
*/
public void executeActions(final Step step, final SCXML stateMachine,
final EventDispatcher evtDispatcher,
final ErrorReporter errRep, final SCInstance scInstance)
throws ModelException {
NotificationRegistry nr = scInstance.getNotificationRegistry();
Collection internalEvents = step.getAfterStatus().getEvents();
Map invokers = scInstance.getInvokers();
// ExecutePhaseActions / OnExit
for (Iterator i = step.getExitList().iterator(); i.hasNext();) {
TransitionTarget tt = (TransitionTarget) i.next();
OnExit oe = tt.getOnExit();
try {
for (Iterator onExitIter = oe.getActions().iterator();
onExitIter.hasNext();) {
((Action) onExitIter.next()).execute(evtDispatcher,
errRep, scInstance, appLog, internalEvents);
}
} catch (SCXMLExpressionException e) {
errRep.onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(),
oe);
}
// check if invoke is active in this state
if (invokers.containsKey(tt)) {
Invoker toCancel = (Invoker) invokers.get(tt);
try {
toCancel.cancel();
} catch (InvokerException ie) {
TriggerEvent te = new TriggerEvent(tt.getId()
+ ".invoke.cancel.failed", TriggerEvent.ERROR_EVENT);
internalEvents.add(te);
}
// done here, don't wait for cancel response
invokers.remove(tt);
}
nr.fireOnExit(tt, tt);
nr.fireOnExit(stateMachine, tt);
TriggerEvent te = new TriggerEvent(tt.getId() + ".exit",
TriggerEvent.CHANGE_EVENT);
internalEvents.add(te);
}
// ExecutePhaseActions / Transitions
for (Iterator i = step.getTransitList().iterator(); i.hasNext();) {
Transition t = (Transition) i.next();
try {
for (Iterator transitIter = t.getActions().iterator();
transitIter.hasNext();) {
((Action) transitIter.next()).execute(evtDispatcher,
errRep, scInstance, appLog, internalEvents);
}
} catch (SCXMLExpressionException e) {
errRep.onError(ErrorConstants.EXPRESSION_ERROR,
e.getMessage(), t);
}
List rtargets = t.getRuntimeTargets();
for (int j = 0; j < rtargets.size(); j++) {
TransitionTarget tt = (TransitionTarget) rtargets.get(j);
nr.fireOnTransition(t, t.getParent(), tt, t);
nr.fireOnTransition(stateMachine, t.getParent(), tt, t);
}
}
// ExecutePhaseActions / OnEntry
for (Iterator i = step.getEntryList().iterator(); i.hasNext();) {
TransitionTarget tt = (TransitionTarget) i.next();
OnEntry oe = tt.getOnEntry();
try {
for (Iterator onEntryIter = oe.getActions().iterator();
onEntryIter.hasNext();) {
((Action) onEntryIter.next()).execute(evtDispatcher,
errRep, scInstance, appLog, internalEvents);
}
} catch (SCXMLExpressionException e) {
errRep.onError(ErrorConstants.EXPRESSION_ERROR, e.getMessage(),
oe);
}
nr.fireOnEntry(tt, tt);
nr.fireOnEntry(stateMachine, tt);
TriggerEvent te = new TriggerEvent(tt.getId() + ".entry",
TriggerEvent.CHANGE_EVENT);
internalEvents.add(te);
// actions in initial transition (if any) and .done events
if (tt instanceof State) {
State ts = (State) tt;
Initial ini = ts.getInitial();
if (ts.isComposite() && ini != null) {
try {
for (Iterator iniIter = ini.getTransition().
getActions().iterator(); iniIter.hasNext();) {
((Action) iniIter.next()).execute(evtDispatcher,
errRep, scInstance, appLog, internalEvents);
}
} catch (SCXMLExpressionException e) {
errRep.onError(ErrorConstants.EXPRESSION_ERROR,
e.getMessage(), ini);
}
}
if (ts.isFinal()) {
State parent = (State) ts.getParent();
String prefix = "";
if (parent != null) {
prefix = parent.getId();
}
te = new TriggerEvent(prefix + ".done",
TriggerEvent.CHANGE_EVENT);
internalEvents.add(te);
if (parent != null) {
scInstance.setDone(parent, true);
}
if (parent != null && parent.isRegion()) {
//3.4 we got a region, which is finalized
//let's check its siblings too
Parallel p = (Parallel) parent.getParent();
int finCount = 0;
int pCount = p.getChildren().size();
for (Iterator regions = p.getChildren().iterator();
regions.hasNext();) {
State reg = (State) regions.next();
if (scInstance.isDone(reg)) {
finCount++;
}
}
if (finCount == pCount) {
te = new TriggerEvent(p.getId() + ".done",
TriggerEvent.CHANGE_EVENT);
internalEvents.add(te);
scInstance.setDone(p, true);
if (stateMachine.isLegacy()) {
te = new TriggerEvent(p.getParent().getId()
+ ".done", TriggerEvent.CHANGE_EVENT);
internalEvents.add(te);
//this is not in the specs, but is makes sense
scInstance.setDone(p.getParentState(), true);
}
}
}
}
}
}
}
/**
* @param stateMachine
* a SM to traverse [in]
* @param step
* with current status and list of transitions to populate
* [inout]
* @param errRep
* ErrorReporter callback [inout]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -