GeneratorJava.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
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-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.generator;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.argouml.application.api.Argo;
import org.argouml.application.helpers.ResourceLoaderWrapper;
import org.argouml.configuration.Configuration;
import org.argouml.language.java.JavaModuleGlobals;
import org.argouml.model.Model;
import org.argouml.moduleloader.ModuleInterface;
import org.argouml.ocl.ArgoFacade;
import org.argouml.uml.DocumentationManager;
import org.argouml.uml.generator.CodeGenerator;
import org.argouml.uml.generator.GeneratorHelper;
import org.argouml.uml.generator.GeneratorManager;
import org.argouml.uml.generator.Language;
import org.argouml.uml.generator.TempFileUtils;
import org.argouml.uml.reveng.ImportInterface;
import tudresden.ocl.OclTree;
import tudresden.ocl.parser.analysis.DepthFirstAdapter;
import tudresden.ocl.parser.node.AConstraintBody;
/**
* FileGenerator implementing class to generate Java for display in diagrams and
* in text fields in the ArgoUML user interface.
*/
public class GeneratorJava implements CodeGenerator, ModuleInterface {
/**
* Logger.
*/
private static final Logger LOG =
Logger.getLogger(GeneratorJava.class.getName());
private boolean verboseDocs;
private boolean lfBeforeCurly;
private static final boolean VERBOSE_DOCS = false;
private static final String LINE_SEPARATOR = System
.getProperty("line.separator");
private static final String LANG_PACKAGE = "java.lang";
private static final Set<String> JAVA_TYPES;
static {
Set<String> types = new HashSet<String>();
types.add("void");
types.add("boolean");
types.add("byte");
types.add("char");
types.add("int");
types.add("short");
types.add("long");
types.add("float");
types.add("double");
JAVA_TYPES = Collections.unmodifiableSet(types);
}
// TODO: make it configurable
// next two flags shows in what mode we are working
/**
* <code>true</code> when GenerateFile.
*/
private static boolean isFileGeneration;
/**
* <code>true</code> if GenerateFile in Update Mode.
*/
private static boolean isInUpdateMode;
/**
* Two spaces used for indenting code in classes.
*/
private static final String INDENT = " ";
/**
* The Language instance.
*/
private static Language java = GeneratorHelper.makeLanguage("Java", "Java",
ResourceLoaderWrapper.lookupIconResource("JavaNotation"));
/**
* Generates a file for the classifier. This method could have been static
* if it where not for the need to call it through the Generatorinterface.
* Returns the full path name of the the generated file or null if no file
* can be generated.
*
* @param modelElement the element to be generated
* @param path the path where the element will be generated
* @return String full path name of the the generated file
*/
private String generateFile(Object modelElement, String path) {
String name = Model.getFacade().getName(modelElement);
if (name == null || name.length() == 0) {
return null;
}
Object classifier = modelElement;
String filename = name + ".java";
StringBuilder sbPath = new StringBuilder(path);
if (!path.endsWith(FILE_SEPARATOR)) {
sbPath.append(FILE_SEPARATOR);
}
String packagePath = getPackageName(Model.getFacade().getNamespace(
classifier));
int lastIndex = -1;
do {
File f = new File(sbPath.toString());
if (!f.isDirectory() && !f.mkdir()) {
LOG.severe(" could not make directory " + path);
return null;
}
if (lastIndex == packagePath.length()) {
break;
}
int index = packagePath.indexOf(".", lastIndex + 1);
if (index == -1) {
index = packagePath.length();
}
sbPath.append(packagePath.substring(lastIndex + 1, index)
+ FILE_SEPARATOR);
lastIndex = index;
} while (true);
String pathname = sbPath.toString() + filename;
// cat.info("-----" + pathname + "-----");
// now decide whether file exist and need an update or is to be
// newly generated
File f = new File(pathname);
isFileGeneration = true; // used to produce method javadoc
// String pathname = path + filename;
// TODO: package, project basepath, tagged values to configure
LOG.info("Generating " + f.getPath());
isFileGeneration = true;
String header = generateHeader(classifier, pathname, packagePath);
String src = generateClassifier(classifier);
BufferedWriter fos = null;
try {
if (Configuration.getString(Argo.KEY_INPUT_SOURCE_ENCODING) == null
|| Configuration.getString(Argo.KEY_INPUT_SOURCE_ENCODING)
.trim().equals("")) {
fos = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(f),
System.getProperty("file.encoding")));
} else {
fos = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(f),
Configuration.getString(
Argo.KEY_INPUT_SOURCE_ENCODING)));
}
fos.write(header);
fos.write(src);
} catch (IOException exp) {
LOG.severe("IO Exception: " + exp + ", for file: " + f.getPath());
} finally {
isFileGeneration = false;
try {
if (fos != null) {
fos.close();
}
} catch (IOException exp) {
LOG.severe("FAILED: " + f.getPath());
}
}
// cat.info("----- end updating -----");
return pathname;
}
private String generateHeader(Object cls, String pathname,
String packagePath) {
StringBuffer sb = new StringBuffer(80);
// TODO: add user-defined copyright
if (VERBOSE_DOCS) {
sb.append("// FILE: ").append(pathname.replace('\\', '/'));
sb.append(LINE_SEPARATOR).append(LINE_SEPARATOR);
}
if (packagePath.length() > 0) {
sb.append("package ").append(packagePath).append(";");
sb.append(LINE_SEPARATOR).append(LINE_SEPARATOR);
}
sb.append(generateImports(cls, packagePath));
return sb.toString();
}
/**
* Generates code for some modelelement. Subclasses should implement this to
* generate code for different notations.
*
* @param o the element to be generated
* @return String the generated code
*/
private String generate(Object o) {
if (o == null) {
return "";
}
if (Model.getFacade().isAActionState(o)) {
return generateActionState(o);
}
if (Model.getFacade().isAExtensionPoint(o)) {
return generateExtensionPoint(o);
}
if (Model.getFacade().isAOperation(o)) {
return generateOperation(o, false);
}
if (Model.getFacade().isAAttribute(o)) {
return generateAttribute(o, false);
}
if (Model.getFacade().isAParameter(o)) {
return generateParameter(o);
}
if (Model.getFacade().isAPackage(o)) {
return generatePackage(o);
}
if (Model.getFacade().isAClassifier(o)) {
return generateClassifier(o);
}
if (Model.getFacade().isAExpression(o)) {
return generateExpression(o);
}
if (o instanceof String) {
return generateName((String) o);
}
if (o instanceof String) {
return generateUninterpreted((String) o);
}
if (Model.getFacade().isAStereotype(o)) {
return generateStereotype(o);
}
if (Model.getFacade().isATaggedValue(o)) {
return generateTaggedValue(o);
}
if (Model.getFacade().isAAssociationEnd(o)) {
return generateAssociationEnd(o);
}
if (Model.getFacade().isAMultiplicity(o)) {
return generateMultiplicity(o);
}
if (Model.getFacade().isAState(o)) {
return generateState(o);
}
if (Model.getFacade().isATransition(o)) {
return generateTransition(o);
}
if (Model.getFacade().isAAction(o)) {
return generateAction(o);
}
if (Model.getFacade().isACallAction(o)) {
return generateAction(o);
}
if (Model.getFacade().isAGuard(o)) {
return generateGuard(o);
}
if (Model.getFacade().isAMessage(o)) {
return generateMessage(o);
}
if (Model.getFacade().isAEvent(o)) {
return generateEvent(o);
}
if (Model.getFacade().isAVisibilityKind(o)) {
return generateVisibility(o);
}
if (Model.getFacade().isAModelElement(o)) {
return generateName(Model.getFacade().getName(o));
}
return o.toString();
}
/**
* Generates the import statements for the class
*
* @param cls The class object
* @param packagePath The package path
* @return a string containing the import statements
*/
private String generateImports(Object cls, String packagePath) {
// If the model is built by the import of Java source code, then a
// component named after the filename was created, which manages the
// import statements for all included classes/interfaces. This
// component is now searched for cls in order to extract the imports.
Object ns = Model.getFacade().getNamespace(cls);
if (ns != null) {
for (Object oe : Model.getFacade().getOwnedElements(ns)) {
if (Model.getFacade().getUmlVersion().charAt(0) == '1'
&& Model.getFacade().isAComponent(oe)) {
for (Object re
: Model.getFacade().getResidentElements(oe)) {
Object r = Model.getFacade().getResident(re);
if (r.equals(cls)) {
return generateArtifactImports(oe);
}
}
} else if (Model.getFacade().isAArtifact(oe)
&& (Model.getCoreHelper().getUtilizedElements(oe)
.contains(cls))) {
return generateArtifactImports(oe);
}
}
}
// We now have the situation that no component with package imports
// was found, so the import statements are guessed from the used model
// elements inside cls.
StringBuffer sb = new StringBuffer(80);
Set<String> importSet = new HashSet<String>();
// now check packages of all feature types
for (Object mFeature : Model.getFacade().getFeatures(cls)) {
if (Model.getFacade().isAAttribute(mFeature)) {
String ftype = generateImportType(
Model.getFacade().getType(mFeature), packagePath);
if (ftype != null) {
importSet.add(ftype);
}
} else if (Model.getFacade().isAOperation(mFeature)) {
// check the parameter types
for (Object parameter : Model.getFacade().getParameters(
mFeature)) {
String ftype = generateImportType(Model.getFacade()
.getType(parameter), packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
// check the return parameter types
for (Object parameter : Model.getCoreHelper()
.getReturnParameters(mFeature)) {
String ftype = generateImportType(Model.getFacade()
.getType(parameter), packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
// check raised signals
for (Object signal : Model.getFacade().getRaisedSignals(
mFeature)) {
if (!Model.getFacade().isAException(signal)) {
continue;
}
String ftype = generateImportType(Model.getFacade()
.getType(signal), packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
}
}
// now check generalizations
for (Object gen : Model.getFacade().getGeneralizations(cls)) {
Object parent = Model.getFacade().getGeneral(gen);
if (parent == cls) {
continue;
}
String ftype = generateImportType(parent, packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
// now check packages of the interfaces
for (Object iface : Model.getFacade().getSpecifications(cls)) {
String ftype = generateImportType(iface, packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
// check association end types
for (Object associationEnd
: Model.getFacade().getAssociationEnds(cls)) {
Object association = Model.getFacade().getAssociation(
associationEnd);
for (Object associationEnd2 : Model.getFacade().getConnections(
association)) {
if (associationEnd2 != associationEnd
&& Model.getFacade().isNavigable(associationEnd2)
&& !Model.getFacade().isAbstract(
Model.getFacade().getAssociation(
associationEnd2))) {
// association end found
if (Model.getFacade().getUpper(associationEnd2) != 1) {
importSet.add("java.util.List");
} else {
String ftype = generateImportType(Model.getFacade()
.getType(associationEnd2), packagePath);
if (ftype != null) {
importSet.add(ftype);
}
}
}
}
}
// finally generate the import statements
for (String importType : importSet) {
sb.append("import ").append(importType).append(";");
sb.append(LINE_SEPARATOR);
}
if (!importSet.isEmpty()) {
sb.append(LINE_SEPARATOR);
}
return sb.toString();
}
private String generateArtifactImports(Object artifact) {
StringBuffer ret = new StringBuffer();
Object compNamespace = Model.getFacade().getNamespace(artifact);
boolean found = false;
for (Object o : Model.getFacade().getClientDependencies(artifact)) {
boolean isJavaImport = false;
for (Object stereotype : Model.getFacade().getStereotypes(o)) {
if ("javaImport".equals(
Model.getFacade().getName(stereotype))) {
isJavaImport = true;
break;
}
}
if (isJavaImport) {
for (Object elem : Model.getFacade().getSuppliers(o)) {
Object ns = Model.getFacade().getNamespace(elem);
if (ns != null && !ns.equals(compNamespace)) {
String packageName = getPackageName(ns);
ret.append("import ");
if (packageName != null && packageName.length() > 0) {
ret.append(getPackageName(ns));
ret.append('.');
}
ret.append(Model.getFacade().getName(elem));
if (Model.getFacade().isAPackage(elem)) {
ret.append(".*");
}
ret.append(";");
ret.append(LINE_SEPARATOR);
found = true;
}
}
}
}
if (found) {
ret.append(LINE_SEPARATOR);
}
return ret.toString();
}
private String generateImportType(Object type, String exclude) {
String ret = null;
if (Model.getFacade().isADataType(type)
&& JAVA_TYPES.contains(Model.getFacade().getName(type))) {
return null;
}
if (type != null && Model.getFacade().getNamespace(type) != null) {
String p = getPackageName(Model.getFacade().getNamespace(type));
if (!p.equals(exclude) && !p.equals(LANG_PACKAGE)) {
if (p.length() > 0) {
ret = p + '.' + Model.getFacade().getName(type);
} else {
ret = Model.getFacade().getName(type);
}
}
}
return ret;
}
/**
* Generate code for an extension point.
* <p>
*
* @param ep The extension point to generate for
*
* @return The generated code string. Always empty in this implementation.
*/
private String generateExtensionPoint(Object ep) {
return null;
}
/**
* Generate source code for an operation.
* <p>
* NOTE: This needs to be package visibility because it is used in
* OperationCodePiece.
*
* @param op UML Operation to generate code for
* @param documented flag indicating documentation comments should be
* included.
* @return String containing generated code.
*/
String generateOperation(Object op, boolean documented) {
if (isFileGeneration) {
documented = true; // fix Issue 1506
}
StringBuffer sb = new StringBuffer(80);
String nameStr = null;
boolean constructor = false;
if (Model.getExtensionMechanismsHelper().hasStereotype(op, "create")) {
nameStr = generateName(Model.getFacade().getName(
Model.getFacade().getOwner(op)));
constructor = true;
} else {
nameStr = generateName(Model.getFacade().getName(op));
}
// Each pattern here must be similar to corresponding code piece
// Operation code piece doesn't start with '\n'
// so the next line is commented. See Issue 1505
// sb.append(LINE_SEPARATOR); // begin with a blank line
if (documented) {
String s = generateConstraintEnrichedDocComment(op, documented,
INDENT);
if (s != null && s.trim().length() > 0) {
// should starts as the code piece
sb.append(s).append(INDENT);
}
}
sb.append(generateVisibility(op));
sb.append(generateAbstractness(op));
sb.append(generateScope(op));
sb.append(generateChangeability(op));
sb.append(generateConcurrency(op));
// pick out return type
Collection returnParams = Model.getCoreHelper().getReturnParameters(op);
Object rp;
if (returnParams.size() == 0) {
rp = null;
} else {
rp = returnParams.iterator().next();
}
if (returnParams.size() > 1) {
LOG.warning("Java generator only handles one return parameter"
+ " - Found " + returnParams.size() + " for "
+ Model.getFacade().getName(op));
}
if (rp != null && !constructor) {
Object returnType = Model.getFacade().getType(rp);
if (returnType == null) {
sb.append("void ");
} else {
sb.append(generateClassifierRef(returnType)).append(' ');
}
}
// name and params
List params = new ArrayList(Model.getFacade().getParameters(op));
params.remove(rp);
sb.append(nameStr).append('(');
if (params != null) {
for (int i = 0; i < params.size(); i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(generateParameter(params.get(i)));
}
}
sb.append(')');
Collection c = Model.getFacade().getRaisedSignals(op);
if (!c.isEmpty()) {
Iterator it = c.iterator();
boolean first = true;
while (it.hasNext()) {
Object signal = it.next();
if (!Model.getFacade().isAException(signal)) {
continue;
}
if (first) {
sb.append(" throws ");
} else {
sb.append(", ");
}
sb.append(Model.getFacade().getName(it.next()));
first = false;
}
}
return sb.toString();
}
private String generateAttribute(Object attr, boolean documented) {
if (isFileGeneration) {
documented = true; // always "documented" if we generate file.
}
StringBuffer sb = new StringBuffer(80);
if (documented) {
String s = generateConstraintEnrichedDocComment(attr, documented,
INDENT);
if (s != null && s.trim().length() > 0) {
sb.append(s).append(INDENT);
}
}
sb.append(generateCoreAttribute(attr));
sb.append(";").append(LINE_SEPARATOR);
return sb.toString();
}
/**
* Generates the core attribute for a multiplicity
*
* @param attr The attribute
* @return a string containing the attribute reference.
*/
String generateCoreAttribute(Object attr) {
StringBuffer sb = new StringBuffer(80);
sb.append(generateVisibility(attr));
sb.append(generateScope(attr));
sb.append(generateChangability(attr));
Object type = Model.getFacade().getType(attr);
Object multi = Model.getFacade().getMultiplicity(attr);
// handle multiplicity here since we need the type
// actually the API of generator is buggy since to generate
// multiplicity correctly we need the attribute too
if (type != null && multi != null) {
if (Model.getFacade().getUpper(multi) == 1) {
sb.append(generateClassifierRef(type)).append(' ');
} else if (Model.getFacade().isADataType(type)) {
sb.append(generateClassifierRef(type)).append("[] ");
} else {
sb.append("java.util.List ");
}
}
sb.append(generateName(Model.getFacade().getName(attr)));
Object init = Model.getFacade().getInitialValue(attr);
if (init != null) {
String initStr = generateExpression(init).trim();
if (initStr.length() > 0) {
sb.append(" = ").append(initStr);
}
}
return sb.toString();
}
/**
* Generates the literal value for an enumeration
*
* @param literal a literal value for an enumeration.
* @param documented indicates whether or not the documentation for the
* enumeration literal should be generated
* @param sep a separator character
* @return a string containing the literal value of an enumeration
*/
private String generateEnumerationLiteral(Object literal,
boolean documented, char sep) {
if (isFileGeneration) {
documented = true; // always "documented" if we generate file.
}
StringBuffer sb = new StringBuffer(80);
if (documented) {
String s = generateConstraintEnrichedDocComment(literal,
documented, INDENT);
if (s != null && s.trim().length() > 0) {
sb.append(s).append(INDENT);
}
}
sb.append(generateName(Model.getFacade().getName(literal)));
sb.append(sep).append(LINE_SEPARATOR);
return sb.toString();
}
/**
* Generates a parameter object.
*
* @param parameter the parameter
* @return a string containing a parameter
*/
private String generateParameter(Object parameter) {
StringBuffer sb = new StringBuffer(20);
// TODO: qualifiers (e.g., const)
// TODO: stereotypes...
sb.append(generateClassifierRef(Model.getFacade().getType(parameter)));
sb.append(' ');
sb.append(generateName(Model.getFacade().getName(parameter)));
// TODO: initial value
return sb.toString();
}
/**
* Generates the package statement
*
* @param p The package object
* @return a string containing the package statement
*/
private String generatePackage(Object p) {
StringBuffer sb = new StringBuffer(80);
String packName = generateName(Model.getFacade().getName(p));
sb.append("package ").append(packName).append(" {");
sb.append(LINE_SEPARATOR);
Collection ownedElements = Model.getFacade().getOwnedElements(p);
for (Object modelElement : ownedElements) {
// This is the only remaining references to generate(), if it
// can be made more specific, we can remove that method - tfm
// (do we support anything other than classifiers in a package?)
sb.append(generate(modelElement));
sb.append(LINE_SEPARATOR).append(LINE_SEPARATOR);
}
sb.append(LINE_SEPARATOR).append("})").append(LINE_SEPARATOR);
return sb.toString();
}
/**
* Generate the start sequence for a classifier. The start sequence is
* everything from the preceding javadoc comment to the opening curly brace.
* Start sequences are non-empty for classes and interfaces only.
*
* This method is intended for package internal usage only.
*
* @param cls the classifier for which to generate the start sequence
*
* @return the generated start sequence
*/
StringBuffer generateClassifierStart(Object cls) {
String sClassifierKeyword;
if (Model.getFacade().isAClass(cls)) {
sClassifierKeyword = "class";
} else if (Model.getFacade().isAInterface(cls)) {
sClassifierKeyword = "interface";
} else if (Model.getFacade().isAEnumeration(cls)) {
sClassifierKeyword = "enum";
} else {
return null; // actors, use cases etc.
}
StringBuffer sb = new StringBuffer(80);
// Add the comments for this classifier first.
// Each pattern here must be similar to corresponding code piece
// Classfier code piece doesn't start with LINE_SEPARATOR
// so the next line is commented. See Issue 1505
// sb.append (LINE_SEPARATOR);
sb.append(DocumentationManager.getComments(cls));
sb.append(generateConstraintEnrichedDocComment(cls, true, ""));
// Now add visibility, but not for non public top level classifiers
if (Model.getFacade().isPublic(cls)
|| Model.getFacade().isAClassifier(
Model.getFacade().getNamespace(cls))) {
sb.append(generateVisibility(Model.getFacade().getVisibility(cls)));
}
// Add other modifiers in JLS order
if (Model.getFacade().isAbstract(cls)
&& !(Model.getFacade().isAInterface(cls))) {
sb.append("abstract ");
}
if (Model.getFacade().isLeaf(cls)) {
sb.append("final ");
}
// add additional modifiers
// TODO: This is for backward compatibility with old models reverse
// engineered with earlier versions of ArgoUML. As of 0.24, and
// probably earlier, ArgoUML should be able to capture all necessary
// information in the model itself. - tfm - 20070217
Object smod = Model.getFacade().getTaggedValue(cls,
ImportInterface.SOURCE_MODIFIERS_TAG);
if (smod != null && Model.getFacade().getValue(smod) != null) {
sb.append(" ");
sb.append(Model.getFacade().getValue(smod));
sb.append(" ");
}
// add classifier keyword and classifier name
sb.append(sClassifierKeyword).append(" ");
sb.append(generateName(Model.getFacade().getName(cls)));
// add type parameters
List templateParameters = Model.getFacade().getTemplateParameters(cls);
for (int i = 0; i < templateParameters.size(); i++) {
if (i == 0) {
sb.append("<");
}
Object param = Model.getFacade().getParameter(
templateParameters.get(i));
sb.append(Model.getFacade().getName(param));
for (String bound : new String[] {"extends", "super"}) {
Object s = Model.getFacade().getTaggedValueValue(param, bound);
if (s != null && s.toString().trim().length() > 0) {
sb.append(" " + bound + " " + s);
}
}
if (i == templateParameters.size() - 1) {
sb.append(">");
} else {
sb.append(", ");
}
}
// add base class/interface
String baseClass = generateGeneralization(Model.getFacade()
.getGeneralizations(cls));
if (!baseClass.equals("")) {
sb.append(" ").append("extends ").append(baseClass);
}
// add implemented interfaces, if needed
// UML: realizations!
if (Model.getFacade().isAClass(cls)) {
String interfaces = generateSpecification(cls);
LOG.fine("Specification: " + interfaces);
if (!interfaces.equals("")) {
sb.append(" ").append("implements ").append(interfaces);
}
}
// add opening brace
sb.append(lfBeforeCurly ? (LINE_SEPARATOR + "{") : " {");
// list tagged values for documentation
String tv = generateTaggedValues(cls);
if (tv != null && tv.length() > 0) {
sb.append(LINE_SEPARATOR).append(INDENT).append(tv);
}
return sb;
}
/**
* Generates the code for a classifer end
*
* @param cls The classifier
* @return a StringBuffer containing the classifier end
*/
private StringBuffer generateClassifierEnd(Object cls) {
StringBuffer sb = new StringBuffer();
if (Model.getFacade().isAClass(cls)
|| Model.getFacade().isAInterface(cls)
|| Model.getFacade().isAEnumeration(cls)) {
if (verboseDocs) {
String classifierkeyword = null;
if (Model.getFacade().isAClass(cls)) {
classifierkeyword = "class";
} else {
classifierkeyword = "interface";
}
sb.append(LINE_SEPARATOR);
sb.append("//end of ").append(classifierkeyword);
sb.append(" ").append(Model.getFacade().getName(cls));
sb.append(LINE_SEPARATOR);
}
sb.append(LINE_SEPARATOR);
sb.append("}");
}
return sb;
}
/**
* Append the classifier end sequence to the prefix text specified. The
* classifier end sequence is the closing curly brace together with any
* comments marking the end of the classifier.
*
* This method is intended for package internal usage.
*
* @param sbPrefix the prefix text to be amended. It is OK to call append on
* this parameter.
* @param cls the classifier for which to generate the classifier end
* sequence. Only classes and interfaces have a classifier end
* sequence.
* @return the complete classifier code, i.e., sbPrefix plus the classifier
* end sequence
*/
StringBuffer appendClassifierEnd(StringBuffer sbPrefix, Object cls) {
sbPrefix.append(generateClassifierEnd(cls));
return sbPrefix;
}
/**
* Generates code for a classifier. In case of Java code is generated for
* classes and interfaces only at the moment.
*
* @param cls a classifier object
*/
private String generateClassifier(Object cls) {
StringBuffer returnValue = new StringBuffer();
StringBuffer start = generateClassifierStart(cls);
if ((start != null) && (start.length() > 0)) {
StringBuffer body = generateClassifierBody(cls);
StringBuffer end = generateClassifierEnd(cls);
returnValue.append(start.toString());
if ((body != null) && (body.length() > 0)) {
returnValue.append(LINE_SEPARATOR);
returnValue.append(body);
if (lfBeforeCurly) {
returnValue.append(LINE_SEPARATOR);
}
}
returnValue.append((end != null) ? end.toString() : "");
}
return returnValue.toString();
}
/**
* Generates the body of a class or interface.
*
* @param cls The classifier object (either a class, interface or
* enumeration.
*/
private StringBuffer generateClassifierBody(Object cls) {
StringBuffer sb = new StringBuffer();
if (Model.getFacade().isAClass(cls)
|| Model.getFacade().isAInterface(cls)
|| Model.getFacade().isAEnumeration(cls)) {
String tv = null; // helper for tagged values
// add attributes
Collection sFeatures = Model.getFacade().getStructuralFeatures(cls);
if (!sFeatures.isEmpty()) {
sb.append(LINE_SEPARATOR);
if (verboseDocs && Model.getFacade().isAClass(cls)) {
sb.append(INDENT).append("// Attributes");
sb.append(LINE_SEPARATOR);
}
boolean first = true;
for (Object structuralFeature : sFeatures) {
if (!first) {
sb.append(LINE_SEPARATOR);
}
sb.append(INDENT);
// The only type of StructuralFeature is an Attribute
sb.append(generateAttribute(structuralFeature, false));
tv = generateTaggedValues(structuralFeature);
if (tv != null && tv.length() > 0) {
sb.append(INDENT).append(tv);
}
first = false;
}
}
if (Model.getFacade().isAEnumeration(cls)) {
addLiterals(cls, sb);
}
Collection ends = Model.getFacade().getAssociationEnds(cls);
if (!ends.isEmpty()) {
addAttributesImplementingAssociations(cls, sb, ends);
}
// Inner classes
Collection elements = Model.getFacade().getOwnedElements(cls);
for (Iterator i = elements.iterator(); i.hasNext();) {
Object element = i.next();
if (Model.getFacade().isAClass(element)
|| Model.getFacade().isAInterface(element)) {
sb.append(generateClassifier(element));
}
}
// add operations
// TODO: constructors
Collection bFeatures = Model.getFacade().getOperations(cls);
if (!bFeatures.isEmpty()) {
addOperations(cls, sb, bFeatures);
}
}
return sb;
}
/**
* Generates the operations
*
* @param cls The classifier object
* @param sb The StringBuffer to which the operations will be added
* @param bFeatures a collection of behavioral features
*/
private void addOperations(Object cls,
StringBuffer sb,
Collection bFeatures) {
String tv;
sb.append(LINE_SEPARATOR);
if (verboseDocs) {
sb.append(INDENT).append("// Operations");
sb.append(LINE_SEPARATOR);
}
boolean first = true;
for (Object behavioralFeature : bFeatures) {
if (!first) {
sb.append(LINE_SEPARATOR);
}
sb.append(INDENT);
sb.append(generateOperation(behavioralFeature, false));
tv = generateTaggedValues(behavioralFeature);
if ((Model.getFacade().isAClass(cls))
&& (Model.getFacade().isAOperation(behavioralFeature))
&& (!Model.getFacade().isAbstract(behavioralFeature))) {
if (lfBeforeCurly) {
sb.append(LINE_SEPARATOR).append(INDENT);
} else {
sb.append(' ');
}
sb.append('{');
if (tv.length() > 0) {
sb.append(LINE_SEPARATOR).append(INDENT).append(tv);
}
// there is no ReturnType in behavioral feature (UML)
sb.append(LINE_SEPARATOR);
sb.append(generateMethodBody(behavioralFeature));
sb.append(INDENT);
sb.append("}").append(LINE_SEPARATOR);
} else {
sb.append(";").append(LINE_SEPARATOR);
if (tv.length() > 0) {
sb.append(INDENT).append(tv).append(LINE_SEPARATOR);
}
}
first = false;
}
}
/**
* This method adds attribute associations to the string buffer.
*
* @param cls The class object
* @param sb The StringBuffer containing the text of the generated class.
* @param ends The association ends
*/
private void addAttributesImplementingAssociations(Object cls,
StringBuffer sb, Collection ends) {
String tv;
sb.append(LINE_SEPARATOR);
if (verboseDocs && Model.getFacade().isAClass(cls)) {
sb.append(INDENT).append("// Associations");
sb.append(LINE_SEPARATOR);
}
for (Object associationEnd : ends) {
Object association = Model.getFacade().getAssociation(
associationEnd);
sb.append(generateAssociationFrom(association, associationEnd));
tv = generateTaggedValues(association);
if (tv != null && tv.length() > 0) {
sb.append(INDENT).append(tv);
}
}
}
/**
* This method adds enumeration literals to the StringBuffer
*
* @param cls The class object
* @param sb The StringBuffer containing the generated class
*/
private void addLiterals(Object cls, StringBuffer sb) {
String tv;
Collection literals = Model.getFacade().getEnumerationLiterals(cls);
if (!literals.isEmpty()) {
sb.append(LINE_SEPARATOR);
if (verboseDocs) {
sb.append(INDENT).append("// Literals");
sb.append(LINE_SEPARATOR);
}
boolean first = true;
int size = literals.size();
int cnt = 0;
for (Object literal : literals) {
cnt++;
if (!first) {
sb.append(LINE_SEPARATOR);
}
sb.append(INDENT);
char sep = cnt != size ? ',' : ';';
sb.append(generateEnumerationLiteral(literal, false, sep));
tv = generateTaggedValues(literal);
if (tv != null && tv.length() > 0) {
sb.append(INDENT).append(tv);
}
first = false;
}
}
}
/**
* Generate the body of a method associated with the given operation. This
* assumes there's at most one method associated!
*
* If no method is associated with the operation, a default method body will
* be generated.
*
* @param op The operation object
*/
private String generateMethodBody(Object op) {
// cat.info("generateMethodBody");
if (op != null) {
for (Object m : Model.getFacade().getMethods(op)) {
if (m != null) {
if (Model.getFacade().getBody(m) != null) {
String body = (String) Model.getFacade().getBody(
Model.getFacade().getBody(m));
// Note that this will not preserve empty lines
// in the body
StringTokenizer tokenizer = new StringTokenizer(body,
"\r\n");
StringBuffer bsb = new StringBuffer();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.length() > 0) {
bsb.append(token);
bsb.append(LINE_SEPARATOR);
}
}
if (bsb.length() <= 0) {
// generateClassifierBody relies on the string
// ending with a new-line
bsb.append(LINE_SEPARATOR);
}
return bsb.toString();
}
return "";
}
}
// pick out return type
Collection returnParams = Model.getCoreHelper()
.getReturnParameters(op);
Object rp;
if (returnParams.size() == 0) {
rp = null;
} else {
rp = returnParams.iterator().next();
}
if (returnParams.size() > 1) {
LOG.warning("Java generator only handles one return parameter"
+ " - Found " + returnParams.size() + " for "
+ Model.getFacade().getName(op));
}
if (rp != null) {
Object returnType = Model.getFacade().getType(rp);
return generateDefaultReturnStatement(returnType);
}
}
return generateDefaultReturnStatement(null);
}
/**
* This method generates default return statements for methods
*
* @param cls The class object
* @return
*/
private String generateDefaultReturnStatement(Object cls) {
if (cls == null) {
return "";
}
String clsName = Model.getFacade().getName(cls);
if (clsName.equals("void")) {
return "";
}
if (clsName.equals("char")) {
return INDENT + "return 'x';" + LINE_SEPARATOR;
}
if (clsName.equals("int")) {
return INDENT + "return 0;" + LINE_SEPARATOR;
}
if (clsName.equals("boolean")) {
return INDENT + "return false;" + LINE_SEPARATOR;
}
if (clsName.equals("byte")) {
return INDENT + "return 0;" + LINE_SEPARATOR;
}
if (clsName.equals("long")) {
return INDENT + "return 0;" + LINE_SEPARATOR;
}
if (clsName.equals("float")) {
return INDENT + "return 0.0;" + LINE_SEPARATOR;
}
if (clsName.equals("double")) {
return INDENT + "return 0.0;" + LINE_SEPARATOR;
}
return INDENT + "return null;" + LINE_SEPARATOR;
}
/**
* This method generates the tagged values
*
* @param e
* @return a String containing the tagged values.
*/
private String generateTaggedValues(Object e) {
if (isInUpdateMode) {
return ""; // no tagged values are generated in update mode.
}
Iterator iter = Model.getFacade().getTaggedValues(e);
if (iter == null) {
return "";
}
boolean first = true;
StringBuffer buf = new StringBuffer();
String s = null;
while (iter.hasNext()) {
/*
* 2002-11-07 Jaap Branderhorst Was
*
* s = generateTaggedValue((MTaggedValue) iter.next());
*
* which caused problems because the test tags (i.e. tags with name
* <NotationName.getName()>+TEST_SUFFIX) were still generated.
*
* New code:
*/
s = generateTaggedValue(iter.next());
// end new code
if (s != null && s.length() > 0) {
if (first) {
buf.append("/* {");
first = false;
} else {
buf.append(", ");
}
buf.append(s);
}
}
/*
* Corrected 2001-09-26 STEFFEN ZSCHALER
*
* Was: if (!first) buf.append("}\n");
*
* which caused problems with new-lines in tagged values.
*/
if (!first) {
buf.append("}*/").append(LINE_SEPARATOR);
}
return buf.toString();
}
/**
* Generates a tagged value
*
* @param tv a tagged value object
* @return a String containing the tagged values (usually comments).
*/
private String generateTaggedValue(Object tv) {
if (tv == null) {
return "";
}
String s = generateUninterpreted(Model.getFacade().getValueOfTag(tv));
if (s == null || s.length() == 0 || s.equals("/** */")) {
return "";
}
String t = Model.getFacade().getTagOfTag(tv);
if (Argo.DOCUMENTATION_TAG.equals(t)) {
return "";
}
return generateName(t) + "=" + s;
}
/**
* Enhance/Create the doc comment for the given model element, including
* tags for any OCL constraints connected to the model element. The tags
* generated are suitable for use with the ocl injector which is part of the
* Dresden OCL Toolkit and are in detail:
*
* @invariant for each invariant specified @precondition for
* each precondition specified @postcondition for each postcondition
* specified @key-type specifying the class of the keys of a mapped
* association Currently mapped associations are not supported yet...
* @element-type specifying the class referenced in an association
*
* @since 2001-09-26 ArgoUML 0.9.3
* @author Steffen Zschaler
*
* @param me the model element for which the documentation comment is needed
* @param ae the association end which is represented by the model element
* @return the documentation comment for the specified model element, either
* enhanced or completely generated
*/
private String generateConstraintEnrichedDocComment(Object me, Object ae) {
String s = generateConstraintEnrichedDocComment(me, true, INDENT);
if (isCollection(ae)) {
// Multiplicity greater 1, that means we will generate some sort of
// collection, so we need to specify the element type tag
StringBuffer sDocComment = new StringBuffer(80);
// Prepare doc comment
if (!(s == null || "".equals(s))) {
// Just remove closing "*/"
sDocComment.append(s.substring(0, s.indexOf("*/") + 1));
} else {
sDocComment.append(INDENT).append("/**").append(LINE_SEPARATOR);
sDocComment.append(INDENT).append(" * ")
.append(LINE_SEPARATOR);
sDocComment.append(INDENT).append(" *");
}
// Build doc comment
// Object type = Model.getFacade().getType(ae);
// if (type != null) {
// sDocComment.append(" @element-type ");
// sDocComment.append(Model.getFacade().getName(type));
// }
// REMOVED: 2002-03-11 STEFFEN ZSCHALER: element type
// unknown is not recognized by the OCL injector...
// else {
// sDocComment += " @element-type unknown";
// }
sDocComment.append(LINE_SEPARATOR).append(INDENT).append(" */");
sDocComment.append(LINE_SEPARATOR);
return sDocComment.toString();
}
return (s != null) ? s : "";
}
/**
* @param element ModelElement which has the Multiplicity
* @return true if multiplicity is non-null and upper bound is greater than
* 1
*/
private boolean isCollection(Object element) {
Object multiplicity = Model.getFacade().getMultiplicity(element);
if (multiplicity != null) {
int upper = Model.getFacade().getUpper(multiplicity);
// -1 is UML's special 'unlimited integer'
if (upper > 1 || upper == -1) {
return true;
}
}
return false;
}
/**
* Enhance/Create the doc comment for the given model element, including
* tags for any OCL constraints connected to the model element. The tags
* generated are suitable for use with the ocl injector which is part of the
* Dresden OCL Toolkit and are in detail:
*
* @invariant for each invariant specified @precondition for
* each precondition specified @postcondition for each postcondition
* specified
*
* @since 2001-09-26 ArgoUML 0.9.3
* @author Steffen Zschaler
*
* @param me the model element for which the documentation comment is needed
* @param documented if existing tagged values should be generated in
* addition to javadoc
* @param indent indent String (usually blanks) for indentation of generated
* comments
* @return the documentation comment for the specified model element, either
* enhanced or completely generated
*/
public static String generateConstraintEnrichedDocComment(Object me,
boolean documented, String indent) {
if (isFileGeneration) {
documented = true; // always "documented" if we generate file
}
// Retrieve any existing doc comment
String s = (VERBOSE_DOCS || DocumentationManager.hasDocs(me))
? DocumentationManager.getDocs(me, indent)
: null;
StringBuffer sDocComment = new StringBuffer(80);
if (s != null && s.trim().length() > 0) {
sDocComment.append(s).append(LINE_SEPARATOR);
}
LOG.fine("documented=" + documented);
if (!documented) {
return sDocComment.toString();
}
// Extract constraints
Collection cConstraints = Model.getFacade().getConstraints(me);
if (cConstraints.size() == 0) {
return sDocComment.toString();
}
// Prepare doc comment
if (s != null) {
// Just remove closing */
s = sDocComment.toString();
sDocComment = new StringBuffer(s.substring(0, s.indexOf("*/") + 1));
} else {
sDocComment.append(INDENT).append("/**").append(LINE_SEPARATOR);
sDocComment.append(INDENT).append(" * ").append(LINE_SEPARATOR);
sDocComment.append(INDENT).append(" *");
}
// Add each constraint
class TagExtractor extends DepthFirstAdapter {
private LinkedList<String> llsTags = new LinkedList<String>();
private String constraintName;
private int constraintID;
/**
* Constructor.
*
* @param sConstraintName The constraint name.
*/
public TagExtractor(String sConstraintName) {
super();
constraintName = sConstraintName;
}
public Iterator getTags() {
return llsTags.iterator();
}
/*
* @see tudresden.ocl.parser.analysis.Analysis#caseAConstraintBody(tudresden.ocl.parser.node.AConstraintBody)
*/
@Override
public void caseAConstraintBody(AConstraintBody node) {
// We don't care for anything below this node, so we
// do not use apply anymore.
String sKind = (node.getStereotype() != null) ? (node
.getStereotype().toString()) : (null);
String sExpression = (node.getExpression() != null) ? (node
.getExpression().toString()) : (null);
String sName = (node.getName() != null) ? (node.getName()
.getText()) : (constraintName + "_" + (constraintID++));
if ((sKind == null) || (sExpression == null)) {
return;
}
String sTag;
if (sKind.equals("inv ")) {
sTag = "@invariant ";
} else if (sKind.equals("post ")) {
sTag = "@postcondition ";
} else if (sKind.equals("pre ")) {
sTag = "@precondition ";
} else {
return;
}
sTag += sName + ": " + sExpression;
llsTags.addLast(sTag);
}
}
tudresden.ocl.check.types.ModelFacade mf = new ArgoFacade(me);
for (Object constraint : cConstraints) {
try {
String body = (String) Model.getFacade().getBody(
Model.getFacade().getBody(constraint));
OclTree otParsed = OclTree.createTree(body, mf);
TagExtractor te = new TagExtractor(Model.getFacade().getName(
constraint));
otParsed.apply(te);
for (Iterator j = te.getTags(); j.hasNext();) {
sDocComment.append(' ').append(j.next());
sDocComment.append(LINE_SEPARATOR);
sDocComment.append(INDENT).append(" *");
}
} catch (IOException ioe) {
LOG.log(Level.SEVERE,
"Nothing to be done, should not happen",
ioe);
}
}
sDocComment.append("/").append(LINE_SEPARATOR);
return sDocComment.toString();
}
/**
* Generates the code for a specific association
*
* @param a The association
* @param associationEnd an association end
* @return a string containing the code for the "from" end of the
* association
*/
private String generateAssociationFrom(Object a, Object associationEnd) {
// TODO: does not handle n-ary associations
StringBuffer sb = new StringBuffer(80);
Collection connections = Model.getFacade().getConnections(a);
for (Object associationEnd2 : connections) {
if (associationEnd2 != associationEnd) {
sb.append(INDENT);
sb.append(generateConstraintEnrichedDocComment(a,
associationEnd2));
sb.append(generateAssociationEnd(associationEnd2));
}
}
return sb.toString();
}
/**
* Generates the code for an association end
*
* @param ae the association end
* @return a String containing a reference to the association end
*/
private String generateAssociationEnd(Object ae) {
if (!Model.getFacade().isNavigable(ae)) {
return "";
}
if (Model.getFacade().isAbstract(
Model.getFacade().getAssociation(ae))) {
return "";
}
// String s = INDENT + "protected ";
// must be public or generate public navigation method!
// String s = INDENT + "public ";
StringBuffer sb = new StringBuffer(80);
sb.append(INDENT).append(generateCoreAssociationEnd(ae));
return (sb.append(";").append(LINE_SEPARATOR)).toString();
}
/**
* Generates the code for an association end
* @param ae The association end.
* @return a string containing the code for an association end
*/
String generateCoreAssociationEnd(Object ae) {
StringBuffer sb = new StringBuffer(80);
sb.append(generateVisibility(Model.getFacade().getVisibility(ae)));
// sb.append(generateVisibility(ae));
if (Model.getFacade().isStatic(ae)) {
sb.append("static ");
}
if (Model.getFacade().isFrozen(ae)) {
sb.append("final ");
}
// String n = ae.getName();
// if (n != null && !String.UNSPEC.equals(n))
// s += generateName(n) + " ";
// if (ae.isNavigable()) s += "navigable ";
// if (ae.getIsOrdered()) s += "ordered ";
if (Model.getFacade().getUpper(ae) == 1) {
sb.append(generateClassifierRef(Model.getFacade().getType(ae)));
} else {
sb.append("List"); // generateMultiplicity(m) + " ";
// create generic type for list
Object type = Model.getFacade().getType(ae);
sb.append(String.format("<%s>", Model.getFacade().getName(type)));
}
sb.append(' ').append(generateAscEndName(ae));
return sb.toString();
}
// //////////////////////////////////////////////////////////////
// internal methods?
private String generateGeneralization(Collection generalizations) {
if (generalizations == null) {
return "";
}
Collection classes = new ArrayList();
for (Object generalization : generalizations) {
Object generalizableElement = Model.getFacade().getGeneral(
generalization);
// assert ge != null
if (generalizableElement != null) {
classes.add(generalizableElement);
}
}
return generateClassList(classes);
}
/**
* Generates the code for a specification
* (usually the classifier references).
*
* @param cls the class object
* @return
*/
private String generateSpecification(Object cls) {
Collection realizations = Model.getFacade().getSpecifications(cls);
if (realizations == null) {
return "";
}
LOG.fine("realizations: " + realizations.size());
StringBuffer sb = new StringBuffer(80);
Iterator clsEnum = realizations.iterator();
while (clsEnum.hasNext()) {
Object inter = clsEnum.next();
sb.append(generateClassifierRef(inter));
if (clsEnum.hasNext()) {
sb.append(", ");
}
}
return sb.toString();
}
/**
* Generates the class list
* @param classifiers a collection of classifiers
* @return a String containing the list of classifiers.
*/
private String generateClassList(Collection classifiers) {
if (classifiers == null) {
return "";
}
StringBuffer sb = new StringBuffer(80);
Iterator clsEnum = classifiers.iterator();
while (clsEnum.hasNext()) {
sb.append(generateClassifierRef(clsEnum.next()));
if (clsEnum.hasNext()) {
sb.append(", ");
}
}
return sb.toString();
}
/**
* Returns a visibility String either for a VisibilityKind (according to the
* definition in NotationProvider2), but also for a model element, because
* if it is a Feature, then the tag 'src_visibility' is to be taken into
* account for generating language dependent visibilities.
* @param an object containing visibility data
*/
private String generateVisibility(Object o) {
if (Model.getFacade().isAFeature(o)) {
// TODO: The src_visibility tag doesn't appear to be created
// anywhere by ArgoUML currently
Object tv = Model.getFacade().getTaggedValue(o, "src_visibility");
if (tv != null) {
String tagged = (String) Model.getFacade().getValue(tv);
if (tagged != null) {
if (tagged.trim().equals("")
|| tagged.trim().toLowerCase().equals("package")
|| tagged.trim().toLowerCase().equals("default")) {
return "";
}
return tagged + " ";
}
}
}
if (Model.getFacade().isAModelElement(o)) {
if (Model.getFacade().isPublic(o)) {
return "public ";
}
if (Model.getFacade().isPrivate(o)) {
return "private ";
}
if (Model.getFacade().isProtected(o)) {
return "protected ";
}
if (Model.getFacade().isPackage(o)) {
return "";
}
}
if (Model.getFacade().isAVisibilityKind(o)) {
if (Model.getVisibilityKind().getPublic().equals(o)) {
return "public ";
}
if (Model.getVisibilityKind().getPrivate().equals(o)) {
return "private ";
}
if (Model.getVisibilityKind().getProtected().equals(o)) {
return "protected ";
}
if (Model.getVisibilityKind().getPackage().equals(o)) {
return "";
}
}
return "";
}
private String generateScope(Object f) {
if (Model.getFacade().isStatic(f)) {
return "static ";
}
return "";
}
/**
* Generate "abstract" keyword for an abstract operation.
*/
private String generateAbstractness(Object op) {
if (Model.getFacade().isAbstract(op)) {
return "abstract ";
}
return "";
}
/**
* Generate "final" keyword for final operations.
* @param op the operation
*/
private String generateChangeability(Object op) {
if (Model.getFacade().isLeaf(op)) {
return "final ";
}
return "";
}
/**
* Generates the changeability for the object
* @param sf indicates whether the object is read-only i.e. "static final"
* @return a String containing the "final" keyword if necessary,
* an empty string otherwise
*/
private String generateChangability(Object sf) {
if (Model.getFacade().isReadOnly(sf)) {
return "final ";
}
return "";
}
/**
* Generates "synchronized" keyword for guarded operations.
*
* @param op The operation
* @return String The synchronized keyword if the operation is guarded, else
* "".
*/
private String generateConcurrency(Object op) {
if (Model.getFacade().getConcurrency(op) != null
&& Model.getConcurrencyKind().getGuarded()
.equals(Model.getFacade().getConcurrency(op))) {
return "synchronized ";
}
return "";
}
/**
* Generates a String representation of a Multiplicity.
*
* @param m the Multiplicity.
*
* @return a human readable String.
*/
private String generateMultiplicity(Object m) {
if (m == null || "1".equals(Model.getFacade().toString(m))) {
return "";
} else {
return Model.getFacade().toString(m);
}
}
private String generateState(Object m) {
return Model.getFacade().getName(m);
}
private String generateTransition(Object m) {
StringBuffer sb = new StringBuffer(generateName(Model.getFacade()
.getName(m)));
String t = generateEvent(Model.getFacade().getTrigger(m));
String g = generateGuard(Model.getFacade().getGuard(m));
String e = generateAction(Model.getFacade().getEffect(m));
if (sb.length() > 0) {
sb.append(": ");
}
sb.append(t);
if (g.length() > 0) {
sb.append(" [").append(g).append(']');
}
if (e.length() > 0) {
sb.append(" / ").append(e);
}
return sb.toString();
}
private String generateAction(Object m) {
// return m.getName();
if (m != null) {
Object script = Model.getFacade().getScript(m);
if ((script != null)
&& (Model.getFacade().getBody(script) != null)) {
return Model.getFacade().getBody(script).toString();
}
}
return "";
}
private String generateGuard(Object m) {
// return generateExpression(Model.getFacade().getExpression(m));
if (m != null && Model.getFacade().getExpression(m) != null) {
return generateExpression(Model.getFacade().getExpression(m));
}
return "";
}
private String generateMessage(Object m) {
if (m == null) {
return "";
}
return generateName(Model.getFacade().getName(m)) + "::"
+ generateAction(Model.getFacade().getAction(m));
}
/*
* Generates the text for a (trigger) event.
*
* @author MVW
*
* @param m Object of any MEvent kind
*
* @return The generated event (as a String).
*/
private String generateEvent(Object m) {
if (Model.getFacade().isAChangeEvent(m)) {
return "when("
+ generateExpression(Model.getFacade().getExpression(m))
+ ")";
}
if (Model.getFacade().isATimeEvent(m)) {
return "after("
+ generateExpression(Model.getFacade().getExpression(m))
+ ")";
}
if (Model.getFacade().isASignalEvent(m)) {
return generateName(Model.getFacade().getName(m));
}
if (Model.getFacade().isACallEvent(m)) {
return generateName(Model.getFacade().getName(m));
}
return "";
}
/**
* This method is responsible for generating the association end name. If
* the association is named, then that name is returned. If the association
* end is named, then that name is returned; otherwise the classifier name
* is returned.s
*
* @param ae The association
* @return a string containing the name of the association end.
*/
String generateAscEndName(Object ae) {
String n = Model.getFacade().getName(ae);
Object asc = Model.getFacade().getAssociation(ae);
String ascName = Model.getFacade().getName(asc);
if (n != null && n.length() > 0) {
n = generateName(n);
} else if (ascName != null && ascName.length() > 0) {
n = generateName(ascName);
} else {
n = generateClassifierRef(Model.getFacade().getType(ae));
}
n = String.valueOf(n.charAt(0)).toLowerCase() + n.substring(1);
return n;
}
/**
* Gets the Java package name for a given namespace, ignoring the root
* namespace (which is the model).
*
* @param namespace the namespace
* @return the Java package name
*/
public String getPackageName(Object namespace) {
if (namespace == null || !Model.getFacade().isANamespace(namespace)
|| Model.getFacade().getNamespace(namespace) == null) {
return "";
}
String packagePath = Model.getFacade().getName(namespace);
if (packagePath == null) {
return "";
}
while ((namespace = Model.getFacade().getNamespace(namespace))
!= null) {
// omit root package name; it's the model's root
if (Model.getFacade().getNamespace(namespace) != null) {
packagePath = Model.getFacade().getName(namespace) + '.'
+ packagePath;
}
}
return packagePath;
}
/*
* @see org.argouml.moduleloader.ModuleInterface#getName()
*/
public String getName() {
return "GeneratorJava";
}
/*
* @see org.argouml.moduleloader.ModuleInterface#getInfo(int)
*/
public String getInfo(int type) {
switch (type) {
case DESCRIPTION:
return "Java notation and code generator";
case AUTHOR:
return JavaModuleGlobals.MODULE_AUTHOR;
case VERSION:
return JavaModuleGlobals.MODULE_VERSION;
case DOWNLOADSITE:
return JavaModuleGlobals.MODULE_DOWNLOADSITE;
default:
return null;
}
}
/*
* Initialize the Java code generator.
*
* @see org.argouml.moduleloader.ModuleInterface#enable()
*/
public boolean enable() {
GeneratorManager.getInstance().addGenerator(java, this);
return true;
}
/*
* Disable the Java code generator.
*
* @see org.argouml.moduleloader.ModuleInterface#disable()
*/
public boolean disable() {
GeneratorManager.getInstance().removeGenerator(java);
return true;
}
/**
* Returns the _lfBeforeCurly.
*
* @return boolean
*/
public boolean isLfBeforeCurly() {
return lfBeforeCurly;
}
/**
* Returns the _verboseDocs.
*
* @return boolean
*/
public boolean isVerboseDocs() {
return verboseDocs;
}
/**
* Sets the lfBeforeCurly.
*
* @param beforeCurl The new value.
*/
public void setLfBeforeCurly(boolean beforeCurl) {
lfBeforeCurly = beforeCurl;
}
/**
* Sets the verboseDocs.
*
* @param verbose The new value.
*/
public void setVerboseDocs(boolean verbose) {
verboseDocs = verbose;
}
private String generateActionState(Object actionState) {
String ret = "";
Object action = Model.getFacade().getEntry(actionState);
if (action != null) {
Object expression = Model.getFacade().getScript(action);
if (expression != null) {
ret = generateExpression(expression);
}
}
return ret;
}
private String generateExpression(Object expr) {
if (Model.getFacade().isAExpression(expr)) {
return generateUninterpreted((String) Model.getFacade().getBody(
expr));
} else if (Model.getFacade().isAConstraint(expr)) {
return generateExpression(Model.getFacade().getBody(expr));
}
return "";
}
private String generateName(String n) {
return n;
}
/**
* Make a string non-null.
* <p>
*
* What is the purpose of this function? Shouldn't it be private static?
*
* @param un The String.
* @return a non-null string.
*/
private String generateUninterpreted(String un) {
if (un == null) {
return "";
}
return un;
}
private String generateClassifierRef(Object cls) {
if (cls == null) {
return "";
}
return Model.getFacade().getName(cls);
}
private String generateStereotype(Object st) {
/*
* TODO: This code is not used. Why is it here? It causes an unwanted
* dependency to the org.argouml.kernel. (Project,...)
*/
// if (st == null)
// return "";
// Project project =
// ProjectManager.getManager().getCurrentProject();
// ProjectSettings ps = project.getProjectSettings();
// if (Model.getFacade().isAModelElement(st)) {
// if (Model.getFacade().getName(st) == null)
// return ""; // Patch by Jeremy Bennett
// if (Model.getFacade().getName(st).length() == 0)
// return "";
// return ps.getLeftGuillemot()
// + generateName(Model.getFacade().getName(st))
// + ps.getRightGuillemot();
// }
// if (st instanceof Collection) {
// Object o;
// StringBuffer sb = new StringBuffer(10);
// boolean first = true;
// Iterator iter = ((Collection) st).iterator();
// while (iter.hasNext()) {
// if (!first)
// sb.append(',');
// o = iter.next();
// if (o != null) {
// sb.append(generateName(Model.getFacade().getName(o)));
// first = false;
// }
// }
// if (!first) {
// return ps.getLeftGuillemot()
// + sb.toString()
// + ps.getRightGuillemot();
// }
// }
return "";
}
/*
* @see
* org.argouml.uml.generator.CodeGenerator#generate(java.util.Collection,
* boolean)
*/
public Collection generate(Collection elements, boolean deps) {
LOG.fine("generate() called");
File tmpdir = null;
try {
tmpdir = TempFileUtils.createTempDir();
if (tmpdir != null) {
generateFiles(elements, tmpdir.getPath(), deps);
return TempFileUtils.readAllFiles(tmpdir);
}
return Collections.EMPTY_LIST;
} finally {
if (tmpdir != null) {
TempFileUtils.deleteDir(tmpdir);
}
LOG.fine("generate() terminated");
}
}
/*
* @see org.argouml.uml.generator.CodeGenerator#generateFiles(
* java.util.Collection, java.lang.String, boolean)
*/
public Collection generateFiles(Collection elements, String path,
boolean deps) {
LOG.fine("generateFiles() called");
// TODO: 'deps' is ignored here
for (Object element : elements) {
generateFile(element, path);
}
return TempFileUtils.readFileNames(new File(path));
}
/*
* @see org.argouml.uml.generator.CodeGenerator#generateFileList(
* java.util.Collection, boolean)
*/
public Collection generateFileList(Collection elements, boolean deps) {
LOG.fine("generateFileList() called");
// TODO: 'deps' is ignored here
File tmpdir = null;
try {
tmpdir = TempFileUtils.createTempDir();
for (Object element : elements) {
generateFile(element, tmpdir.getName());
}
return TempFileUtils.readFileNames(tmpdir);
} finally {
if (tmpdir != null) {
TempFileUtils.deleteDir(tmpdir);
}
}
}
}