Modeller.java
/* $Id$
*****************************************************************************
* Copyright (c) 2009-2013 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thomas Neustupny
* Alexander Lepekhine
* Laurent Braud
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2006 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.
// Copyright (c) 2003-2008 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.language.java.reveng;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.argouml.application.api.Argo;
import org.argouml.kernel.ProjectManager;
import org.argouml.language.java.reveng.classfile.ParserUtils;
import org.argouml.model.CoreFactory;
import org.argouml.model.Facade;
import org.argouml.model.Model;
import org.argouml.ocl.OCLUtil;
import org.argouml.profile.Profile;
import org.argouml.uml.reveng.ImportCommon;
import org.argouml.uml.reveng.ImportInterface;
/**
* Modeller maps Java source code(parsed/recognised by ANTLR) to UML model
* elements, it applies some of the semantics in JSR-26. Note: JSR-26 was
* withdrawn in March, 2004, so it obviously provides no guidance for more
* recent language features such as Java 5.
*
* TODO: This really needs a more sophisticated symbol table facility. It
* currently uses the model repository as its symbol table which makes it easy
* to merge into an existing model, but it also sometimes requires guessing
* about what a symbol represents (e.g. interface, class, or package) so that
* the name can instantiated in a concrete form. - tfm 20070911
*
* @author Marcus Andersson, Thomas Neustupny
*/
public class Modeller {
private static final Logger LOG =
Logger.getLogger(Modeller.class.getName());
private static final String JAVA_PACKAGE = "java.lang";
private static final List<String> EMPTY_STRING_LIST =
Collections.emptyList();
/**
* Current working model.
*/
private Object model;
/**
* Java profile model.
*/
private Profile javaProfile;
/**
* Current import settings.
*/
private ImportCommon importSession;
/**
* The package which the currentClassifier belongs to.
*/
private Object currentPackage;
/**
* Keeps the data that varies during parsing.
*/
private ParseState parseState;
/**
* Stack up the state when descending inner classes.
*/
private Stack<ParseState> parseStateStack;
/**
* Only attributes will be generated.
*/
private boolean noAssociations;
/**
* Arrays will be modelled as unique datatypes.
*/
private boolean arraysAsDatatype;
/**
* The name of the file being parsed.
*/
private String fileName;
/**
* Arbitrary attributes.
*/
private Hashtable<String, Object> attributes =
new Hashtable<String, Object>();
/**
* List of the names of parsed method calls.
*/
private List<String> methodCalls = new ArrayList<String>();
/**
* HashMap of parsed local variables. Indexed by variable name with string
* representation of the type stored as the value.
*/
private Hashtable<String, String> localVariables =
new Hashtable<String, String>();
/**
* New model elements that were created during this reverse engineering
* session. TODO: We want a stronger type here, but ArgoUML treats all
* elements as just simple Objects.
*/
private Collection<Object> newElements;
/**
* Flag to control generation of artificial names for associations. If true,
* generate names of form "From->To". If false, set name to null.
*/
private boolean generateNames = true;
/**
* Create a new modeller.
*
* @param theModel The model to work with.
* @param attributeSelected true if associations should be modeled as
* attributes
* @param datatypeSelected true if arrays should be modeled as datatypes
* instead of instead of using UML multiplicities
* @param theFileName the current file name
* @deprecated for 0.27.2 by thn. Use the other constructor.
*/
public Modeller(Object theModel, boolean attributeSelected,
boolean datatypeSelected, String theFileName) {
this(theModel, null, attributeSelected, datatypeSelected, theFileName);
}
/**
* Create a new modeller.
*
* @param theModel The model to work with.
* @param theJavaProfile The Java profile.
* @param attributeSelected true if associations should be modeled as
* attributes
* @param datatypeSelected true if arrays should be modeled as datatypes
* instead of instead of using UML multiplicities
* @param theFileName the current file name
*/
public Modeller(Object theModel, Profile theJavaProfile,
boolean attributeSelected, boolean datatypeSelected,
String theFileName) {
model = theModel;
javaProfile = theJavaProfile;
noAssociations = attributeSelected;
arraysAsDatatype = datatypeSelected;
currentPackage = this.model;
newElements = new HashSet<Object>();
parseState = new ParseState(this.model, getPackage(JAVA_PACKAGE, true));
parseStateStack = new Stack<ParseState>();
fileName = theFileName;
if (javaProfile == null) {
LOG.warning("No Java profile activated for Java source import. "
+ "Why?");
}
}
/**
* @param key the key of the attribute to get
* @return the value of the attribute
*/
public Object getAttribute(String key) {
return attributes.get(key);
}
/**
* @param key the key of the attribute
* @param value the value for the attribute
*/
public void setAttribute(String key, Object value) {
attributes.put(key, value);
}
/**
* This is a mapping from a Java compilation Unit -> a UML artifact.
* Classes are resident in a component in UML1, and realizing classifiers
* in UML2. Imports are relationships between artifacts and other
* classes / packages.
* <p>
*
* See JSR 26 (for UML1?).
* <p>
*
* Adding artifacts is a little messy since there are 2 cases:
*
* <ol>
* <li>source file has package statement, will be added several times since
* lookup in addComponent() only looks in the model since the package
* namespace is not yet known.
*
* <li>source file has not package statement: artifact is added to the
* model namespace. There is no package statement so the lookup will
* always work.
*
* </ol>
* Therefore in the case of (1), we need to delete duplicate artifacts in
* the addPackage() method.
* <p>
*
* In either case we need to add a package since we don't know in advance if
* there will be a package statement.
* <p>
*/
public void addComponent() {
// try and find the artifact in the current package
// to cope with repeated imports
// [this will never work if a package statement exists:
// because the package statement is parsed after the component is
// identified]
Object artifact = Model.getFacade().lookupIn(currentPackage, fileName);
if (artifact == null) {
// remove the java specific ending (per JSR 26).
// BUT we can't do this because then the component will be confused
// with its class with the same name when invoking
// Model.getFacade().lookupIn(Object,String)
/*
* if(fileName.endsWith(".java")) fileName = fileName.substring(0,
* fileName.length()-5);
*/
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
artifact = Model.getCoreFactory().createComponent();
} else {
artifact = Model.getCoreFactory().createArtifact();
}
Model.getCoreHelper().setName(artifact, fileName);
newElements.add(artifact);
}
parseState.setArtifact(artifact);
// set the namespace of the component, in the event
// that the source file does not have a package stmt
Model.getCoreHelper().setNamespace(parseState.getArtifact(), model);
}
/**
* Called from the parser when a package clause is found.
*
* @param name The name of the package.
*/
public void addPackage(String name) {
// We used to add diagrams to the project here for each package
// but diagram creation is handled in the common code for all
// reverse engineering modules now
// Find the top level package
String ownerPackageName, currentName = name;
ownerPackageName = getPackageName(currentName);
while (!"".equals(ownerPackageName)) {
currentName = ownerPackageName;
ownerPackageName = getPackageName(currentName);
}
// here, getPackage must NOT use the Java profile, because
// the declared package need to be in the user model
Object mPackage = getPackage(currentName, false);
// Save src_path in the upper package
// TODO: Rework this so that we don't need importSession here.
// perhaps move to the common import code. - tfm
if (importSession != null
&& importSession.getSrcPath() != null
&& Model.getFacade().getTaggedValue(mPackage,
ImportInterface.SOURCE_PATH_TAG) == null) {
String[] srcPaths = {
importSession.getSrcPath()
};
buildTaggedValue(mPackage, ImportInterface.SOURCE_PATH_TAG,
srcPaths);
}
// Find or create a Package model element for this package
// (in user model only, because a new package must be added).
mPackage = getPackage(name, false);
// Set the current package for the following source code.
currentPackage = mPackage;
parseState.addPackageContext(mPackage);
// set the namespace of the artifact
// check to see if there is already a artifact defined:
Object artifact = Model.getFacade().lookupIn(currentPackage, fileName);
if (artifact == null) {
// set the namespace of the artifact
Model.getCoreHelper().setNamespace(parseState.getArtifact(),
currentPackage);
} else {
// an artifact already exists,
// so delete the latest one(the duplicate)
Object oldArtifact = parseState.getArtifact();
Model.getUmlFactory().delete(oldArtifact);
newElements.remove(oldArtifact);
// change the parse state to the existing one.
parseState.setArtifact(artifact);
}
}
/**
* Called from the parser when an import clause is found.
*
* @param name The name of the import. Can end with a '*'.
*/
public void addImport(String name) {
addImport(name, false);
}
/*
* ClassSignature: TypeParametersopt Superopt Interfacesopt
*
* TypeParameters ::= < TypeParameterList > TypeParameterList ::=
* TypeParameterList , TypeParameter | TypeParameter
*
* TypeParameter: TypeVariable TypeBoundopt TypeBound: extends
* ClassOrInterfaceType AdditionalBoundListopt AdditionalBoundList:
* AdditionalBound AdditionalBoundList AdditionalBound AdditionalBound: &
* InterfaceType
*/
public void addClassSignature(String signature) {
addTypeParameters(parseState.getClassifier(),
ParserUtils.extractTypeParameters(signature));
}
/**
* Called from the parser when an import clause is found.
*
* @param name The name of the import. Can end with a '*'.
* @param forceIt Force addition by creating all that's missing.
*/
void addImport(String name, boolean forceIt) {
// only do imports on the 2nd pass.
if (getLevel() == 0) {
return;
}
String packageName = getPackageName(name);
if (packageName == null || "".equals(packageName)) {
// TODO: This won't happen and can be removed when there is a
// real symbol table for name lookup instead of guessing based
// on parsing "." strings
LOG.log(Level.WARNING,
"Import skipped - unable to get package name for {0}",
name);
return;
}
// TODO: In the case of an inner class, we probably want either the
// qualified name with both outer and inner class names, or just the
// outer class name
String classifierName = getClassifierName(name);
Object mPackage = getPackage(packageName, true);
// import on demand
if (classifierName.equals("*")) {
parseState.addPackageContext(mPackage);
Object srcFile = parseState.getArtifact();
buildDependency(mPackage, srcFile, "javaImport");
}
// single type import
else {
Object mClassifier = null;
try {
mClassifier = (new PackageContext(null, mPackage)).get(
classifierName, false, javaProfile);
} catch (ClassifierNotFoundException e) {
if (forceIt && classifierName != null && mPackage != null) {
// call getPackage again WITHOUT Java profile, because
// class creation is only allowed in the user model
mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
mClassifier = Model.getFacade().lookupIn(mPackage,
classifierName);
if (mClassifier == null) {
// we must guess if it's a class/interface, so: class
LOG.info("Modeller.java: "
+ "forced creation of unknown classifier "
+ classifierName);
// TODO: A better strategy would be to defer creating
// this until we can determine what it is
mClassifier = Model.getCoreFactory().buildClass(
classifierName, mPackage);
newElements.add(mClassifier);
}
} else {
warnClassifierNotFound(classifierName,
"an imported classifier");
}
}
if (mClassifier != null) {
parseState.addClassifierContext(mClassifier);
Object srcFile = parseState.getArtifact();
buildDependency(mClassifier, srcFile, "javaImport");
}
}
}
/**
* Called from the parser to add a dependency to a classifier.
*
* @param name The name of the classifier candidate.
*/
void addClassifierDependency(String name) {
String classifierName = stripVarargAndGenerics(name);
Object clientObj = parseState.getClassifier();
if (clientObj == null) {
return;
}
Object supplierObj = null;
// first try: lookup classifierName in same namespace
Object ns = Model.getFacade().getNamespace(clientObj);
if (ns != null) {
supplierObj = Model.getFacade().lookupIn(ns, classifierName);
}
// second try: resolve fully qualified classifier (xxx.yyy.Zzz)
String packageName = getPackageName(name);
if (supplierObj == null && packageName != null && packageName.length() > 0) {
classifierName = this.getClassifierName(name);
Object mPackage = getPackage(packageName, true);
supplierObj = Model.getFacade().lookupIn(mPackage, classifierName);
}
// third try: lookup in imports
Object srcFile = parseStateStack.lastElement().getArtifact();
if (supplierObj == null && srcFile != null) {
Collection dependencies = Model.getFacade().getClientDependencies(srcFile);
for (Object dep : dependencies) {
for (Object suppl : Model.getFacade().getSuppliers(dep)) {
if (Model.getFacade().isAPackage(suppl)) {
supplierObj = Model.getFacade().lookupIn(suppl, classifierName);
} else if (classifierName.equals(Model.getFacade().getName(suppl))) {
supplierObj = suppl;
}
}
if (supplierObj != null) {
break;
}
}
}
// finally build the dependency
if (supplierObj != null) {
buildDependency(supplierObj, clientObj, null);
}
}
/*
* Build a dependency, e.g. a Java import equivalent in UML. First search
* for an existing dependency. Create a new one if not found.
*/
private Object buildDependency(Object supplier, Object client, String stereoname) {
// TODO: add <<javaImport>> stereotype to Java profile - thn
Collection dependencies = Model.getCoreHelper().getDependencies(
supplier, client);
for (Object dep : dependencies) {
for (Object stereotype : Model.getFacade().getStereotypes(dep)) {
if (stereoname == null || stereoname.equals(
Model.getFacade().getName(stereotype))) {
return dep;
}
}
}
// Didn't find it. Let's create one.
Object dependency = Model.getCoreFactory().buildDependency(client,
supplier);
if (stereoname != null && Model.getFacade().getUmlVersion().charAt(0) == '1') {
// TODO: support for stereotypes in eUML
Model.getCoreHelper().addStereotype(dependency,
getUML1Stereotype(stereoname));
ProjectManager.getManager().updateRoots();
}
String newName = makeDependencyName(client, supplier);
Model.getCoreHelper().setName(dependency, newName);
newElements.add(dependency);
return dependency;
}
private String makeAbstractionName(Object child, Object parent) {
return makeFromToName(child, parent);
}
private String makeAssociationName(Object from, Object to) {
return makeFromToName(from, to);
}
private String makeDependencyName(Object from, Object to) {
return makeFromToName(from, to);
}
private String makeFromToName(Object from, Object to) {
if (!generateNames) {
return null;
} else {
return makeFromToName(Model.getFacade().getName(from), Model
.getFacade().getName(to));
}
}
private String makeFromToName(String from, String to) {
if (!generateNames) {
return null;
} else {
// TODO: This isn't localized, but I'm not sure it can be
// without other side effects - tfm - 20070410
return from + " -> " + to;
}
}
/**
* Called from the parser when a class declaration is found.
*
* @param name The name of the class.
* @param modifiers A sequence of class modifiers.
* @param superclassName Zero or one string with the name of the superclass.
* Can be fully qualified or just a simple class name.
* @param interfaces Zero or more strings with the names of implemented
* interfaces. Can be fully qualified or just a simple interface
* name.
* @param javadoc The javadoc comment. null or "" if no comment available.
*/
public void addClass(String name, short modifiers, String superclassName,
List<String> interfaces, String javadoc) {
addClass(name, modifiers, EMPTY_STRING_LIST, superclassName,
interfaces, javadoc, false);
}
/**
* Called from the parser when a class declaration is found.
*
* @param name The name of the class.
* @param modifiers A bitmask of class modifiers.
* @param typeParameters List of strings containing names of types for
* parameters
* @param superclassName Zero or one string with the name of the superclass.
* Can be fully qualified or just a simple class name.
* @param interfaces Zero or more strings with the names of implemented
* interfaces. Can be fully qualified or just a simple interface
* name.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
*/
void addClass(String name, short modifiers, List<String> typeParameters,
String superclassName, List<String> interfaces, String javadoc,
boolean forceIt) {
if (typeParameters != null && typeParameters.size() > 0) {
logError("type parameters not supported on Class", name);
for (String s : typeParameters) {
logError("type parameter ", s);
}
}
Object mClass = addClassifier(Model.getCoreFactory().createClass(),
name, modifiers, javadoc, typeParameters);
Model.getCoreHelper().setAbstract(mClass,
(modifiers & JavaParser.ACC_ABSTRACT) > 0);
Model.getCoreHelper().setLeaf(mClass,
(modifiers & JavaParser.ACC_FINAL) > 0);
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper().setRoot(mClass, false);
}
newElements.add(mClass);
// only do generalizations and realizations on the 2nd pass.
if (getLevel() == 0) {
return;
}
if (superclassName != null) {
Object parentClass = null;
try {
parentClass = getContext(superclassName).get(
getClassifierName(superclassName), false, javaProfile);
getGeneralization(currentPackage, parentClass, mClass);
} catch (ClassifierNotFoundException e) {
if (forceIt && superclassName != null && model != null) {
LOG.info("Modeller.java: forced creation of unknown class "
+ superclassName);
String packageName = getPackageName(superclassName);
String classifierName = getClassifierName(superclassName);
// here, getPackage must NOT use the Java profile, because
// class creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
parentClass = Model.getFacade().lookupIn(mPackage,
classifierName);
if (parentClass == null) {
parentClass = Model.getCoreFactory().buildClass(
classifierName, mPackage);
newElements.add(parentClass);
}
getGeneralization(currentPackage, parentClass, mClass);
} else {
warnClassifierNotFound(superclassName, "a generalization");
}
}
}
if (interfaces != null) {
addInterfaces(mClass, interfaces, forceIt);
}
}
/**
* Called from the parser when an anonymous inner class is found.
*
* @param type The type of this anonymous class.
*/
public void addAnonymousClass(String type) {
addAnonymousClass(type, false);
}
/**
* Called from the parser when an anonymous inner class is found.
*
* @param type The type of this anonymous class.
* @param forceIt Force addition by creating all that's missing.
*/
void addAnonymousClass(String type, boolean forceIt) {
String name = parseState.anonymousClass();
try {
Object mClassifier = getContext(type).get(getClassifierName(type),
false, javaProfile);
List<String> interfaces = new ArrayList<String>();
if (Model.getFacade().isAInterface(mClassifier)) {
interfaces.add(type);
}
addClass(name, (short) 0, EMPTY_STRING_LIST, Model.getFacade()
.isAClass(mClassifier) ? type : null, interfaces, "",
forceIt);
} catch (ClassifierNotFoundException e) {
// Must add it anyway, or the class popping will mismatch.
addClass(name, (short) 0, EMPTY_STRING_LIST, null,
EMPTY_STRING_LIST, "", forceIt);
LOG.info("Modeller.java: an anonymous class was created "
+ "although it could not be found in the classpath.");
}
}
/**
* Add an Interface to the model.
*
* TODO: This method preserves the historical public API which is used by
* other reverse engineering modules such as the Classfile module. This
* really needs to be decoupled.
*
* @param name The name of the interface.
* @param modifiers A sequence of interface modifiers.
* @param interfaces Zero or more strings with the names of extended
* interfaces. Can be fully qualified or just a simple interface
* name.
* @param javadoc The javadoc comment. "" if no comment available.
*/
public void addInterface(String name, short modifiers,
List<String> interfaces, String javadoc) {
addInterface(name, modifiers, EMPTY_STRING_LIST, interfaces, javadoc,
false);
}
/**
* Called from the parser when an interface declaration is found.
*
* @param name The name of the interface.
* @param modifiers A sequence of interface modifiers.
* @param interfaces Zero or more strings with the names of extended
* interfaces. Can be fully qualified or just a simple interface
* name.
* @param javadoc The javadoc comment. "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
*/
void addInterface(String name, short modifiers,
List<String> typeParameters, List<String> interfaces,
String javadoc, boolean forceIt) {
if (typeParameters != null && typeParameters.size() > 0) {
logError("type parameters not supported on Interface", name);
}
Object mInterface = addClassifier(Model.getCoreFactory()
.createInterface(), name, modifiers, javadoc, typeParameters);
// only do generalizations and realizations on the 2nd pass.
if (getLevel() == 0) {
return;
}
for (String interfaceName : interfaces) {
Object parentInterface = null;
try {
parentInterface = getContext(interfaceName).get(
getClassifierName(interfaceName), true, javaProfile);
getGeneralization(currentPackage, parentInterface, mInterface);
} catch (ClassifierNotFoundException e) {
if (forceIt && interfaceName != null && model != null) {
LOG.info("Modeller.java: "
+ "forced creation of unknown interface "
+ interfaceName);
String packageName = getPackageName(interfaceName);
String classifierName = getClassifierName(interfaceName);
// here, getPackage must NOT use the Java profile, because
// interface creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
parentInterface = Model.getFacade().lookupIn(mPackage,
classifierName);
if (parentInterface == null) {
parentInterface = Model.getCoreFactory()
.buildInterface(classifierName, mPackage);
newElements.add(parentInterface);
}
getGeneralization(currentPackage, parentInterface,
mInterface);
} else {
warnClassifierNotFound(interfaceName, "a generalization");
}
}
}
}
/**
* Called from the parser when an enumeration declaration is found.
*
* @param name The name of the class.
* @param modifiers A sequence of class modifiers.
* @param interfaces Zero or more strings with the names of implemented
* interfaces. Can be fully qualified or just a simple interface
* name.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
*/
void addEnumeration(String name, short modifiers, List<String> interfaces,
String javadoc, boolean forceIt) {
Object mEnum = null;
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
mEnum = addClassifier(Model.getCoreFactory().createClass(),
name, modifiers, javadoc, EMPTY_STRING_LIST); // no type params
// for now
Model.getCoreHelper().addStereotype(mEnum,
getUML1Stereotype("enumeration"));
ProjectManager.getManager().updateRoots();
} else {
// TODO: always use UML Enumerations, like this:
mEnum = Model.getCoreFactory().createEnumeration();
Object mNamespace;
if (parseState.getClassifier() != null) {
// the new classifier is a java inner enumeration
mNamespace = parseState.getClassifier();
} else {
// the new classifier is a top level java enumeration
parseState.outerClassifier();
mNamespace = currentPackage;
}
LOG.log(Level.INFO, "Created new enumeration for {0}", name);
Model.getCoreHelper().setName(mEnum, name);
Model.getCoreHelper().setNamespace(mEnum, mNamespace);
newElements.add(mEnum);
parseState.innerClassifier(mEnum);
// change the parse state to a classifier parse state
parseStateStack.push(parseState);
parseState = new ParseState(parseState, mEnum, currentPackage);
setVisibility(mEnum, modifiers);
// Add classifier documentation tags during
// first (or only) pass only
if (getLevel() <= 0) {
addDocumentationTag(mEnum, javadoc);
}
}
if ((modifiers & JavaParser.ACC_ABSTRACT) > 0) {
// abstract enums are illegal in Java
logError("Illegal \"abstract\" modifier on enum ", name);
} else {
Model.getCoreHelper().setAbstract(mEnum, false);
}
if ((modifiers & JavaParser.ACC_FINAL) > 0) {
// it's an error to explicitly use the 'final' keyword for an enum
// declaration
logError("Illegal \"final\" modifier on enum ", name);
} else {
// enums are implicitly final unless they contain a class body
// (which we won't know until we process the constants
Model.getCoreHelper().setLeaf(mEnum, true);
}
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper().setRoot(mEnum, false);
}
// only do realizations on the 2nd pass.
if (getLevel() == 0) {
return;
}
if (interfaces != null) {
addInterfaces(mEnum, interfaces, forceIt);
}
}
/**
* @param mClass
* @param interfaces
* @param forceIt
*/
private void addInterfaces(Object mClass, List<String> interfaces,
boolean forceIt) {
for (String interfaceName : interfaces) {
Object mInterface = null;
try {
mInterface = getContext(interfaceName).get(
getClassifierName(interfaceName), true, javaProfile);
} catch (ClassifierNotFoundException e) {
if (forceIt && interfaceName != null && model != null) {
LOG.info("Modeller.java: "
+ "forced creation of unknown interface "
+ interfaceName);
String packageName = getPackageName(interfaceName);
String classifierName = getClassifierName(interfaceName);
// here, getPackage must NOT use the Java profile, because
// interface creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
mInterface = Model.getFacade().lookupIn(mPackage,
classifierName);
if (mInterface == null) {
mInterface = Model.getCoreFactory().buildInterface(
classifierName, mPackage);
newElements.add(mInterface);
}
} else {
warnClassifierNotFound(interfaceName, "an abstraction");
}
}
// TODO: This should use the Model API's buildAbstraction - tfm
if (mInterface != null && mInterface != mClass) {
Object mAbstraction = getAbstraction(mInterface, mClass);
if (Model.getFacade().getSuppliers(mAbstraction).size() == 0) {
Model.getCoreHelper().addSupplier(mAbstraction, mInterface);
Model.getCoreHelper().addClient(mAbstraction, mClass);
}
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper()
.setNamespace(mAbstraction, currentPackage);
Model.getCoreHelper().addStereotype(mAbstraction,
getUML1Stereotype(CoreFactory.REALIZE_STEREOTYPE));
ProjectManager.getManager().updateRoots();
}
newElements.add(mAbstraction);
}
}
}
/**
* Called from the parser when an enumeration literal is found.
*
* @param name The name of the enumerationLiteral.
*/
void addEnumerationLiteral(String name) {
Object enumeration = parseState.getClassifier();
if (!isAEnumeration(enumeration)) {
throw new ParseStateException("not an Enumeration");
}
short mod = JavaParser.ACC_PUBLIC | JavaParser.ACC_FINAL
| JavaParser.ACC_STATIC;
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
// TODO: make this obsolete (always use real enumerations)
addAttribute(mod, null, name, null, null, true);
} else {
Model.getCoreFactory().buildEnumerationLiteral(name, enumeration);
}
// add an <<enum>> stereotype to distinguish it from fields
// in the class body?
}
/*
* Recognizer for enumeration. In UML1 an enumeration is a Class with
* the <<enumeration>> stereotype applied.
*/
private boolean isAEnumeration(Object element) {
if (Model.getFacade().isAEnumeration(element)) {
return true;
}
// TODO: make the following obsolete
if (!Model.getFacade().isAClass(element)) {
return false;
}
return Model.getExtensionMechanismsHelper().hasStereotype(element,
"enumeration");
}
/**
* Add an annotation declaration
*
* @param name identifier for annotation definition.
* @param modifiers A sequence of modifiers.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
*/
void addAnnotationDefinition(String name, short modifiers, String javadoc,
boolean forceIt) {
// TODO: Not implemented
logError("Java 5 annotation definitions not supported", "@" + name);
}
/**
* Called from the parser when an annotation declaration is found.
*
* @param name identifier for annotation.
*/
void addAnnotation(String name) {
// TODO: Not implemented
logError("Java 5 annotations not supported", "@" + name);
}
/**
* Done adding an annotation.
*/
void endAnnotation() {
// TODO: Placeholder. Can we use popClassifier here?
}
void addTypeParameters(Object modelElement, List<String> typeParameters) {
if (modelElement == null || typeParameters == null) {
return;
}
if (Model.getFacade().getTemplateParameters(modelElement).size() == 0) {
for (String parameter : typeParameters) {
// parse parameter to name and bounds
Pattern p =
Pattern.compile("([^ ]*)( super | extends )?((.*))");
Matcher m = p.matcher(parameter);
if (m.matches()) {
String templateParameterName = m.group(1);
Object param = Model.getCoreFactory().createParameter();
Model.getCoreHelper().setName(param, templateParameterName);
Object templateParameter =
Model.getCoreFactory()
.buildTemplateParameter(modelElement, param, null);
if (m.group(2) != null) {
// bounds are saved as tagged value in param
buildTaggedValue(param,
m.group(2).trim(),
new String[]{m.group(3)});
}
Model.getCoreHelper()
.addTemplateParameter(modelElement, templateParameter);
}
}
}
}
/**
* Common code used by addClass and addInterface.
*
* @param newClassifier Supply one if none is found in the model.
* @param name Name of the classifier.
* @param modifiers String of modifiers.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param typeParameters List of types for parameters (not implemented)
* @return The newly created/found classifier.
*/
private Object addClassifier(Object newClassifier, String name,
short modifiers, String javadoc, List<String> typeParameters) {
Object mClassifier;
Object mNamespace;
if (parseState.getClassifier() != null) {
// the new classifier is a java inner class
mClassifier = Model.getFacade().lookupIn(
parseState.getClassifier(), name);
mNamespace = parseState.getClassifier();
} else {
// the new classifier is a top level java class
parseState.outerClassifier();
mClassifier = Model.getFacade().lookupIn(currentPackage, name);
mNamespace = currentPackage;
}
if (mClassifier == null) {
// if the classifier could not be found in the model
LOG.log(Level.INFO, "Created new classifier for {0}", name);
mClassifier = newClassifier;
Model.getCoreHelper().setName(mClassifier, name);
Model.getCoreHelper().setNamespace(mClassifier, mNamespace);
newElements.add(mClassifier);
} else {
// it was found and we delete any existing tagged values.
LOG.log(Level.INFO, "Found existing classifier for {0}", name);
// TODO: Rewrite existing elements instead? - tfm
cleanModelElement(mClassifier);
}
parseState.innerClassifier(mClassifier);
// set up the artifact manifestation (only for top level classes)
if (parseState.getClassifier() == null) {
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
// set the classifier to be a resident in its component:
// (before we push a new parse state on the stack)
// This test is carried over from a previous implementation,
// but I'm not sure why it would already be set - tfm
if (Model.getFacade()
.getElementResidences(mClassifier).isEmpty()) {
Object resident = Model.getCoreFactory()
.createElementResidence();
Model.getCoreHelper().setResident(resident, mClassifier);
Model.getCoreHelper().setContainer(resident,
parseState.getArtifact());
}
} else {
Object artifact = parseState.getArtifact();
Collection c =
Model.getCoreHelper().getUtilizedElements(artifact);
if (!c.contains(mClassifier)) {
Object manifestation = Model.getCoreFactory()
.buildManifestation(mClassifier);
Model.getCoreHelper()
.addManifestation(artifact, manifestation);
}
}
}
// change the parse state to a classifier parse state
parseStateStack.push(parseState);
parseState = new ParseState(parseState, mClassifier, currentPackage);
setVisibility(mClassifier, modifiers);
// Add classifier documentation tags during first (or only) pass only
if (getLevel() <= 0) {
addDocumentationTag(mClassifier, javadoc);
}
addTypeParameters(mClassifier, typeParameters);
return mClassifier;
}
/**
* Return the current import pass/level.
*
* @return 0, 1, or 2 depending on current import level and pass of
* processing. Returns -1 if level isn't defined.
*/
private int getLevel() {
Object level = this.getAttribute("level");
if (level != null) {
return ((Integer) level).intValue();
}
return -1;
}
/**
* Called from the parser when a classifier is completely parsed.
*/
public void popClassifier() {
// Remove operations and attributes not in source
parseState.removeObsoleteFeatures();
// Remove inner classes not in source
parseState.removeObsoleteInnerClasses();
parseState = parseStateStack.pop();
}
/**
* Add an Operation to the current model
*
* @param modifiers A sequence of operation modifiers.
* @param returnType The return type of the operation.
* @param name The name of the operation as a string
* @param parameters A List of parameter declarations containing types and
* names.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @return The operation.
*/
public Object addOperation(short modifiers, String returnType, String name,
List<ParameterDeclaration> parameters, String javadoc) {
return addOperation(modifiers, EMPTY_STRING_LIST, returnType, name,
parameters, javadoc, false);
}
/**
* Called from the parser when an operation is found.
*
* @param modifiers A sequence of operation modifiers.
* @param returnType The return type of the operation.
* @param name The name of the operation as a string
* @param parameters A number of lists, each representing a parameter.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
* @return The operation.
*/
Object addOperation(short modifiers, List<String> typeParameters,
String returnType, String name,
List<ParameterDeclaration> parameters, String javadoc,
boolean forceIt) {
if (typeParameters != null && typeParameters.size() > 0) {
logError("type parameters not supported on operation return type",
name);
}
Object mOperation = getOperation(name);
parseState.feature(mOperation);
Model.getCoreHelper().setAbstract(mOperation,
(modifiers & JavaParser.ACC_ABSTRACT) > 0);
Model.getCoreHelper().setLeaf(mOperation,
(modifiers & JavaParser.ACC_FINAL) > 0);
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper().setRoot(mOperation, false);
}
setOwnerScope(mOperation, modifiers);
setVisibility(mOperation, modifiers);
if ((modifiers & JavaParser.ACC_SYNCHRONIZED) > 0) {
Model.getCoreHelper().setConcurrency(mOperation,
Model.getConcurrencyKind().getGuarded());
} else if (Model.getFacade().getConcurrency(mOperation) == Model
.getConcurrencyKind().getGuarded()) {
Model.getCoreHelper().setConcurrency(mOperation,
Model.getConcurrencyKind().getSequential());
}
Object[] c = Model.getFacade().getParameters(mOperation).toArray();
for (Object parameter : c) {
Model.getCoreHelper().removeParameter(mOperation, parameter);
}
Object mParameter;
Object mClassifier = null;
if (returnType == null
|| ("void".equals(returnType) && name.equals(Model.getFacade()
.getName(parseState.getClassifier())))) {
// Constructor
Model.getCoreHelper().addStereotype(mOperation,
getStereotype(mOperation, "create", "BehavioralFeature"));
ProjectManager.getManager().updateRoots();
} else {
try {
mClassifier =
// FIXME: This can't throw away the fully qualified
// name before starting the search!
getContext(returnType).get(getClassifierName(returnType),
false, javaProfile);
} catch (ClassifierNotFoundException e) {
if (forceIt && returnType != null && model != null) {
LOG.info("Modeller.java: "
+ "forced creation of unknown classifier "
+ returnType);
String packageName = getPackageName(returnType);
String classifierName = getClassifierName(returnType);
// here, getPackage must NOT use the Java profile, because
// class creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
mClassifier = Model.getFacade().lookupIn(mPackage,
classifierName);
if (mClassifier == null) {
mClassifier = Model.getCoreFactory().buildClass(
classifierName, mPackage);
newElements.add(mClassifier);
}
} else {
warnClassifierNotFound(returnType, "operation return type");
}
}
if (mClassifier != null) {
mParameter = buildReturnParameter(mOperation, mClassifier);
}
}
for (ParameterDeclaration parameter : parameters) {
String typeName = parameter.getType();
// TODO: A type name with a trailing "..." represents
// a variable length parameter list. It can only be
// the last parameter and it gets converted to an array
// on method invocation, so perhaps we should model it that
// way (ie convert "Foo..." to "Foo[]"). - tfm - 20070329
if (typeName.endsWith("...")) {
logError("Unsupported variable length parameter list notation",
parameter.getName());
}
mClassifier = null;
try {
mClassifier = getContext(typeName).get(
getClassifierName(typeName), false, javaProfile);
} catch (ClassifierNotFoundException e) {
if (forceIt && model != null) {
LOG.info("Modeller.java: "
+ "forced creation of unknown classifier "
+ typeName);
String packageName = getPackageName(typeName);
String classifierName = getClassifierName(typeName);
// here, getPackage must NOT use the Java profile, because
// class creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
mClassifier = Model.getFacade().lookupIn(mPackage,
classifierName);
if (mClassifier == null) {
mClassifier = Model.getCoreFactory().buildClass(
classifierName, mPackage);
newElements.add(mClassifier);
}
} else {
warnClassifierNotFound(typeName, "operation params");
}
}
if (mClassifier != null) {
mParameter = buildInParameter(mOperation, mClassifier,
parameter.getName());
if (!Model.getFacade().isAClassifier(mClassifier)) {
// the type resolution failed to find a valid classifier.
logError("Modeller.java: a valid type for a parameter "
+ "could not be resolved:\n " + "In file: "
+ fileName + ", for operation: "
+ Model.getFacade().getName(mOperation)
+ ", for parameter: ", Model.getFacade().getName(
mParameter));
}
}
}
addDocumentationTag(mOperation, javadoc);
return mOperation;
}
private Object buildInParameter(Object operation, Object classifier,
String name) {
Object parameter = buildParameter(operation, classifier, name);
Model.getCoreHelper().setKind(parameter,
Model.getDirectionKind().getInParameter());
return parameter;
}
private Object buildReturnParameter(Object operation, Object classifier) {
Object parameter = buildParameter(operation, classifier, "return");
Model.getCoreHelper().setKind(parameter,
Model.getDirectionKind().getReturnParameter());
return parameter;
}
private Object buildParameter(Object operation, Object classifier,
String name) {
Object parameter = Model.getCoreFactory().buildParameter(operation,
classifier);
Model.getCoreHelper().setName(parameter, name);
return parameter;
}
/**
* Warn user that information available in input source will not be
* reflected accurately in the model.
*
* @param name name of the classifier which wasn't found
* @param operation - a string indicating what type of operation was being
* attempted
*/
private void warnClassifierNotFound(String name, String operation) {
logError("Modeller.java: a classifier (" + name
+ ") that was in the source "
+ "file could not be generated in the model ", operation);
}
/**
* Add an error message to the log to be shown to the user.
* <p>
* TODO: This currently just writes to the error log. It needs to return
* errors some place that the user can see them and deal with them. We also
* need a way to get the line and column numbers to help the user track the
* problem down.
*/
private void logError(String message, String identifier) {
LOG.warning(message + " : " + identifier);
}
/**
* Called from the parser when a class field is parsed. This can occur in a
* class block where it indicates a method body to to be added to an
* operation (An operation will have exactly one Java body) OR it can occur
* in the enum declaration (not currently supported).
*
* TODO: Support use in an enum declaration
*
* @param op An operation.
* @param body A method body.
*/
public void addBodyToOperation(Object op, String body) {
if (op == null || !Model.getFacade().isAOperation(op)) {
// This can occur if there's a class field in an enum definition
throw new ParseStateException(
"Found class body in context other than a class");
}
if (body == null || body.length() == 0) {
return;
}
Object method = getMethod(Model.getFacade().getName(op));
parseState.feature(method);
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper().setBody(
method,
Model.getDataTypesFactory().createProcedureExpression("Java",
body));
} else {
Model.getDataTypesHelper().setBody(method, body);
Model.getDataTypesHelper().setLanguage(method, "Java");
}
// Add the method to it's specification.
Model.getCoreHelper().addMethod(op, method);
// Add this method as an element to the classifier that owns
// the operation.
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper()
.addFeature(Model.getFacade().getOwner(op), method);
} else {
Model.getCoreHelper()
.addOwnedElement(Model.getFacade().getOwner(op), method);
}
}
/**
* Called from the parser when an attribute is found.
*
* @param modifiers A sequence of attribute modifiers.
* @param typeSpec The attribute's type.
* @param name The name of the attribute.
* @param initializer The initial value of the attribute.
* @param javadoc The javadoc comment. null or "" if no comment available.
*/
public void addAttribute(short modifiers, String typeSpec, String name,
String initializer, String javadoc) {
addAttribute(modifiers, typeSpec, name, initializer, javadoc, false);
}
/**
* Called from the parser when an attribute is found.
*
* @param modifiers A sequence of attribute modifiers.
* @param typeSpec The attribute's type.
* @param name The name of the attribute.
* @param initializer The initial value of the attribute.
* @param javadoc The javadoc comment. null or "" if no comment available.
* @param forceIt Force addition by creating all that's missing.
*/
void addAttribute(short modifiers, String typeSpec, String name,
String initializer, String javadoc, boolean forceIt) {
String multiplicity = "1_1";
Object mClassifier = null;
if (typeSpec != null) {
if (!arraysAsDatatype && typeSpec.indexOf('[') != -1) {
typeSpec = typeSpec.substring(0, typeSpec.indexOf('['));
multiplicity = "1_N";
}
// the attribute type
try {
// get the attribute type
mClassifier = getContext(typeSpec).get(
getClassifierName(typeSpec), false, javaProfile);
} catch (ClassifierNotFoundException e) {
if (forceIt && typeSpec != null && model != null) {
LOG.info("Modeller.java: forced creation of"
+ " unknown classifier " + typeSpec);
String packageName = getPackageName(typeSpec);
String classifierName = getClassifierName(typeSpec);
// here, getPackage must NOT use the Java profile, because
// class creation is only allowed in the user model
Object mPackage = (packageName.length() > 0) ? getPackage(
packageName, false) : model;
// a last chance: maybe it's in this user model package:
mClassifier = Model.getFacade().lookupIn(mPackage,
classifierName);
if (mClassifier == null) {
mClassifier = Model.getCoreFactory().buildClass(
classifierName, mPackage);
newElements.add(mClassifier);
}
} else {
warnClassifierNotFound(typeSpec, "an attribute");
}
}
if (mClassifier == null) {
logError("failed to find or create type", typeSpec);
return;
}
}
// if we want to create a UML attribute:
if (mClassifier == null
|| noAssociations
|| Model.getFacade().isADataType(mClassifier)
|| (Model.getFacade().getNamespace(mClassifier) == getPackage(
JAVA_PACKAGE, true))) {
Object mAttribute = parseState.getAttribute(name);
if (mAttribute == null) {
mAttribute = buildAttribute(parseState.getClassifier(),
mClassifier, name);
}
parseState.feature(mAttribute);
setOwnerScope(mAttribute, modifiers);
setVisibility(mAttribute, modifiers);
Model.getCoreHelper().setMultiplicity(mAttribute, multiplicity);
if (Model.getFacade().isAClassifier(mClassifier)) {
// TODO: This should already have been done in buildAttribute
Model.getCoreHelper().setType(mAttribute, mClassifier);
} else {
// the type resolution failed to find a valid classifier.
logError("Modeller.java: a valid type for a parameter "
+ "could not be resolved:\n " + "In file: " + fileName
+ ", for attribute: ", Model.getFacade().getName(
mAttribute));
}
// Set the initial value for the attribute.
if (initializer != null) {
// we must remove line endings and tabs from the intializer
// strings, otherwise the classes will display horribly.
initializer = initializer.replace('\n', ' ');
initializer = initializer.replace('\t', ' ');
Object newInitialValue = Model.getDataTypesFactory()
.createExpression("Java", initializer);
Model.getCoreHelper().setInitialValue(mAttribute,
newInitialValue);
}
if ((modifiers & JavaParser.ACC_FINAL) > 0) {
Model.getCoreHelper().setReadOnly(mAttribute, true);
} else if (Model.getFacade().isReadOnly(mAttribute)) {
Model.getCoreHelper().setReadOnly(mAttribute, true);
}
addDocumentationTag(mAttribute, javadoc);
}
// we want to create a UML association from the java attribute
else {
Object mAssociationEnd = getAssociationEnd(name, mClassifier);
Model.getCoreHelper().setStatic(mAssociationEnd,
(modifiers & JavaParser.ACC_STATIC) > 0);
setVisibility(mAssociationEnd, modifiers);
Model.getCoreHelper()
.setMultiplicity(mAssociationEnd, multiplicity);
Model.getCoreHelper().setType(mAssociationEnd, mClassifier);
Model.getCoreHelper().setName(mAssociationEnd, name);
if ((modifiers & JavaParser.ACC_FINAL) > 0) {
Model.getCoreHelper().setReadOnly(mAssociationEnd, true);
}
if (!mClassifier.equals(parseState.getClassifier())) {
// Because if they are equal,
// then getAssociationEnd(name, mClassifier) could return
// the wrong assoc end, on the other hand the navigability
// is already set correctly (at least in this case), so the
// next line is not necessary. (maybe never necessary?) - thn
Model.getCoreHelper().setNavigable(mAssociationEnd, true);
}
addDocumentationTag(mAssociationEnd, javadoc);
}
}
/**
* Find a generalization in the model. If it does not exist, a new
* generalization is created.
*
* @param mPackage Look in this package.
* @param parent The superclass.
* @param child The subclass.
* @return The generalization found or created.
*/
private Object getGeneralization(Object mPackage, Object parent,
Object child) {
Object mGeneralization = Model.getFacade().getGeneralization(child,
parent);
if (mGeneralization == null) {
mGeneralization = Model.getCoreFactory().buildGeneralization(child,
parent);
newElements.add(mGeneralization);
}
if (mGeneralization != null
&& Model.getFacade().getUmlVersion().charAt(0) == '1') {
Model.getCoreHelper().setNamespace(mGeneralization, mPackage);
}
return mGeneralization;
}
/**
* Find an abstraction<<realize>> in the model. If it does not exist, a new
* abstraction is created.
*
* @param parent The superclass.
* @param child The subclass.
* @return The abstraction found or created.
*/
private Object getAbstraction(Object parent, Object child) {
Object mAbstraction = null;
for (Iterator i = Model.getFacade().getClientDependencies(child)
.iterator(); i.hasNext();) {
mAbstraction = i.next();
Collection c = Model.getFacade().getSuppliers(mAbstraction);
if (c == null || c.size() == 0) {
Model.getCoreHelper().removeClientDependency(child,
mAbstraction);
} else {
if (parent != c.toArray()[0]) {
mAbstraction = null;
} else {
break;
}
}
}
if (mAbstraction == null) {
mAbstraction = Model.getCoreFactory().buildAbstraction(
makeAbstractionName(child, parent), parent, child);
newElements.add(mAbstraction);
}
return mAbstraction;
}
/**
* Find a class in a package. If it does not exist, a new class is
* created.
*
* @param mPackage Look in this package.
* @param name The name of the class.
* @return The class found or created.
*/
private Object getClass(Object mPackage, String name) {
Object mClass = null;
for (Object c : Model.getCoreHelper().getAllClasses(mPackage)) {
if (name.equals(Model.getFacade().getName(c))) {
mClass = c;
break;
}
}
if (mClass == null) {
mClass = Model.getCoreFactory().buildClass(name, mPackage);
newElements.add(mClass);
}
return mClass;
}
/**
* Find a package in the project. If it does not exist, a new package is
* created in the user model.
*
* @param name The name of the package.
* @param useProfile also look in the Java profile if true
* @return The package found or created.
*/
private Object getPackage(String name, boolean useProfile) {
Object mPackage = searchPackageInModel(name, useProfile);
if (mPackage == null) {
// whole or part of the package path need to be built in model:
Object currentNs = model;
StringTokenizer st = new StringTokenizer(name, ".");
while (st.hasMoreTokens()) {
String rname = st.nextToken();
mPackage = Model.getFacade().lookupIn(currentNs, rname);
// the actual package might already exist in the user model
if (mPackage == null
|| !Model.getFacade().isAPackage(mPackage)) {
mPackage = Model.getModelManagementFactory().buildPackage(
getRelativePackageName(rname));
// set the owner for this package.
Model.getCoreHelper().addOwnedElement(currentNs, mPackage);
newElements.add(mPackage);
}
currentNs = mPackage;
}
}
return mPackage;
}
/**
* Search recursively for nested packages in the user model. So if you pass
* a package org.argouml.kernel , this method searches for a package kernel,
* that is owned by a package argouml, which is owned by a package org. This
* method is required to nest the parsed packages. It optionally first
* searches in the Java profile.
*
* @param name The fully qualified package name of the package we are
* searching for.
* @param useProfile first have a look in the Java profile if true
* @return The found package or null, if it is not in the model.
*/
private Object searchPackageInModel(String name, boolean useProfile) {
Object ret = null;
if ("".equals(getPackageName(name))) {
if (useProfile && javaProfile != null) {
try {
Object m = javaProfile.getProfilePackages().iterator()
.next();
ret = Model.getFacade().lookupIn(m, name);
} catch (Exception e) {
ret = null;
}
}
if (ret == null) {
ret = Model.getFacade().lookupIn(model, name);
}
return ret;
}
Object owner = searchPackageInModel(getPackageName(name), useProfile);
return owner == null ? null : Model.getFacade().lookupIn(owner,
getRelativePackageName(name));
}
/**
* Find an operation in the currentClassifier. If the operation is not
* found, a new is created.
*
* @param name The name of the operation.
* @return The operation found or created.
*/
private Object getOperation(String name) {
Object mOperation = parseState.getOperation(name);
if (mOperation != null) {
LOG.info("Getting the existing operation " + name);
} else {
LOG.info("Creating a new operation " + name);
Object cls = parseState.getClassifier();
Object returnType = ProjectManager.getManager().getCurrentProject()
.getDefaultReturnType();
mOperation = Model.getCoreFactory().buildOperation2(cls,
returnType, name);
newElements.add(mOperation);
}
return mOperation;
}
/**
* Find an operation in the currentClassifier. If the operation is not
* found, a new is created.
*
* @param name The name of the method.
* @return The method found or created.
*/
private Object getMethod(String name) {
Object method = parseState.getMethod(name);
if (method != null) {
LOG.info("Getting the existing method " + name);
} else {
LOG.info("Creating a new method " + name);
method = Model.getCoreFactory().buildMethod(name);
newElements.add(method);
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
// Is this done twice (see caller of this method)?
Model.getCoreHelper()
.addFeature(parseState.getClassifier(), method);
}
}
return method;
}
/**
* Build a new attribute in the current classifier.
*
* @param classifier the model were are reverse engineering into
* @param type the the type of the new attribute
* @param name The name of the attribute.
* @return The attribute found or created.
*/
private Object buildAttribute(Object classifier, Object type, String name) {
Object mAttribute = Model.getCoreFactory().buildAttribute2(classifier,
type);
newElements.add(mAttribute);
Model.getCoreHelper().setName(mAttribute, name);
return mAttribute;
}
/**
* Find an associationEnd for a binary Association from the
* currentClassifier to the type specified. If not found, a new is created.
*
* @param name The name of the attribute.
* @param mClassifier Where the association ends.
* @return The attribute found or created.
*/
private Object getAssociationEnd(String name, Object mClassifier) {
Object mAssociationEnd = null;
for (Iterator i = Model.getFacade().getAssociationEnds(mClassifier)
.iterator(); i.hasNext();) {
Object ae = i.next();
Object assoc = Model.getFacade().getAssociation(ae);
if (name.equals(Model.getFacade().getName(ae))
&& Model.getFacade().getConnections(assoc).size() == 2
&& Model.getFacade().getType(
Model.getFacade().getNextEnd(ae)) == parseState
.getClassifier()) {
mAssociationEnd = ae;
}
}
if (mAssociationEnd == null && !noAssociations) {
String newName = makeAssociationName(parseState.getClassifier(),
mClassifier);
Object mAssociation = buildDirectedAssociation(newName, parseState
.getClassifier(), mClassifier);
// this causes a problem when mClassifier is not only
// at one assoc end: (which one is the right one?)
mAssociationEnd = Model.getFacade().getAssociationEnd(mClassifier,
mAssociation);
}
return mAssociationEnd;
}
/**
* Build a unidirectional association between two Classifiers.
*
* @param name name of the association
* @param sourceClassifier source classifier (end which is non-navigable)
* @param destClassifier destination classifier (end which is navigable)
* @return newly created Association
*/
public static Object buildDirectedAssociation(String name,
Object sourceClassifier, Object destClassifier) {
return Model.getCoreFactory().buildAssociation(destClassifier, true,
sourceClassifier, false, name);
}
/**
* Get the stereotype with a specific name. UML 1.x only.
*
* @param name The name of the stereotype.
* @return The stereotype.
*/
private Object getUML1Stereotype(String name) {
LOG.fine("Trying to find a stereotype of name <<" + name + ">>");
// Is this line really safe wouldn't it just return the first
// model element of the same name whether or not it is a stereotype
Object stereotype = Model.getFacade().lookupIn(model, name);
if (stereotype == null) {
LOG.fine("Couldn't find so creating it");
return Model.getExtensionMechanismsFactory().buildStereotype(name,
model);
}
if (!Model.getFacade().isAStereotype(stereotype)) {
// and so this piece of code may create an existing stereotype
// in error.
LOG.fine("Found something that isn't a stereotype so creating it");
return Model.getExtensionMechanismsFactory().buildStereotype(name,
model);
}
LOG.fine("Found it");
return stereotype;
}
/**
* Find the first suitable stereotype with baseclass for a given object.
*
* @param me
* @param name
* @param baseClass
* @return the stereotype if found
*
* @throws IllegalArgumentException if the desired stereotypes for the
* modelelement and baseclass was not found and could not be
* created. No stereotype is created.
*/
private Object getStereotype(Object me, String name, String baseClass) {
Collection models = ProjectManager.getManager().getCurrentProject()
.getModels();
Collection stereos = Model.getExtensionMechanismsHelper()
.getAllPossibleStereotypes(models, me);
Object stereotype = null;
if (stereos != null && stereos.size() > 0) {
Iterator iter = stereos.iterator();
while (iter.hasNext()) {
stereotype = iter.next();
if (Model.getExtensionMechanismsHelper().isStereotypeInh(
stereotype, name, baseClass)) {
LOG.info("Returning the existing stereotype of <<"
+ Model.getFacade().getName(stereotype) + ">>");
return stereotype;
}
}
}
if (Model.getFacade().getUmlVersion().charAt(0) != '1') {
// For UML2, we must fail now, because stereotypes must be found
// only in profiles.
throw new IllegalArgumentException("Could not find "
+ "a suitable stereotype for " + Model.getFacade().getName(me)
+ " - stereotype: <<" + name + ">> base: " + baseClass
+ ".\n"
+ "Check if environment variable eUML.resources "
+ "is correctly set.");
}
// (UML 1.x only from here)
// Instead of failing, this should create any stereotypes that it
// requires. Most likely cause of failure is that the stereotype isn't
// included in the profile that is being used. - tfm 20060224
stereotype = getUML1Stereotype(name);
if (stereotype != null) {
Model.getExtensionMechanismsHelper().addBaseClass(stereotype, me);
return stereotype;
}
// This should never happen then:
throw new IllegalArgumentException("Could not find "
+ "a suitable stereotype for " + Model.getFacade().getName(me)
+ " - stereotype: <<" + name + ">> base: " + baseClass);
}
/**
* Return the tagged value with a specific tag.
*
* @param element The tagged value belongs to this.
* @param name The tag.
* @return The found tag. A new is created if not found.
*/
private Object getTaggedValue(Object element, String name) {
Object tv = Model.getFacade().getTaggedValue(element, name);
if (tv == null) {
String[] empties = {
""
};
buildTaggedValue(element, name, empties);
tv = Model.getFacade().getTaggedValue(element, name);
}
return tv;
}
/**
* This classifier was earlier generated by reference but now it is its time
* to be parsed so we clean out remnants.
*
* @param element that they are removed from
*/
private void cleanModelElement(Object element) {
Object tv = Model.getFacade().getTaggedValue(element,
Facade.GENERATED_TAG);
while (tv != null) {
Model.getUmlFactory().delete(tv);
tv = Model.getFacade()
.getTaggedValue(element, Facade.GENERATED_TAG);
}
}
/**
* Get the package name from a fully specified classifier name.
*
* @param name A fully specified classifier name.
* @return The package name.
*/
private String getPackageName(String name) {
name = stripVarargAndGenerics(name);
int lastDot = name.lastIndexOf('.');
if (lastDot == -1) {
return "";
}
String pkgName = name.substring(0, lastDot);
// If the last element begins with an uppercase character, assume
// that we've really got a class, not a package.
// TODO: A better strategy would be to defer until we can disambiguate
if (Character.isUpperCase(getRelativePackageName(pkgName).charAt(0))) {
return getPackageName(pkgName);
} else {
return pkgName;
}
}
/**
* Get the relative package name from a fully qualified package name. So if
* the parameter is 'org.argouml.kernel' the method is supposed to return
* 'kernel' (the package kernel is in package 'org.argouml').
*
* @param packageName A fully qualified package name.
* @return The relative package name.
*/
private String getRelativePackageName(String packageName) {
// Since the relative package name corresponds
// to the classifier name of a fully qualified
// classifier, we simply use this method.
// TODO: This won't correctly identify the package for an inner class
// e.g. package.Foo.Bar, but getPackageName() makes an attempt to
// guess correctly
return getClassifierName(packageName);
}
/**
* Get the classifier name from a fully specified classifier name.
* <p>
* FIXME: Most uses of this method are wrong. We should be adding context
* such as package names or outer classifier names, not removing it, before
* doing lookup so that the search methods have the fully qualified name to
* work with.
*
* @param name A fully specified classifier name.
* @return The classifier name.
*/
private String getClassifierName(String name) {
name = stripVarargAndGenerics(name);
int lastDot = name.lastIndexOf('.');
if (lastDot == -1) {
return name;
}
return name.substring(lastDot + 1);
}
/**
* Set the visibility for a model element.
*
* @param element The model element.
* @param modifiers A sequence of modifiers which may contain 'private',
* 'protected' or 'public'.
*/
private void setVisibility(Object element, short modifiers) {
if ((modifiers & JavaParser.ACC_PRIVATE) > 0) {
Model.getCoreHelper().setVisibility(element,
Model.getVisibilityKind().getPrivate());
} else if ((modifiers & JavaParser.ACC_PROTECTED) > 0) {
Model.getCoreHelper().setVisibility(element,
Model.getVisibilityKind().getProtected());
} else if ((modifiers & JavaParser.ACC_PUBLIC) > 0) {
Model.getCoreHelper().setVisibility(element,
Model.getVisibilityKind().getPublic());
} else {
// Default Java visibility is "package"
Model.getCoreHelper().setVisibility(element,
Model.getVisibilityKind().getPackage());
}
}
/**
* Set the owner scope for a feature.
*
* @param feature The feature.
* @param modifiers A sequence of modifiers which may contain 'static'.
*/
private void setOwnerScope(Object feature, short modifiers) {
Model.getCoreHelper().setStatic(feature,
(modifiers & JavaParser.ACC_STATIC) > 0);
}
/**
* Get the context for a classifier name that may or may not be fully
* qualified. The context contains either the user model, or a package
* or class inside the user model, or a package or class in the Java
* profile.
*
* @param name the classifier name
* @return the context
*/
private Context getContext(String name) {
Context context = parseState.getContext();
String packageName = getPackageName(name);
Object pkg = model;
if (!"".equals(packageName)) {
pkg = getPackage(packageName, true);
}
String classifierName = name.substring(packageName.length());
if (classifierName.charAt(0) == '.') {
classifierName = classifierName.substring(1);
}
classifierName = stripVarargAndGenerics(classifierName);
int lastDot = classifierName.lastIndexOf('.');
if (lastDot != -1) {
String clsName = classifierName.substring(0, lastDot);
Object cls = getClass(pkg, clsName);
context =
new OuterClassifierContext(
context.getContext(), cls, pkg,
clsName + '$');
} else if (!"".equals(packageName)) {
context = new PackageContext(context, pkg);
}
return context;
}
/**
* Add the contents of a single standard javadoc tag to the model element.
* Usually this will be added as a tagged value.
*
* This is called from {@link #addDocumentationTag} only.
*
* @param me the model element to add to
* @param sTagName the name of the javadoc tag
* @param sTagData the contents of the javadoc tag
*/
private void addJavadocTagContents(Object me, String sTagName,
String[] sTagData) {
if (sTagData != null
&& (sTagData.length == 0 || sTagData[0] == null)) {
LOG.fine("Called addJavadocTagContents with no tag data!");
return;
}
int colonPos = (sTagData != null) ? sTagData[0].indexOf(':') : -1;
if (colonPos != -1
&& (("invariant".equals(sTagName))
|| ("pre-condition".equals(sTagName))
|| ("post-condition".equals(sTagName)))) {
// add as OCL constraint
String sContext = OCLUtil.getContextString(me);
String name = sTagData[0].substring(0, colonPos);
String body = null;
if (sTagName.equals("invariant")) {
// add as invariant constraint Note that no checking
// of constraint syntax is performed... BAD!
body = sContext + " inv " + sTagData;
} else if (sTagName.equals("pre-condition")) {
body = sContext + " pre " + sTagData;
} else {
body = sContext + " post " + sTagData;
}
Object bexpr = Model.getDataTypesFactory().createBooleanExpression(
"OCL", body);
Object mc = Model.getCoreFactory().buildConstraint(name, bexpr);
Model.getCoreHelper().addConstraint(me, mc);
if (Model.getFacade().getNamespace(me) != null) {
// Apparently namespace management is not supported
// for all model elements. As this does not seem to
// cause problems, I'll just leave it at that for the
// moment...
Model.getCoreHelper().addOwnedElement(
Model.getFacade().getNamespace(me), mc);
}
} else {
if ("stereotype".equals(sTagName)) {
// multiple stereotype support:
// make one stereotype tag from many stereotype tags
Object tv = getTaggedValue(me, sTagName);
if (tv != null) {
String sStereotype = Model.getFacade().getValueOfTag(tv);
if (sStereotype != null && sStereotype.length() > 0) {
sTagData[0] = sStereotype + ',' + sTagData[0];
}
}
// now eliminate multiple entries in that comma separated list
HashSet<String> stSet = new HashSet<String>();
StringTokenizer st = new StringTokenizer(sTagData[0], ", ");
while (st.hasMoreTokens()) {
stSet.add(st.nextToken().trim());
}
StringBuffer sb = new StringBuffer();
Iterator<String> iter = stSet.iterator();
while (iter.hasNext()) {
if (sb.length() > 0) {
sb.append(',');
}
sb.append(iter.next());
}
sTagData[0] = sb.toString();
}
buildTaggedValue(me, sTagName, sTagData);
}
}
private void buildTaggedValue(Object me,
String sTagName,
String[] sTagData) {
Object tv = Model.getFacade().getTaggedValue(me, sTagName);
if (tv == null) {
// using deprecated buildTaggedValue here, because getting the tag
// definition from a tag name is the critical step, and this is
// implemented in ExtensionMechanismsFactory in a central place,
// but not as a public method:
Model.getExtensionMechanismsHelper().addTaggedValue(
me,
Model.getExtensionMechanismsFactory()
.buildTaggedValue(sTagName, sTagData[0]));
} else {
Model.getExtensionMechanismsHelper().setDataValues(tv, sTagData);
}
}
/**
* Add the javadocs as a tagged value 'documentation' to the model element.
* All comment delimiters are removed prior to adding the comment.
*
* Added 2001-10-05 STEFFEN ZSCHALER.
*
* @param modelElement the model element to which to add the documentation
* @param sJavaDocs the documentation comments to add ("" or null if no java
* docs)
*/
private void addDocumentationTag(Object modelElement, String sJavaDocs) {
if ((sJavaDocs != null) && (sJavaDocs.trim().length() >= 5)) {
StringBuffer sbPureDocs = new StringBuffer(80);
String sCurrentTagName = null;
String[] sCurrentTagData = {
null
};
int nStartPos = 3; // skip the leading /**
boolean fHadAsterisk = true;
while (nStartPos < sJavaDocs.length()) {
switch (sJavaDocs.charAt(nStartPos)) {
case '*':
fHadAsterisk = true;
nStartPos++;
break;
case ' ': // all white space, hope I didn't miss any ;-)
case '\t':
// ignore white space before the first asterisk
if (!fHadAsterisk) {
nStartPos++;
break;
}
default:
// normal comment text or standard tag
// check ahead for tag
int j = nStartPos;
while ((j < sJavaDocs.length())
&& ((sJavaDocs.charAt(j) == ' ') || (sJavaDocs
.charAt(j) == '\t'))) {
j++;
}
if (j < sJavaDocs.length()) {
if (sJavaDocs.charAt(j) == '@') {
// if the last javadoc is on the last line
// no new line will be found, causing an
// indexoutofboundexception.
int lineEndPos = 0;
if (sJavaDocs.indexOf('\n', j) < 0) {
lineEndPos = sJavaDocs.length() - 2;
} else {
lineEndPos = sJavaDocs.indexOf('\n', j) + 1;
}
sbPureDocs.append(sJavaDocs
.substring(j, lineEndPos));
// start standard tag potentially add
// current tag to set of tagged values...
if (sCurrentTagName != null) {
addJavadocTagContents(modelElement,
sCurrentTagName, sCurrentTagData);
}
// open new tag
int nTemp = sJavaDocs.indexOf(' ', j + 1);
if (nTemp == -1) {
nTemp = sJavaDocs.length() - 1;
}
sCurrentTagName = sJavaDocs.substring(j + 1, nTemp);
int nTemp1 = sJavaDocs.indexOf('\n', ++nTemp);
if (nTemp1 == -1) {
nTemp1 = sJavaDocs.length();
} else {
nTemp1++;
}
sCurrentTagData[0] = sJavaDocs.substring(nTemp,
nTemp1);
nStartPos = nTemp1;
} else {
// continue standard tag or comment text
int nTemp = sJavaDocs.indexOf('\n', nStartPos);
if (nTemp == -1) {
nTemp = sJavaDocs.length();
} else {
nTemp++;
}
if (sCurrentTagName != null) {
sbPureDocs.append(sJavaDocs.substring(
nStartPos, nTemp));
sCurrentTagData[0] += " "
+ sJavaDocs.substring(nStartPos, nTemp);
} else {
sbPureDocs.append(sJavaDocs.substring(
nStartPos, nTemp));
}
nStartPos = nTemp;
}
}
fHadAsterisk = false;
}
}
sJavaDocs = sbPureDocs.toString();
/*
* After this, we have the documentation text, but there's still a
* trailing '/' left, either at the end of the actual comment text
* or at the end of the last tag.
*/
sJavaDocs = removeTrailingSlash(sJavaDocs);
// handle last tag, if any (strip trailing slash there too)
if (sCurrentTagName != null) {
sCurrentTagData[0] = removeTrailingSlash(sCurrentTagData[0]);
addJavadocTagContents(modelElement, sCurrentTagName,
sCurrentTagData);
}
// Now store documentation text in a tagged value
String[] javadocs = {
sJavaDocs
};
buildTaggedValue(modelElement, Argo.DOCUMENTATION_TAG, javadocs);
addStereotypes(modelElement);
}
}
/*
* Remove a trailing slash, including the entire line if it's the only thing
* on the line.
*/
private String removeTrailingSlash(String s) {
if (s.endsWith("\n/")) {
return s.substring(0, s.length() - 2);
} else if (s.endsWith("*/")) {
// need if end comment in the same line than comment
return s.substring(0, s.length() - 2);
} else if (s.endsWith("/")) {
return s.substring(0, s.length() - 1);
} else {
return s;
}
}
/*
* Remove information that currently is not handled.
* TODO: Handle them instead.
*/
private String stripVarargAndGenerics(String name) {
if (name != null) {
if (name.endsWith("...")) {
// handle vararg
name = name.substring(0, name.length() - 3);
}
if (name.endsWith(">")) {
// handle generics
int i = name.length() - 2;
int cnt = 1;
while (i >= 0 && cnt > 0) {
if (name.charAt(i) == '<') {
cnt--;
} else if (name.charAt(i) == '>') {
cnt++;
}
i--;
}
name = name.substring(0, i + 1);
}
}
return name;
}
/*
* If there is a tagged value named 'stereotype', make it a real stereotype
* and remove the tagged value. We allow multiple instances of this tagged
* value AND parse a single instance for multiple stereotypes
*/
private void addStereotypes(Object modelElement) {
// TODO: What we do here is allowed for UML 1.x only!
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
Object tv = Model.getFacade()
.getTaggedValue(modelElement, "stereotype");
if (tv != null) {
String stereo = Model.getFacade().getValueOfTag(tv);
if (stereo != null && stereo.length() > 0) {
StringTokenizer st = new StringTokenizer(stereo, ", ");
while (st.hasMoreTokens()) {
Model.getCoreHelper().addStereotype(modelElement,
getUML1Stereotype(st.nextToken().trim()));
}
ProjectManager.getManager().updateRoots();
}
Model.getUmlFactory().delete(tv);
}
}
}
/**
* Manage collection of parsed method calls. Used for reverse engineering of
* interactions.
*/
/**
* Add a parsed method call to the collection of method calls.
*
* @param methodName The method name called.
*/
public void addCall(String methodName) {
methodCalls.add(methodName);
}
/**
* Get collection of method calls.
*
* @return list containing collected method calls
*/
public synchronized List<String> getMethodCalls() {
return methodCalls;
}
/**
* Clear collected method calls.
*/
public void clearMethodCalls() {
methodCalls.clear();
}
/**
* Add a local variable declaration to the list of variables.
*
* @param type type of declared variable
* @param name name of declared variable
*/
public void addLocalVariableDeclaration(String type, String name) {
localVariables.put(name, type);
}
/**
* Return the collected set of local variable declarations.
*
* This is read from
* {@link org.argouml.language.java.ui.RESequenceDiagramDialog#parseBody()}
*
*
* @return hash table containing all local variable declarations.
*/
public Map<String, String> getLocalVariableDeclarations() {
return Collections.unmodifiableMap(localVariables);
}
/**
* Clear the set of local variable declarations.
*/
public void clearLocalVariableDeclarations() {
localVariables.clear();
}
/**
* Get the elements which were created while reverse engineering this file.
*
* @return the collection of elements
*/
public Collection<Object> getNewElements() {
return newElements;
}
/**
* Set flag that controls name generation. Artificial names are generated by
* default for historical reasons, but in most cases they are just clutter.
*
* @param generateNamesFlag true to generate artificial names of the form
* "From->To" for Associations, Dependencies, etc.
*/
public void setGenerateNames(boolean generateNamesFlag) {
generateNames = generateNamesFlag;
}
}