|
What this is
Other links
The source code// $Id: ParserDisplay.java,v 1.126 2004/10/01 14:01:42 mvw Exp $ // Copyright (c) 1996-2004 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.uml.generator; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.Vector; import org.apache.log4j.Logger; import org.argouml.application.api.Notation; import org.argouml.kernel.Project; import org.argouml.kernel.ProjectManager; import org.argouml.model.ModelFacade; import org.argouml.model.uml.UmlFactory; import org.argouml.model.uml.behavioralelements.activitygraphs.ActivityGraphsFactory; import org.argouml.model.uml.behavioralelements.activitygraphs.ActivityGraphsHelper; import org.argouml.model.uml.behavioralelements.commonbehavior.CommonBehaviorFactory; import org.argouml.model.uml.behavioralelements.statemachines.StateMachinesFactory; import org.argouml.model.uml.foundation.core.CoreFactory; import org.argouml.model.uml.foundation.core.CoreHelper; import org.argouml.model.uml.foundation.datatypes.DataTypesFactory; import org.argouml.model.uml.foundation.extensionmechanisms.ExtensionMechanismsFactory; import org.argouml.model.uml.foundation.extensionmechanisms.ExtensionMechanismsHelper; import org.argouml.model.uml.modelmanagement.ModelManagementHelper; import org.argouml.uml.ProfileJava; import org.argouml.util.MyTokenizer; /** * Interface specifying the operation to take when a PropertySpecialString is * matched. * * @author Michael Stockman * @since 0.11.2 * @see PropertySpecialString */ interface PropertyOperation { /** * Invoked by PropertySpecialString when it has matched a property name. * * @param element * The element on which the property was set. * @param value * The value of the property, may be null if no value was given. */ public void found(Object element, String value); } /** * Declares a string that should take special action when it is found as a * property in {@link ParserDisplay#setProperties ParserDisplay.setProperties}. * *
* The following properties are recognized to have special meaning: frozen. * * * This syntax is compatible with the UML 1.3 spec. * * (formerly: visibility name [multiplicity] : type-expression = * initial-value {property-string} ) (2nd formerly: [visibility] [keywords] * type name [= init] [;] ) * * @param s * The String to parse. * @param attr * The attribute to modify to comply with the instructions in s. * @throws java.text.ParseException * when it detects an error in the attribute string. See also * ParseError.getErrorOffset(). * * @see org.argouml.uml.generator.Parser#parseAttribute(String, Object) */ public void parseAttribute(String s, Object attr) throws ParseException { String multiplicity = null; String name = null; Vector properties = null; String stereotype = null; String token; String type = null; String value = null; String visibility = null; boolean hasColon = false; boolean hasEq = false; int multindex = -1; MyTokenizer st; s = s.trim(); if (s.length() > 0 && VISIBILITYCHARS.indexOf(s.charAt(0)) >= 0) { visibility = s.substring(0, 1); s = s.substring(1); } try { st = new MyTokenizer(s, " ,\t,<<,>>,[,],:,=,{,},\\,", attributeCustomSep); while (st.hasMoreTokens()) { token = st.nextToken(); if (" ".equals(token) || "\t".equals(token) || ",".equals(token)) { if (hasEq) value += token; } else if ("<<".equals(token)) { if (hasEq) { value += token; } else { if (stereotype != null) throw new ParseException( "Attribute cannot have two" + " stereotypes", st .getTokenIndex()); stereotype = ""; while (true) { token = st.nextToken(); if (">>".equals(token)) break; stereotype += token; } } } else if ("[".equals(token)) { if (hasEq) { value += token; } else { if (multiplicity != null) throw new ParseException( "Attribute cannot have two" + " multiplicities", st .getTokenIndex()); multiplicity = ""; multindex = st.getTokenIndex() + 1; while (true) { token = st.nextToken(); if ("]".equals(token)) break; multiplicity += token; } } } else if ("{".equals(token)) { String propname = ""; String propvalue = null; if (properties == null) properties = new Vector(); while (true) { token = st.nextToken(); if (",".equals(token) || "}".equals(token)) { if (propname.length() > 0) { properties.add(propname); properties.add(propvalue); } propname = ""; propvalue = null; if ("}".equals(token)) break; } else if ("=".equals(token)) { if (propvalue != null) throw new ParseException("Property " + propname + " cannot have two values", st .getTokenIndex()); propvalue = ""; } else if (propvalue == null) { propname += token; } else { propvalue += token; } } if (propname.length() > 0) { properties.add(propname); properties.add(propvalue); } } else if (":".equals(token)) { hasColon = true; hasEq = false; } else if ("=".equals(token)) { if (value != null) throw new ParseException("Attribute cannot have two " + "default values", st.getTokenIndex()); value = ""; hasColon = false; hasEq = true; } else { if (hasColon) { if (type != null) throw new ParseException( "Attribute cannot have two" + " types", st .getTokenIndex()); if (token.length() > 0 && (token.charAt(0) == '\"' || token.charAt(0) == '\'')) throw new ParseException("Type cannot be quoted", st.getTokenIndex()); if (token.length() > 0 && token.charAt(0) == '(') throw new ParseException("Type cannot be an " + "expression", st.getTokenIndex()); type = token; } else if (hasEq) { value += token; } else { if (name != null && visibility != null) throw new ParseException("Extra text in Attribute", st.getTokenIndex()); if (token.length() > 0 && (token.charAt(0) == '\"' || token.charAt(0) == '\'')) throw new ParseException( "Name or visibility cannot" + " be quoted", st.getTokenIndex()); if (token.length() > 0 && token.charAt(0) == '(') throw new ParseException( "Name or visibility cannot" + " be an expression", st .getTokenIndex()); if (name == null && visibility == null && token.length() > 1 && VISIBILITYCHARS.indexOf(token.charAt(0)) >= 0) { visibility = token.substring(0, 1); token = token.substring(1); } if (name != null) { visibility = name; name = token; } else { name = token; } } } } } catch (NoSuchElementException nsee) { throw new ParseException("Unexpected end of attribute", s.length()); } catch (ParseException pre) { throw pre; } LOG.debug("ParseAttribute [name: " + name + " visibility: " + visibility + " type: " + type + " value: " + value + " stereo: " + stereotype + " mult: " + multiplicity); if (properties != null) { for (int i = 0; i + 1 < properties.size(); i += 2) { LOG.debug("\tProperty [name: " + properties.get(i) + " = " + properties.get(i + 1) + "]"); } } if (visibility != null) { short vis = getVisibility(visibility.trim()); ModelFacade.setVisibility(attr, vis); } if (name != null) ModelFacade.setName(attr, name.trim()); else if (ModelFacade.getName(attr) == null || "".equals(ModelFacade.getName(attr))) ModelFacade.setName(attr, "anonymous"); if (type != null) { Object ow = ModelFacade.getOwner(attr); Object ns = null; if (ow != null && ModelFacade.getNamespace(ow) != null) ns = ModelFacade.getNamespace(ow); else ns = ModelFacade.getModel(attr); ModelFacade.setType(attr, getType(type.trim(), ns)); } if (value != null) { Object initExpr = UmlFactory.getFactory().getDataTypes() .createExpression(Notation.getDefaultNotation().toString(), value.trim()); ModelFacade.setInitialValue(attr, initExpr); } if (multiplicity != null) { try { ModelFacade .setMultiplicity(attr, UmlFactory.getFactory() .getDataTypes().createMultiplicity( multiplicity.trim())); } catch (IllegalArgumentException iae) { throw new ParseException("Bad multiplicity (" + iae + ")", multindex); } } if (properties != null) setProperties(attr, properties, attributeSpecialStrings); if (stereotype != null) { stereotype = stereotype.trim(); Object stereo = getStereotype(attr, stereotype); if (stereo != null) ModelFacade.setStereotype(attr, stereo); else if ("".equals(stereotype)) ModelFacade.setStereotype(attr, null); } } /** * Finds the classifier associated with the type named in name. * * @param name * The name of the type to get. * @param defaultSpace * The default name-space to place the type in. * @return The classifier associated with the name. */ private Object getType(String name, Object defaultSpace) { Object type = null; Project p = ProjectManager.getManager().getCurrentProject(); // Should we be getting this from the GUI? BT 11 aug 2002 type = p.findType(name, false); if (type == null) { // no type defined yet type = UmlFactory.getFactory().getCore().buildClass(name, defaultSpace); } if (ModelFacade.getModel(type) != p.getModel() && !ModelManagementHelper.getHelper().getAllNamespaces( p.getModel()).contains(ModelFacade.getNamespace(type))) { type = ModelManagementHelper.getHelper().getCorrespondingElement( type, ModelFacade.getModel(defaultSpace)); } return type; } /** * Finds a visibility for the visibility specified by name. If no known * visibility can be deduced, private visibility is used. * * @param name * The Java name of the visibility. * @return A visibility corresponding to name. */ private short getVisibility(String name) { if ("+".equals(name) || "public".equals(name)) return ModelFacade.ACC_PUBLIC; else if ("#".equals(name) || "protected".equals(name)) return ModelFacade.ACC_PROTECTED; else /* if ("-".equals(name) || "private".equals(name)) */ return ModelFacade.ACC_PRIVATE; } /** * Applies a Vector of name value pairs of properties to a model element. * The name is treated as the tag of a tagged value unless it is one of the * PropertySpecialStrings, in which case the action of the * PropertySpecialString is invoked. * * @param elem * An model element to apply the properties to. * @param prop * A Vector with name, value pairs of properties. * @param spec * An array of PropertySpecialStrings to use. */ private void setProperties(Object elem, Vector prop, PropertySpecialString spec[]) { String name; String value; int i, j; nextProp: for (i = 0; i + 1 < prop.size(); i += 2) { name = (String) prop.get(i); value = (String) prop.get(i + 1); if (name == null) continue; name = name.trim(); if (value != null) value = value.trim(); for (j = i + 2; j < prop.size(); j += 2) { String s = (String) prop.get(j); if (s != null && name.equalsIgnoreCase(s.trim())) continue nextProp; } if (spec != null) { for (j = 0; j < spec.length; j++) if (spec[j].invoke(elem, name, value)) continue nextProp; } ModelFacade.setTaggedValue(elem, name, value); } } /** * Recursively search a hive of a model for a stereotype with the name given * in name. * * @param obj * The model element to be suitable for. * @param root * The model element to search from. * @param name * The name of the stereotype to search for. * @return An stereotype named name, or null if none is found. */ private Object recFindStereotype(Object obj, Object root, String name) { Object stereo; if (root == null) return null; if (ModelFacade.isAStereotype(root) && name.equals(ModelFacade.getName(root))) { if (ExtensionMechanismsHelper.getHelper().isValidStereoType(obj, /* (MStereotype) */root)) return root; else LOG .debug("Missed stereotype " + ModelFacade.getBaseClass(root)); } if (!ModelFacade.isANamespace(root)) return null; Collection ownedElements = ModelFacade.getOwnedElements(root); if (ownedElements == null) { return null; } // Loop through each element in the namespace, recursing. Iterator iter = ownedElements.iterator(); while (iter.hasNext()) { stereo = recFindStereotype(obj, iter.next(), name); if (stereo != null) return stereo; } return null; } /** * Finds a stereotype named name either in the subtree of the model rooted * at root, or in the the ProfileJava model. * *
* TODO: Should create the stereotype under root if it isn't found.
*
* @param obj
* A MModelElements to find a suitable stereotype for.
* @param name
* The name of the stereotype to search for.
* @return A stereotype named name, or possibly null.
*/
private Object getStereotype(Object obj, String name) {
Object root = ModelFacade.getModel(obj);
Object stereo;
stereo = recFindStereotype(obj, root, name);
if (stereo != null)
return stereo;
stereo = recFindStereotype(obj, ProfileJava.getInstance()
.getProfileModel(), name);
if (stereo != null)
return ModelManagementHelper.getHelper().getCorrespondingElement(
stereo, root);
if (root != null && name.length() > 0) {
stereo = ExtensionMechanismsFactory.getFactory().buildStereotype(
obj, name, root);
}
return stereo;
}
/**
* Parse user input for state bodies and assign the individual lines to
* according actions or transistions. The user input consists of multiple
* lines like: action-label / action-expression or the format of a regular
* transition - see parseTransition(). "action-label" stands for one of
* "entry", "do" and "exit". The words "entry", "do" and "exit" are
* case-independent.
*
* @param st
* The State object.
* @param s
* The string to parse.
*/
public void parseStateBody(Object st, String s) {
boolean foundEntry = false;
boolean foundExit = false;
boolean foundDo = false;
Collection transitionList = new ArrayList();
java.util.StringTokenizer lines = new java.util.StringTokenizer(s,
"\n\r");
while (lines.hasMoreTokens()) {
String line = lines.nextToken().trim();
if (line.toLowerCase().startsWith("entry")) {
parseStateEntryAction(st, line);
foundEntry = true;
} else if (line.toLowerCase().startsWith("exit")) {
parseStateExitAction(st, line);
foundExit = true;
} else if (line.toLowerCase().startsWith("do")) {
parseStateDoAction(st, line);
foundDo = true;
} else {
Object t = UmlFactory.getFactory().getStateMachines()
.createTransition();
//.buildInternalTransition(st);
/*
* TODO: Why does buildInternalTransition() not work? (MVW) If I
* try, then the Target and Source get reset to null before they
* are displayed...
*/
if (t == null)
continue;
ModelFacade.setTarget(t, st);
ModelFacade.setSource(t, st);
parseTransition(t, line);
//LOG.debug("just parsed:" + GeneratorDisplay.Generate(t));
transitionList.add(t);
}
}
if (!foundEntry) {
delete(ModelFacade.getEntry(st));
}
if (!foundExit) {
delete(ModelFacade.getExit(st));
}
if (!foundDo) {
delete(ModelFacade.getDoActivity(st));
}
Vector internals = new Vector(ModelFacade.getInternalTransitions(st));
Vector oldinternals = new Vector(ModelFacade
.getInternalTransitions(st));
internals.removeAll(oldinternals); //now the vector is empty
// don't forget to remove old internals!
for (int i = 0; i < oldinternals.size(); i++)
UmlFactory.getFactory().delete(
/* (MTransition) */oldinternals.elementAt(i));
internals.addAll(transitionList);
ModelFacade.setInternalTransitions(st, transitionList);
}
/**
* Parse a line of the form: "entry /action" and create an action. The word
* "entry" is case-independent.
*
* @param st
* the state object
* @param s
* the string to be parsed
*/
public void parseStateEntryAction(Object st, String s) {
if (s.toLowerCase().startsWith("entry") && s.indexOf("/") > -1)
s = s.substring(s.indexOf("/") + 1).trim();
Object oldEntry = ModelFacade.getEntry(st);
if (oldEntry == null) {
ModelFacade.setEntry(st, buildNewCallAction(s));
} else {
updateAction(oldEntry, s);
}
}
/**
* Parse a line of the form: "exit /action" and create an action. The word
* "exit" is case-independent.
*
* @param st
* the state object
* @param s
* the string to be parsed
*/
public void parseStateExitAction(Object st, String s) {
if (s.toLowerCase().startsWith("exit") && s.indexOf("/") > -1)
s = s.substring(s.indexOf("/") + 1).trim();
Object oldExit = ModelFacade.getExit(st);
if (oldExit == null) {
ModelFacade.setExit(st, buildNewCallAction(s));
} else {
updateAction(oldExit, s);
}
}
/**
* Parse a line of the form: "do /action" and create an action. The word
* "do" is case-independent.
*
* @param st
* the state object
* @param s
* the string to be parsed
*/
public void parseStateDoAction(Object st, String s) {
if (s.toLowerCase().startsWith("do") && s.indexOf("/") > -1)
s = s.substring(s.indexOf("/") + 1).trim();
Object oldDo = ModelFacade.getDoActivity(st);
if (oldDo == null) {
ModelFacade.setDoActivity(st, buildNewCallAction(s));
} else {
updateAction(oldDo, s);
}
}
/**
* Parse a transition description line of the form: "event-signature
* [guard-condition] / action-expression" If the last character of this line
* is a ";", then it is ignored. The "event-signature" may be one of the 4
* formats:
* ChangeEvent: "when(condition)"
* TimeEvent: "after(duration)"
* CallEvent: "a(parameter-list)".
* SignalEvent: TODO: Add support for signal events.
*
* @param trans
* The transition object to which this string applies.
* @param s
* The string to be parsed.
* @return the transition object
*
* @see org.argouml.uml.generator.Parser#parseTransition(Object, String)
*/
public Object parseTransition(Object trans, String s) {
s = s.trim();
StringTokenizer tokenizer = new StringTokenizer(s, "[/");
String eventSignature = null;
String guardCondition = null;
String actionExpression = null;
while (tokenizer.hasMoreTokens()) {
String nextToken = tokenizer.nextToken();
if (nextToken.endsWith("]")) {
guardCondition = nextToken.substring(0, nextToken.length() - 1);
} else {
if (s.startsWith(nextToken)) {
eventSignature = nextToken;
}
else
if (s.endsWith(nextToken)) {
actionExpression = nextToken;
}
}
}
if (eventSignature != null) {
try {
parseEventSignature(trans, eventSignature);
} catch (ParseException e) {
LOG.warn(e);
}
}
if (guardCondition != null) {
Object guard = parseGuard(guardCondition.substring(guardCondition
.indexOf('[') + 1));
ModelFacade.setGuard(trans, guard);
}
if (actionExpression != null) {
Object action = parseAction(actionExpression.trim());
ModelFacade.setEffect(trans, action);
}
return trans;
// //DO NOT DELETE!
// //MVW: I still need this to help me create
// //the functionality that is now missing!
//
// // strip any trailing semi-colons
// s = s.trim();
// while (s.length() > 0 && s.charAt(s.length() - 1) == ';')
// s = s.substring(0, s.length() - 1).trim();
//
// // strip off the name, and the ":"
// String name = "";
// String trigger = "";
// String guard = "";
// String actions = "";
// if (s.indexOf(":", 0) > -1) {
// String s1 = s.substring(0, s.indexOf(":"));
// // the name may not contain a "(", for the case of: a(b:c)
// // the name may not contain a "/",
// // for when the action contains a ":"
// if ((s1.indexOf("(") < 0) && (s1.indexOf("/") < 0)) {
// name = s1.trim();
// s = s.substring(s.indexOf(":") + 1).trim();
// }
// }
//
// // get the guard from between the []
// if (s.indexOf("[", 0) > -1 && s.indexOf("]", 0) > -1) {
// guard = s.substring(s.indexOf("[", 0) + 1,
// s.indexOf("]")).trim();
// s = s.substring(0, s.indexOf("["))
// + s.substring(s.indexOf("]") + 1);
// s = s.trim();
// }
//
// // everything behind the "/" is the action
// if (s.indexOf("/", 0) > -1) {
// actions = s.substring(s.indexOf("/") + 1).trim();
// s = s.substring(0, s.indexOf("/")).trim();
// }
//
// // and the remainder is the trigger name
// trigger = s;
//
// // let's look for a TimeEvent, ChangeEvent,
// // CallEvent or SignalEvent
// boolean timeEvent = false;
// boolean changeEvent = false;
// boolean callEvent = false;
// boolean signalEvent = false;
// String operationName = "";
// if ((s.toLowerCase().startsWith("after"))
// && (s.indexOf("(", 0) > -1)
// && (s.indexOf(")", 0) > -1))
// { //s shall contain the TimeExpression
// s = s.substring(s.indexOf("(") + 1).trim();
// s = s.substring(0, s.lastIndexOf(")")).trim();
// timeEvent = true;
// } else if ((s.toLowerCase().startsWith("when"))
// && (s.indexOf("(", 0) > -1)
// && (s.indexOf(")", 0) > -1))
// { // s shall contain the ChangeExpression
// s = s.substring(s.indexOf("(") + 1).trim();
// s = s.substring(0, s.lastIndexOf(")")).trim();
// changeEvent = true;
// } else if ((s.indexOf("(", 0) > -1)
// && (s.indexOf(")", 0) > -1))
// { // s shall contain the operation
// operationName = s.substring(0, s.indexOf("(")).trim();
// callEvent = true;
// } else { // s shall contain the signal
// signalEvent = true;
// }
//
// LOG.debug("name=|" + name + "|");
// LOG.debug("trigger=|" + trigger + "|");
// LOG.debug("guard=|" + guard + "|");
// LOG.debug("actions=|" + actions + "|");
//
// // use the name we found to (re)name the transition
// ModelFacade.setName(trans, name);
//
//// /* The following handles the Event that is the trigger of
//// this transition.
//// We can distinct between 4 cases:
//// 1. A trigger is given. None exists yet.
//// 2. The name of the trigger was present, but is (the same or)
// altered.
//// 3. A trigger is not given. None exists yet.
//// 4. The name of the trigger was present, but is removed.
//// The reaction in these cases should be:
//// 1. Create a new trigger, name it, and hook it to the transition.
//// 2. Rename the trigger.
//// 3. Nop.
//// 4. Unhook and erase the existing trigger.
////
//// TODO:
//// In fact it could be made even more complicated for case 1:
//// If the transition did not have a trigger before,
//// a trigger-name is given, and a trigger already
//// existed with that name,
//// then we have to use the existing trigger object!
//// */
// Object evt = ModelFacade.getTrigger(trans);
// boolean created_evt = false;
// if (trigger.length() > 0) {
// // case 1 and 2
// if (evt == null) {
// // case 1
// if (timeEvent) { // after(...)
// evt = UmlFactory.getFactory().getStateMachines()
// .buildTimeEvent(s);
// }
// if (changeEvent) { // when(...)
// evt = UmlFactory.getFactory().getStateMachines()
// .buildChangeEvent(s);
// }
// if (callEvent) { // operation(paramlist)
// evt = UmlFactory.getFactory().getStateMachines()
// .buildCallEvent(trans, trigger);
// }
// if (signalEvent) { // signalname
// evt = UmlFactory.getFactory().getStateMachines()
// .buildSignalEvent(trigger);
// }
// created_evt = true;
// } else {
// // case 2
// if (!ModelFacade.getName(evt).equals(trigger)) {
// ModelFacade.setName(evt, trigger);
// if (timeEvent && !ModelFacade.isATimeEvent(evt)) {
// UmlFactory.getFactory().delete(evt);
// evt = UmlFactory.getFactory()
// .getStateMachines().buildTimeEvent(s);
// created_evt = true;
// }
// if (changeEvent && !ModelFacade
// .isAChangeEvent(evt)) {
// UmlFactory.getFactory().delete(evt);
// evt = UmlFactory.getFactory()
// .getStateMachines().buildChangeEvent(s);
// created_evt = true;
// }
// if (callEvent && !ModelFacade.isACallEvent(evt)) {
// UmlFactory.getFactory().delete(evt);
// evt = UmlFactory.getFactory()
// .getStateMachines()
// .buildCallEvent(trans, trigger);
// created_evt = true;
// }
// if (signalEvent && !ModelFacade
// .isASignalEvent(evt)) {
// UmlFactory.getFactory().delete(evt);
// evt = UmlFactory.getFactory()
// .getStateMachines()
// .buildSignalEvent(trigger);
// created_evt = true;
// }
// }
// }
// if (created_evt && (evt != null)) {
// StateMachinesHelper.getHelper()
// .setEventAsTrigger(trans, evt);
//
// /* The next part is explained by the following
// * quote from the UML spec:
// * "The event declaration has scope within
// * the package it appears in and may be used in
// * state diagrams for classes that have visibility
// * inside the package. An event is not local to
// * a single class."
// */
// Object enclosing = StateMachinesHelper.getHelper()
// .getStateMachine(trans);
// while ((!ModelFacade.isAPackage(enclosing))
// && (enclosing != null)) {
// enclosing = ModelFacade.getNamespace(enclosing);
// }
// if (enclosing != null) {
// ModelFacade.setNamespace(evt, enclosing);
// }
// }
// } else {
// // case 3 and 4
// if (evt == null) {
// ;// case 3
// } else {
// // case 4
// UmlFactory.getFactory().delete(evt); // erase it
// }
// }
//
// /* handle the Guard
// We can distinct between 4 cases:
// 1. A guard is given. None exists yet.
// 2. The expression of the guard was present, but is altered.
// 3. A guard is not given. None exists yet.
// 4. The expression of the guard was present, but is removed.
// The reaction in these cases should be:
// 1. Create a new guard, set its name, language & expression,
// and hook it to the transition.
// 2. Change the guard's expression. Leave the name & language
// untouched.
// 3. Nop.
// 4. Unhook and erase the existing guard.
// */
// Object g = ModelFacade.getGuard(trans);
// if (guard.length() > 0) {
// if (g == null) {
// // case 1
// /*TODO: In the next line, I should use buildGuard(),
// * but it doesn't show the guard on the diagram...
// * Why? (MVW)*/
// g = UmlFactory.getFactory().getStateMachines()
// .createGuard();
// if (g != null) {
// ModelFacade.setExpression(g, UmlFactory.getFactory()
// .getDataTypes().createBooleanExpression(
// "Java", guard));
// ModelFacade.setName(g, "anon");
// ModelFacade.setTransition(g, trans);
// ModelFacade.setGuard(trans, g);
// }
// } else {
// // case 2
//
// /* TODO: This does not work! Why not? (MVW)
// Object expr = ModelFacade.getExpression(g);
// ModelFacade.setBody(expr,guard);
// ModelFacade.setExpression(g,expr); */
//
// //hence a less elegant workaround that works:
// String language = ModelFacade.getLanguage(
// ModelFacade.getExpression(g));
// ModelFacade.setExpression(g, UmlFactory.getFactory()
// .getDataTypes().createBooleanExpression(
// language, guard));
// /* TODO: In this case, the properties panel
// is not updated
// with the changed expression! */
// }
// } else {
// if (g == null) {
// ;// case 3
// } else {
// // case 4
// UmlFactory.getFactory().delete(g); // erase it
// }
// }
//
// /* handle the Effect (Action)
// We can distinct between 4 cases:
// 1. An effect is given. None exists yet.
// 2. The expression of the effect was present, but is altered.
// 3. An effct is not given. None exists yet.
// 4. The expression of the effect was present, but is removed.
// The reaction in these cases should be:
// 1. Create a new CallAction, set its name, language &
// expression, and hook it to the transition.
// 2. Change the effect's expression. Leave the actiontype, name
// & language untouched.
// 3. Nop.
// 4. Unhook and erase the existing effect.
// */
// Object effect = ModelFacade.getEffect(trans);
// if (actions.length() > 0) {
// if (effect == null) { // case 1
// effect = UmlFactory.getFactory().getCommonBehavior()
// .createCallAction();
// ModelFacade.setScript(effect, UmlFactory.getFactory()
// .getDataTypes().createActionExpression(
// "Java", actions));
// ModelFacade.setName(effect, "anon");
// ModelFacade.setEffect(trans, effect);
// } else { // case 2
// String language = ModelFacade.getLanguage(ModelFacade
// .getScript(effect));
// ModelFacade.setScript(effect, UmlFactory.getFactory()
// .getDataTypes().createActionExpression(
// language, actions));
// }
// } else { // case 3 & 4
// if (effect == null) {
// ;// case 3
// } else {
// // case 4
// UmlFactory.getFactory().delete(effect); // erase it
// }
// }
//
// return trans;
}
/**
* Parses a line on the form:
*
* * role and baselist can be given in any order. * *
* This syntax is compatible with the UML 1.3 specification.
*
* (formerly: "name: base" )
*
* @param cls
* The classifier role to apply any changes to.
* @param s
* The String to parse.
* @throws java.text.ParseException
* when it detects an error in the attribute string. See also
* ParseError.getErrorOffset().
*/
public void parseClassifierRole(Object cls, String s)
throws ParseException {
String name = null;
String token;
String role = null;
String base = null;
Vector bases = null;
boolean hasColon = false;
boolean hasSlash = false;
try {
MyTokenizer st = new MyTokenizer(s, " ,\t,/,:,\\,");
while (st.hasMoreTokens()) {
token = st.nextToken();
if (" ".equals(token) || "\t".equals(token)) {
;// Do nothing
} else if ("/".equals(token)) {
hasSlash = true;
hasColon = false;
if (base != null) {
if (bases == null)
bases = new Vector();
bases.add(base);
}
base = null;
} else if (":".equals(token)) {
hasColon = true;
hasSlash = false;
if (bases == null)
bases = new Vector();
if (base != null)
bases.add(base);
base = null;
} else if (",".equals(token)) {
if (base != null) {
if (bases == null)
bases = new Vector();
bases.add(base);
}
base = null;
} else if (hasColon) {
if (base != null)
throw new ParseException(
"Extra text in Classifier Role", st
.getTokenIndex());
base = token;
} else if (hasSlash) {
if (role != null)
throw new ParseException(
"Extra text in Classifier Role", st
.getTokenIndex());
role = token;
} else {
if (name != null)
throw new ParseException(
"Extra text in Classifier Role", st
.getTokenIndex());
name = token;
}
}
} catch (NoSuchElementException nsee) {
throw new ParseException("Unexpected end of attribute", s.length());
} catch (ParseException pre) {
LOG.error("parseexception", pre);
throw pre;
}
if (base != null) {
if (bases == null)
bases = new Vector();
bases.add(base);
}
// TODO: What to do about object name???
// if (name != null)
// ;
if (role != null)
ModelFacade.setName(cls, role.trim());
if (bases != null) {
// Remove bases that aren't there anymore
Collection b = ModelFacade.getBases(cls);
Iterator it = b.iterator();
Object c;
Object ns = ModelFacade.getNamespace(cls);
if (ns != null && ModelFacade.getNamespace(ns) != null)
ns = ModelFacade.getNamespace(ns);
else
ns = ModelFacade.getModel(cls);
while (it.hasNext()) {
c = it.next();
if (!bases.contains(ModelFacade.getName(c)))
ModelFacade.removeBase(cls, c);
}
it = bases.iterator();
addBases:
while (it.hasNext()) {
String d = ((String) it.next()).trim();
Iterator it2 = b.iterator();
while (it2.hasNext()) {
c = it2.next();
if (d.equals(ModelFacade.getName(c)))
continue addBases;
}
c = getType(d, ns);
if (ModelFacade.isACollaboration(ModelFacade.getNamespace(c)))
ModelFacade.setNamespace(c, ns);
ModelFacade.addBase(cls, c);
}
}
}
/**
* TODO: - This method is too complex, lets break it up. Parses a message
* line on the form:
*
*
* Which is rather complex, so a few examples: * This syntax is compatible with the UML 1.3 specification. * * * Actually, only a subset of this syntax is currently supported, and some * is not even planned to be supported. The exceptions are intno, which * allows a number possibly followed by a sequence of letters in the range * 'a' - 'z', seqelem, which does not allow a recurrance, and message, which * does allow one recurrance near seq2. (formerly: name: action ) * * @param mes * The MMessage to apply any changes to. * @param s * The String to parse. * @throws ParseException * when it detects an error in the attribute string. See also * ParseError.getErrorOffset(). */ public void parseMessage(Object mes, String s) throws ParseException { String fname = null; String guard = null; String paramExpr = null; String token; String varname = null; Vector predecessors = new Vector(); Vector seqno = null; Vector currentseq = new Vector(); Vector args = null; boolean mustBePre = false; boolean mustBeSeq = false; boolean parallell = false; boolean iterative = false; boolean mayDeleteExpr = false; boolean refindOperation = false; boolean hasPredecessors = false; int i; currentseq.add(null); currentseq.add(null); try { MyTokenizer st = new MyTokenizer(s, " ,\t,*,[,],.,:,=,/,\\,", MyTokenizer.PAREN_EXPR_STRING_SEPARATOR); while (st.hasMoreTokens()) { token = st.nextToken(); if (" ".equals(token) || "\t".equals(token)) { if (currentseq == null) { if (varname != null && fname == null) { varname += token; } } } else if ("[".equals(token)) { if (mustBePre) throw new ParseException("Predecessors cannot be " + "qualified", st.getTokenIndex()); mustBeSeq = true; if (guard != null) throw new ParseException("Messages cannot have several" + " guard or iteration specifications", st .getTokenIndex()); guard = ""; while (true) { token = st.nextToken(); if ("]".equals(token)) break; guard += token; } } else if ("*".equals(token)) { if (mustBePre) throw new ParseException("Predecessors cannot be " + "iterated", st.getTokenIndex()); mustBeSeq = true; if (currentseq != null) iterative = true; } else if (".".equals(token)) { if (currentseq == null) throw new ParseException("Unexpected dot ('.')", st .getTokenIndex()); if (currentseq.get(currentseq.size() - 2) != null || currentseq.get(currentseq.size() - 1) != null) { currentseq.add(null); currentseq.add(null); } } else if (":".equals(token)) { if (st.hasMoreTokens()) { String t = st.nextToken(); if ("=".equals(t)) { st.putToken(":="); continue; } st.putToken(t); } if (mustBePre) throw new ParseException("Predecessors must be " + "terminated with \'/\' and not with \':\'", st.getTokenIndex()); if (currentseq != null) { if (currentseq.size() > 2 && currentseq.get(currentseq.size() - 2) == null && currentseq.get(currentseq.size() - 1) == null) { currentseq.remove(currentseq.size() - 1); currentseq.remove(currentseq.size() - 1); } seqno = currentseq; currentseq = null; mayDeleteExpr = true; } } else if ("/".equals(token)) { if (st.hasMoreTokens()) { String t = st.nextToken(); if ("/".equals(t)) { st.putToken("//"); continue; } st.putToken(t); } if (mustBeSeq) { throw new ParseException("The sequence number must be " + "terminated with \':\' and not with \'/\'", st.getTokenIndex()); } mustBePre = false; mustBeSeq = true; if (currentseq.size() > 2 && currentseq.get(currentseq.size() - 2) == null && currentseq.get(currentseq.size() - 1) == null) { currentseq.remove(currentseq.size() - 1); currentseq.remove(currentseq.size() - 1); } if (currentseq.get(currentseq.size() - 2) != null || currentseq.get(currentseq.size() - 1) != null) { predecessors.add(currentseq); currentseq = new Vector(); currentseq.add(null); currentseq.add(null); } hasPredecessors = true; } else if ("//".equals(token)) { if (mustBePre) throw new ParseException("Predecessors cannot be " + "parallellized", st.getTokenIndex()); mustBeSeq = true; if (currentseq != null) parallell = true; } else if (",".equals(token)) { if (currentseq != null) { if (mustBeSeq) throw new ParseException("Messages cannot have " + "many sequence numbers", st .getTokenIndex()); mustBePre = true; if (currentseq.size() > 2 && currentseq.get(currentseq.size() - 2) == null && currentseq.get(currentseq.size() - 1) == null) { currentseq.remove(currentseq.size() - 1); currentseq.remove(currentseq.size() - 1); } if (currentseq.get(currentseq.size() - 2) != null || currentseq.get(currentseq.size() - 1) != null) { predecessors.add(currentseq); currentseq = new Vector(); currentseq.add(null); currentseq.add(null); } hasPredecessors = true; } else { if (varname == null && fname != null) { varname = fname + token; fname = null; } else if (varname != null && fname == null) { varname += token; } else { throw new ParseException( "Unexpected character (,)", st .getTokenIndex()); } } } else if ("=".equals(token) || ":=".equals(token)) { if (currentseq == null) { if (varname == null) { varname = fname; fname = ""; } else { fname = ""; } } } else if (currentseq == null) { if (paramExpr == null && token.charAt(0) == '(') { if (token.charAt(token.length() - 1) != ')') throw new ParseException("Malformed parameters", st .getTokenIndex()); if (fname == null || "".equals(fname)) throw new ParseException("Must be a function name " + "before the parameters", st .getTokenIndex()); if (varname == null) varname = ""; paramExpr = token.substring(1, token.length() - 1); } else if (varname != null && fname == null) { varname += token; } else if (fname == null || fname.length() == 0) { fname = token; } else { throw new ParseException("Unexpected token (" + token + ")", st.getTokenIndex()); } } else { boolean hasVal = currentseq.get(currentseq.size() - 2) != null; boolean hasOrd = currentseq.get(currentseq.size() - 1) != null; boolean assigned = false; int bp = findMsgOrderBreak(token); if (!hasVal && !assigned && bp == token.length()) { try { currentseq.set(currentseq.size() - 2, new Integer( token)); assigned = true; } catch (NumberFormatException nfe) { } } if (!hasOrd && !assigned && bp == 0) { try { currentseq.set(currentseq.size() - 1, new Integer( parseMsgOrder(token))); assigned = true; } catch (NumberFormatException nfe) { } } if (!hasVal && !hasOrd && !assigned && bp > 0 && bp < token.length()) { Integer nbr, ord; try { nbr = new Integer(token.substring(0, bp)); ord = new Integer( parseMsgOrder(token.substring(bp))); currentseq.set(currentseq.size() - 2, nbr); currentseq.set(currentseq.size() - 1, ord); assigned = true; } catch (NumberFormatException nfe) { } } if (!assigned) { throw new ParseException("Unexpected token (" + token + ")", st.getTokenIndex()); } } } } catch (NoSuchElementException nsee) { throw new ParseException("Unexpected end of message", s.length()); } catch (ParseException pre) { throw pre; } if (paramExpr != null) { MyTokenizer st = new MyTokenizer(paramExpr, "\\,", parameterCustomSep); args = new Vector(); while (st.hasMoreTokens()) { token = st.nextToken(); if (",".equals(token)) { if (args.size() == 0) args.add(null); args.add(null); } else { if (args.size() == 0) { if (token.trim().length() == 0) continue; args.add(null); } String arg = (String) args.get(args.size() - 1); if (arg != null) arg = arg + token; else arg = token; args.set(args.size() - 1, arg); } } } else if (mayDeleteExpr) { args = new Vector(); } if (LOG.isDebugEnabled()) { StringBuffer buf = new StringBuffer(); buf.append("ParseMessage: " + s + "\n"); buf.append("Message: "); for (i = 0; seqno != null && i + 1 < seqno.size(); i += 2) { if (i > 0) buf.append(", "); buf.append(seqno.get(i) + " (" + seqno.get(i + 1) + ")"); } buf.append("\n"); buf.append("predecessors: " + predecessors.size() + "\n"); for (i = 0; i < predecessors.size(); i++) { int j; Vector v = (Vector) predecessors.get(i); buf.append(" Predecessor: "); for (j = 0; v != null && j + 1 < v.size(); j += 2) { if (j > 0) buf.append(", "); buf.append(v.get(j) + " (" + v.get(j + 1) + ")"); } } buf.append("guard: " + guard + " it: " + iterative + " pl: " + parallell + "\n"); buf.append(varname + " := " + fname + " ( " + paramExpr + " )" + "\n"); LOG.debug(buf); } if (ModelFacade.getAction(mes) == null) { Object a = UmlFactory.getFactory().getCommonBehavior() .createCallAction(); ModelFacade.addOwnedElement(ModelFacade.getContext(ModelFacade .getInteraction(mes)), a); ModelFacade.setAction(mes, a); } if (guard != null) { guard = "[" + guard.trim() + "]"; if (iterative) { if (parallell) guard = "*//" + guard; else guard = "*" + guard; } Object/* MIterationExpression */expr = UmlFactory.getFactory() .getDataTypes().createIterationExpression( Notation.getDefaultNotation().toString(), guard); ModelFacade.setRecurrence(ModelFacade.getAction(mes), expr); } if (fname == null) { if (!mayDeleteExpr && ModelFacade.getScript(ModelFacade.getAction(mes)) != null) { String body = (String) ModelFacade.getBody(ModelFacade .getScript(ModelFacade.getAction(mes))); int idx = body.indexOf(":="); if (idx >= 0) idx++; else idx = body.indexOf("="); if (idx >= 0) fname = body.substring(idx + 1); else fname = body; } else fname = ""; } if (varname == null) { if (!mayDeleteExpr && ModelFacade.getScript(ModelFacade.getAction(mes)) != null) { String body = (String) ModelFacade.getBody(ModelFacade .getScript(ModelFacade.getAction(mes))); int idx = body.indexOf(":="); if (idx < 0) idx = body.indexOf("="); if (idx >= 0) varname = body.substring(0, idx); else varname = ""; } else varname = ""; } if (fname != null) { String expr = fname.trim(); if (varname != null && varname.length() > 0) expr = varname.trim() + " := " + expr; if (ModelFacade.getScript(ModelFacade.getAction(mes)) == null || !expr.equals(ModelFacade.getBody(ModelFacade .getScript(ModelFacade.getAction(mes))))) { Object e = UmlFactory.getFactory().getDataTypes() .createActionExpression( Notation.getDefaultNotation().toString(), expr.trim()); ModelFacade.setScript(ModelFacade.getAction(mes), e); refindOperation = true; } } if (args != null) { Collection c = ModelFacade.getActualArguments(ModelFacade .getAction(mes)); Iterator it = c.iterator(); for (i = 0; i < args.size(); i++) { Object arg = (it.hasNext() ? /* (MArgument) */it.next() : null); if (arg == null) { arg = UmlFactory.getFactory().getCommonBehavior() .createArgument(); ModelFacade.addActualArgument(ModelFacade.getAction(mes), arg); refindOperation = true; } if (ModelFacade.getValue(arg) == null || !args.get(i).equals( ModelFacade.getBody(ModelFacade.getValue(arg)))) { String value = (args.get(i) != null ? (String) args.get(i) : ""); Object e = UmlFactory.getFactory().getDataTypes() .createExpression( Notation.getDefaultNotation().toString(), value.trim()); ModelFacade.setValue(arg, e); } } while (it.hasNext()) { ModelFacade.removeActualArgument(ModelFacade.getAction(mes), /* (MArgument) */it.next()); refindOperation = true; } } if (seqno != null) { Object/* MMessage */root; // Find the preceding message, if any, on either end of the // association. String pname = ""; String mname = ""; String gname = GeneratorDisplay.getInstance() .generateMessageNumber(mes); boolean swapRoles = false; int majval = (seqno.get(seqno.size() - 2) != null ? Math.max( ((Integer) seqno.get(seqno.size() - 2)).intValue() - 1, 0) : 0); int minval = (seqno.get(seqno.size() - 1) != null ? Math.max( ((Integer) seqno.get(seqno.size() - 1)).intValue(), 0) : 0); for (i = 0; i + 1 < seqno.size(); i += 2) { int bv = (seqno.get(i) != null ? Math.max(((Integer) seqno .get(i)).intValue(), 1) : 1); int sv = (seqno.get(i + 1) != null ? Math.max(((Integer) seqno .get(i + 1)).intValue(), 0) : 0); if (i > 0) mname += "."; mname += Integer.toString(bv) + (char) ('a' + sv); if (i + 3 < seqno.size()) { if (i > 0) pname += "."; pname += Integer.toString(bv) + (char) ('a' + sv); } } root = null; if (pname.length() > 0) { root = findMsg(ModelFacade.getSender(mes), pname); if (root == null) { root = findMsg(ModelFacade.getReceiver(mes), pname); if (root != null) { swapRoles = true; } } } else if (!hasMsgWithActivator(ModelFacade.getSender(mes), null) && hasMsgWithActivator(ModelFacade.getReceiver(mes), null)) swapRoles = true; if (compareMsgNumbers(mname, gname)) { ;// Do nothing } else if (isMsgNumberStartOf(gname, mname)) { throw new ParseException("Cannot move a message into the " + "subtree rooted at self", 0); } else if (ModelFacade.getPredecessors(mes).size() > 1 && ModelFacade.getMessages3(mes).size() > 1) { throw new ParseException("Cannot move a message which is " + "both start and end of several threads", 0); } else if (root == null && pname.length() > 0) { throw new ParseException("Cannot find the activator for the " + "message", 0); } else if (swapRoles && ModelFacade.getMessages4(mes).size() > 0 && (ModelFacade.getSender(mes) != ModelFacade .getReceiver(mes))) { throw new ParseException("Cannot reverse the direction of a " + "message that is an activator", 0); } else { // Disconnect the message from the call graph Collection c = ModelFacade.getPredecessors(mes); Collection c2 = ModelFacade.getMessages3(mes); Iterator it; it = c2.iterator(); while (it.hasNext()) { ModelFacade.removeMessage3(mes, /* (MMessage) */it.next()); } it = c.iterator(); while (it.hasNext()) { Iterator it2 = c2.iterator(); Object pre = /* (MMessage) */it.next(); ModelFacade.removePredecessor(mes, pre); while (it2.hasNext()) { ModelFacade.addPredecessor(it2.next(), pre); } } // Connect the message at a new spot ModelFacade.setActivator(mes, root); if (swapRoles) { Object/* MClassifierRole */r = ModelFacade.getSender(mes); ModelFacade.setSender(mes, ModelFacade.getReceiver(mes)); ModelFacade.setReceiver(mes, r); } if (root == null) { c = filterWithActivator(ModelFacade .getMessages2(ModelFacade.getSender(mes)), null); } else { c = ModelFacade.getMessages4(root); } c2 = findCandidateRoots(c, root, mes); it = c2.iterator(); // If c2 is empty, then we're done (or there is a // cycle in the message graph, which would be bad) If // c2 has more than one element, then the model is // crappy, but we'll just use one of them anyway if (majval <= 0) { while (it.hasNext()) ModelFacade.addMessage3(mes, /* (MMessage) */it.next()); } else if (it.hasNext()) { Object/* MMessage */pre = walk(/* (MMessage) */it.next(), majval - 1, false); Object/* MMessage */post = successor(pre, minval); if (post != null) { ModelFacade.removePredecessor(post, pre); ModelFacade.addPredecessor(post, mes); } insertSuccessor(pre, mes, minval); } refindOperation = true; } } if (fname != null && refindOperation) { Object role = ModelFacade.getReceiver(mes); Vector ops = getOperation(ModelFacade.getBases(role), fname.trim(), ModelFacade.getActualArguments(ModelFacade.getAction(mes)) .size()); // TODO: Should someone choose one, if there are more // than one? if (ModelFacade.isACallAction(ModelFacade.getAction(mes))) { Object a = /* (MCallAction) */ModelFacade.getAction(mes); if (ops.size() > 0) ModelFacade.setOperation(a, /* (MOperation) */ops.get(0)); else ModelFacade.setOperation(a, null); } } // TODO: Predecessors is not implemented, because it // causes some problems that I've not found an easy way to handle yet, // d00mst. The specific problem is that the notation currently is // ambiguous on second message after a thread split. // Why not implement it anyway? d00mst if (hasPredecessors) { Collection roots = findCandidateRoots(ModelFacade .getMessages(ModelFacade.getInteraction(mes)), null, null); Vector pre = new Vector(); Iterator it; predfor: for (i = 0; i < predecessors.size(); i++) { it = roots.iterator(); while (it.hasNext()) { Object/* MMessage */msg = walkTree(/*(MMessage)*/it.next(), (Vector) predecessors.get(i)); if (msg != null && msg != mes) { if (isBadPreMsg(mes, msg)) { throw new ParseException( "One predecessor cannot be a predecessor " + "to this message", 0); } pre.add(msg); continue predfor; } } throw new ParseException("Could not find predecessor", 0); } GeneratorDisplay.MsgPtr ptr = GeneratorDisplay.getInstance().new MsgPtr(); GeneratorDisplay.getInstance().recCountPredecessors(mes, ptr); if (ptr.message != null && !pre.contains(ptr.message)) pre.add(ptr.message); ModelFacade.setPredecessors(mes, pre); } } /** * Examines the call tree from chld to see if ans is an ancestor. * * @param ans * MMessage * @param chld * MMessage */ private boolean isBadPreMsg(Object ans, Object chld) { while (chld != null) { if (ans == chld) return true; if (isPredecessorMsg(ans, chld, 100)) return true; chld = ModelFacade.getActivator(chld); } return false; } /** * Examines the call tree from suc to see if pre is a predecessor. This * function is recursive and md specifies the maximum level of recursions * allowed. * * @param pre * MMessage * @param suc * MMessage */ private boolean isPredecessorMsg(Object pre, Object suc, int md) { Iterator it = ModelFacade.getPredecessors(suc).iterator(); while (it.hasNext()) { Object m = /* (MMessage) */it.next(); if (m == pre) return true; if (md > 0 && isPredecessorMsg(pre, m, md - 1)) return true; } return false; } /** * Walks a call tree from a root node following the directions given in path * to a destination node. If the destination node cannot be reached, then * null is returned. * * @param root The root of the call tree. * @param path The path to walk in the call tree. * @return The message at the end of path, or null. */ private Object walkTree(Object root, Vector path) { int i; for (i = 0; i + 1 < path.size(); i += 2) { int bv = (path.get(i) != null ? Math.max(((Integer) path.get(i)) .intValue() - 1, 0) : 0); int sv = (path.get(i + 1) != null ? Math.max(((Integer) path .get(i + 1)).intValue(), 0) : 0); root = walk(root, bv - 1, true); if (root == null) { return null; } if (bv > 0) { root = successor(root, sv); if (root == null) { return null; } } if (i + 3 < path.size()) { Iterator it = findCandidateRoots( ModelFacade.getMessages4(root), root, null).iterator(); // Things are strange if there are more than one candidate root, // it has no obvious interpretation in terms of a call tree. if (!it.hasNext()) return null; root = /* (MMessage) */it.next(); } } return root; } /** * Examines a collection to see if any message has the given message as an * activator. * * @param r * MClassifierRole * @param m * MMessage */ private boolean hasMsgWithActivator(Object r, Object m) { Iterator it = ModelFacade.getMessages2(r).iterator(); while (it.hasNext()) { if (ModelFacade.getActivator(it.next()) == m) { return true; } } return false; } /** * Inserts message s as the p'th successor of message m. * * @param m * MMessage * @param s * MMessage */ private void insertSuccessor(Object m, Object s, int p) { Vector v = new Vector(ModelFacade.getMessages3(m)); if (v.size() > p) v.insertElementAt(s, p); else v.add(s); ModelFacade.setMessages3(m, v); } /** * Finds the steps'th successor of message r in the sense that it is a * direct successor of r. Returns null if r has fewer successors. */ private Object/* MMessage */successor(Object/* MMessage */r, int steps) { Iterator it = ModelFacade.getMessages3(r).iterator(); while (it.hasNext() && steps > 0) { it.next(); steps--; } if (it.hasNext()) return /* (MMessage) */it.next(); return null; } /** * Finds the steps'th successor of r in the sense that it is a successor of * a successor of r (steps times). The first successor with the same * activator as r is used in each step. If there are not enough successors, * then struct determines the result. If struct is true, then null is * returned, otherwise the last successor found. */ private Object walk(Object/* MMessage */r, int steps, boolean strict) { Object/* MMessage */act = ModelFacade.getActivator(r); while (steps > 0) { Iterator it = ModelFacade.getMessages3(r).iterator(); do { if (!it.hasNext()) return (strict ? null : r); r = /* (MMessage) */it.next(); } while (ModelFacade.getActivator(r) != act); steps--; } return r; } /** * Finds the root candidates in a collection c, ie the messages in c that * has the activator a (may be null) and has no predecessor with the same * activator. If veto isn't null, then the message in veto will not be * included in the Collection of candidates. */ private Collection findCandidateRoots(Collection c, Object/* MMessage */a, Object/* MMessage */veto) { Iterator it = c.iterator(); Vector v = new Vector(); while (it.hasNext()) { Object m = /* (MMessage) */it.next(); if (m == veto) continue; if (ModelFacade.getActivator(m) != a) continue; Iterator it2 = ModelFacade.getPredecessors(m).iterator(); boolean candidate = true; while (it2.hasNext()) { Object m2 = /* (MMessage) */it2.next(); if (ModelFacade.getActivator(m2) == a) candidate = false; } if (candidate) v.add(m); } return v; } /** * Finds the messages in Collection c that has message a as activator. */ private Collection filterWithActivator(Collection c, Object/*MMessage*/a) { Iterator it = c.iterator(); Vector v = new Vector(); while (it.hasNext()) { Object m = /* (MMessage) */it.next(); if (ModelFacade.getActivator(m) == a) v.add(m); } return v; } /** * Finds the message in ClassifierRole r that has the message number written * in n. If it isn't found, null is returned. */ private Object findMsg(Object/* MClassifierRole */r, String n) { Collection c = ModelFacade.getMessages1(r); Iterator it = c.iterator(); while (it.hasNext()) { Object msg = /* (MMessage) */it.next(); String gname = GeneratorDisplay.getInstance() .generateMessageNumber(msg); if (compareMsgNumbers(gname, n)) return msg; } return null; } /** * Compares two message numbers with each other to see if they are equal, in * the sense that they refer to the same position in a call tree. */ private boolean compareMsgNumbers(String n1, String n2) { return isMsgNumberStartOf(n1, n2) && isMsgNumberStartOf(n2, n1); } /** * Compares two message numbers n1, n2 with each other to determine if n1 * specifies a the same position as n2 in a call tree or n1 specifies a * position that is a father of the position specified by n2. */ private boolean isMsgNumberStartOf(String n1, String n2) { int i, j, len, jlen; len = n1.length(); jlen = n2.length(); i = 0; j = 0; for (; i < len;) { int ibv, isv; int jbv, jsv; ibv = 0; for (; i < len; i++) { char c = n1.charAt(i); if (c < '0' || c > '9') break; ibv *= 10; ibv += c - '0'; } isv = 0; for (; i < len; i++) { char c = n1.charAt(i); if (c < 'a' || c > 'z') break; isv *= 26; isv += c - 'a'; } jbv = 0; for (; j < jlen; j++) { char c = n2.charAt(j); if (c < '0' || c > '9') break; jbv *= 10; jbv += c - '0'; } jsv = 0; for (; j < jlen; j++) { char c = n2.charAt(j); if (c < 'a' || c > 'z') break; jsv *= 26; jsv += c - 'a'; } if (ibv != jbv || isv != jsv) { return false; } if (i < len && n1.charAt(i) != '.') { return false; } else i++; if (j < jlen && n2.charAt(j) != '.') { return false; } else j++; } return true; } /** * Finds the operations in Collection c with name name and params number of * parameters. If no operation is found, one is created. The applicable * operations are returned. */ private Vector getOperation(Collection c, String name, int params) { Vector options = new Vector(); Iterator it; if (name == null || name.length() == 0) return options; it = c.iterator(); while (it.hasNext()) { Object clf = /* (MClassifier) */it.next(); Collection oe = ModelFacade.getFeatures(clf); Iterator it2 = oe.iterator(); while (it2.hasNext()) { Object me = /* (MModelElement) */it2.next(); if (!(ModelFacade.isAOperation(me))) continue; Object op = /* (MOperation) */me; if (!name.equals(ModelFacade.getName(op))) continue; if (params != countParameters(op)) continue; options.add(op); } } if (options.size() > 0) return options; it = c.iterator(); if (it.hasNext()) { String expr = name + "("; int i; for (i = 0; i < params; i++) { if (i > 0) expr += ", "; expr += "param" + (i + 1); } expr += ")"; // Jaap Branderhorst 2002-23-09 added next lines to link // parameters and operations to the figs that represent // them Object cls = /* (MClassifier) */it.next(); Object op = UmlFactory.getFactory().getCore().buildOperation(cls); try { parseOperation(expr, op); } catch (ParseException pe) { LOG.error("Unexpected ParseException in getOperation: " + pe, pe); } options.add(op); } return options; } /** * Counts the number of parameters that are not return values. */ private int countParameters(Object bf) { Collection c = ModelFacade.getParameters(bf); Iterator it = c.iterator(); int count = 0; while (it.hasNext()) { Object p = it.next(); if (ModelFacade.isReturn(p)) continue; count++; } return count; } /** * Parses a message order specification. */ private static int parseMsgOrder(String s) { int i, t; int v = 0; t = s.length(); for (i = 0; i < t; i++) { char c = s.charAt(i); if (c < 'a' || c > 'z') throw new NumberFormatException(); v *= 26; v += c - 'a'; } return v; } /** * Finds the break between message number and (possibly) message order. */ private static int findMsgOrderBreak(String s) { int i, t; t = s.length(); for (i = 0; i < t; i++) { char c = s.charAt(i); if (c < '0' || c > '9') break; } return i; } /** * Parse a line of the form: "name: action" * * @param sti * the stimulus object to which the string applies * @param s * the string to be parsed. */ public void parseStimulus(Object sti, String s) { // strip any trailing semi-colons s = s.trim(); if (s.length() == 0) return; if (s.charAt(s.length() - 1) == ';') s = s.substring(0, s.length() - 2); //cut trailing string "new Action" s = s.trim(); if (s.length() == 0) return; if (s.endsWith("new Action")) s = s.substring(0, s.length() - 10); String name = ""; String action = ""; String actionfirst = ""; if (s.indexOf(":", 0) > -1) { name = s.substring(0, s.indexOf(":")).trim(); actionfirst = s.substring(s.indexOf(":") + 1).trim(); if (actionfirst.indexOf(":", 0) > 1) { action = actionfirst.substring(0, actionfirst.indexOf(":")) .trim(); } else action = actionfirst; } else name = s; Object act = ModelFacade.getDispatchAction(sti); ModelFacade.setName(act, action); ModelFacade.setName(sti, name); } /** * @deprecated Since 0.15.1, this is essentially a String constructor. It * breaks the idea that the parser is editing preexisting * objects, which is bad. It is not used within core ArgoUML. * d00mst. * It is used in {@link #parseTransition(Object, String)} * above. * * @param s the string to parse * @return the created object */ public Object parseAction(String s) { Object a = UmlFactory.getFactory().getCommonBehavior() .createCallAction(); ModelFacade.setScript(a, UmlFactory.getFactory().getDataTypes() .createActionExpression("", s)); return a; } /** * @deprecated Since 0.15.1, this is essentially a String constructor. It * breaks the idea the idea that the parser is editing * preexisting objects, which is bad. It is not used within core * ArgoUML. d00mst. * It is used in {@link #parseTransition(Object, String)} * above. * * @param s the string to parse * @return the new guard */ public Object parseGuard(String s) { Object g = UmlFactory.getFactory().getStateMachines().createGuard(); ModelFacade.setExpression(g, UmlFactory.getFactory().getDataTypes() .createBooleanExpression("", s)); return g; } /** * Parses the event-signature. An event-signature has the following syntax: *
|
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.