GeneratorCpp.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:
* Luis Sergio Oliveira (euluis)
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2009 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.cpp.generator;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.UUID;
import java.util.Vector;
import java.util.logging.Logger;
import org.argouml.configuration.Configuration;
import org.argouml.configuration.ConfigurationKey;
import static org.argouml.language.cpp.profile.ProfileCpp.*;
import static org.argouml.model.Model.*;
import org.argouml.uml.DocumentationManager;
import org.argouml.uml.UUIDHelper;
import org.argouml.uml.generator.CodeGenerator;
import org.argouml.uml.generator.SourceUnit;
/**
* Class to generate C++ source code.
*
* WARNING: Don't know if this is a threat (and I think it's not), but this
* class is NOT THREAD SAFE. DO NOT CALL METHODS FROM DIFFERENT THREADS.
* At the moment, it works, but this is just in case someone in the future
* tries to generate 1265 files in parallel and guesses why it doesn't work :-)
*/
public class GeneratorCpp implements CodeGenerator {
/**
* The logger.
*/
private static final Logger LOG = Logger.getLogger(
GeneratorCpp.class.getName());
static final String LANGUAGE_NAME = "cpp";
// Customizable variables
private boolean verboseDocs = false;
private boolean lfBeforeCurly = false;
private String indent = " "; // customizable (non final) indent
private boolean hdrGuardUpperCase = false;
private boolean hdrGuardGUID = false;
// Configuration keys for the above configurable variables
private static final ConfigurationKey KEY_CPP_INDENT =
Configuration.makeKey(LANGUAGE_NAME, "indent");
private static final ConfigurationKey KEY_CPP_LF_BEFORE_CURLY =
Configuration.makeKey(LANGUAGE_NAME, "lf-before-curly");
private static final ConfigurationKey KEY_CPP_VERBOSE_COMM =
Configuration.makeKey(LANGUAGE_NAME, "verbose-comments");
private static final ConfigurationKey KEY_CPP_SECT =
Configuration.makeKey(LANGUAGE_NAME, "sections");
private static final ConfigurationKey KEY_CPP_HEADER_GUARD_UPPERCASE =
Configuration.makeKey(LANGUAGE_NAME, "header-guard-case");
private static final ConfigurationKey KEY_CPP_HEADER_GUARD_GUID =
Configuration.makeKey(LANGUAGE_NAME, "header-guard-guid");
private static final ConfigurationKey KEY_CPP_DEFAULT_INLINE =
Configuration.makeKey(LANGUAGE_NAME, "default-inline");
private static Section sect;
/**
* Store actual namespace, to avoid unneeded curly braces.
*
* @author Achim Spangler
* @since 2002-12-07
*/
private Object actualNamespace;
/** Current classifier, for which the code is being generated.
*/
private Object currClass = null;
/**
* Set of the local files to include with #include "file.h"
* Automatically generated from classifier dependencies.
*/
private Set localInc = new TreeSet();
/**
* Set of the external files to include with #include <file.h>
* Automatically generated from classifier dependencies.
*/
private Set extInc = new TreeSet();
/**
* Set of the system files to include, like #include <vector>
* Automatically generated.
*/
private Set systemInc = new TreeSet();
/** Set of classifier that needs to be defined (i.e. #included)
*/
private Set includeCls = new LinkedHashSet();
/** Set of classifier that only needs to be predeclared
*/
private Set predeclCls = new LinkedHashSet();
/**
* System newline separator.
*/
private static final String LINE_SEPARATOR =
System.getProperty("line.separator");
/**
* C++ doesn't place visibility information for each class member
* --> sort items during generation and store visibility state
* of lastly generated member in central class variable, so that
* the appropriate lines: "public:", "protected:", "private:"
* can be created.
*
* @author Achim Spangler
* @since 2002-11-28
*/
private static final int PUBLIC_PART = 0;
private static final int PROTECTED_PART = 1;
private static final int PRIVATE_PART = 2;
private static final int[] ALL_PARTS = {
PUBLIC_PART,
PROTECTED_PART,
PRIVATE_PART,
};
private static final String[] PART_NAME = {
"public", "protected", "private"
};
/**
* C++ uses two files for each class: header (.h) with class definition
* and source (.cpp) with methods implementation
* --> two generation passes are needed.
*
* @author Achim Spangler
* @since 2002-11-28
*/
private static final int NONE_PASS = 1;
private static final int HEADER_PASS = 2;
private static final int SOURCE_PASS = 3;
private static int generatorPass = NONE_PASS;
/**
* use Tag generation for generation of: doccomment, simple tags of
* tags which are not used for document or simple tags for all.
*
* @author Achim Spangler
* @since 2002-12-05
*/
private static final int DOC_COMMENT_TAGS = 1;
private static final int ALL_BUT_DOC_TAGS = 2;
private static final int ALL_TAGS = 3;
/**
* C++ developers need to specify for parameters whether they are
* pointers or references (especially for class-types)
* -> a general check function must get the searched tag.
*
* @author Achim Spangler
* @since 2002-12-06
*/
private static final int NORMAL_MOD = 0;
private static final int REFERENCE_MOD = 1;
private static final int POINTER_MOD = 2;
private static final int CONST_MOD = 3;
private static GeneratorCpp singleton;
/**
* Prefix for names in the std namespace. Defaults to "std::",
* but could be "" if a "using namsepace std;" directive is used.
*/
private String stdPrefix = "std::";
/**
* Get the instance of the singleton for the C++ generator.
*
* @return the singleton of the generator.
*/
public static synchronized GeneratorCpp getInstance() {
if (singleton != null)
return singleton;
return new GeneratorCpp(); // the constructor will set singleton
}
/**
* Constructor.
*/
protected GeneratorCpp() {
singleton = this;
loadConfig();
}
/** Reset the generator in the initial state before
* starting to generate code.
*/
protected void cleanupGenerator() {
// clears collections of dependencies
localInc.clear();
extInc.clear();
systemInc.clear();
includeCls.clear();
predeclCls.clear();
// set currClass to null, so if it's used when it shouldn't
// it will raise a NullPointerException
currClass = null;
actualNamespace = null;
}
/** Set up the generator in order to generate the code
* for 'cls'.
* @param cls The classifier to generate the code for
*/
protected void setupGenerator(Object cls) {
cleanupGenerator();
currClass = cls;
}
/** Internal helper that generates the file content (.cpp or .h)
* and returns it as a String, without actually creating a file.
*/
private String generateFileAsString(Object o, String pathname) {
setupGenerator(o);
if (generatorPass == SOURCE_PASS && getFacade().isAInterface(o))
return ""; // don't generate the .cpp, it's useless.
String headerTop = generateHeaderTop(pathname);
String header = generateHeader(o);
// This can only be a classifier, right? - tfm
String src = generateClassifier(o);
String inlinedMethods = null;
if (generatorPass == HEADER_PASS) {
inlinedMethods = generateInlinedMethodsOutsideClass(o);
}
String footer = generateFooter();
// generate #includes and predeclarations
// this must be *after* generate()
StringBuffer incl = new StringBuffer();
if (generatorPass == SOURCE_PASS) {
localInc.add(getFacade().getName(o) + ".h");
}
generateIncludes(incl);
if (generatorPass == HEADER_PASS) {
if (incl.length() > 0) incl.append(LINE_SEPARATOR);
generatePredeclare(incl);
}
// paste all the pieces in the final result
StringBuffer result = new StringBuffer();
String guard = new String();
if (generatorPass == HEADER_PASS) {
String name = getFacade().getName(o);
String guardPack =
generateRelativePackage(o, null, "_").substring(1);
guard = name + getFileExtension().replace('.', '_');
if (guardPack.length() > 0) {
guard = guardPack + "_" + guard;
}
if (hdrGuardGUID) {
guard = guard + "_"
+ UUID.randomUUID().toString().replace("-", "_");
}
if (hdrGuardUpperCase) {
guard = guard.toUpperCase();
}
result.append("#ifndef " + guard + LINE_SEPARATOR
+ "#define " + guard
+ LINE_SEPARATOR + LINE_SEPARATOR);
}
result.append(headerTop);
result.append(incl.toString());
result.append(header);
result.append(src);
if (generatorPass == HEADER_PASS && inlinedMethods != null) {
result.append(inlinedMethods);
}
result.append(footer);
if (generatorPass == HEADER_PASS) {
result.append(LINE_SEPARATOR);
result.append("#endif" + " // " + guard);
result.append(LINE_SEPARATOR);
}
return result.toString();
}
/**
* Generate the source code (.cpp) for the given object
* @param o the object to be generated
* @return the generated code as a string
*/
String generateCpp(Object o) {
generatorPass = SOURCE_PASS;
String name =
generateRelativePackage(o, null, "/").substring(1);
if (name.length() > 0) name += "/";
name += getFacade().getName(o) + ".cpp";
String ret = generateFileAsString(o, name);
cleanupGenerator();
generatorPass = NONE_PASS;
return ret;
}
/**
* Generate the header code (.h) for the given object
* @param o the object to be generated
* @return the generated header as a string
*/
String generateH(Object o) {
generatorPass = HEADER_PASS;
String name =
generateRelativePackage(o, null, "/").substring(1);
if (name.length() > 0) name += "/";
name += getFacade().getName(o) + ".h";
String ret = generateFileAsString(o, name);
cleanupGenerator();
generatorPass = NONE_PASS;
return ret;
}
/**
* Generate the package name for the specified object,
* relative to the specified package. Use sep as the
* package separator.
* @param cls Object to generate the path for
* @param pack Generate path relative to this package
* @param sep package separator
* @return path relative to pack, if pack is a parent of
* cls, else relative to the project root. If the
* path is relative to the project root, it's prefixed
* with sep.
*/
private String generateRelativePackage(Object cls, Object pack,
String sep) {
StringBuffer packagePath = new StringBuffer();
// avoid model being used as a package name
Object parent = getFacade().getNamespace(cls);
while (parent != null && parent != pack) {
// ommit root package name; it's the model's root
Object grandParent = getFacade().getNamespace(parent);
if (grandParent != null) {
String name = getFacade().getName(parent);
if (packagePath.length() > 0) {
packagePath.insert(0, sep);
}
packagePath.insert(0, name);
}
parent = grandParent;
}
if (parent == null) { // relative to root, prefix with sep
packagePath.insert(0, sep);
}
return packagePath.toString();
}
/** 2002-11-28 Achim Spangler
* @return file extension for actual generation pass
*/
private String getFileExtension() {
if (generatorPass == HEADER_PASS) return ".h";
return ".cpp";
}
/**
* Generates the relative path for the specified classifier.
* @param cls The classifier.
* @return Returns relative path of cls (without filename).
*/
private String generatePath(Object cls) {
String packagePath =
generateRelativePackage(cls, null, CodeGenerator.FILE_SEPARATOR);
packagePath = packagePath.substring(1);
return packagePath;
}
/**
* create the needed directories for the derived appropriate pathname
* @return Returns the filename with full path of cls.
*/
private String createDirectoriesPathname(Object cls, String path) {
String name = getFacade().getName(cls);
if (name == null || name.length() == 0) {
return "";
}
if (!path.endsWith (CodeGenerator.FILE_SEPARATOR)) {
path += CodeGenerator.FILE_SEPARATOR;
}
String packagePath = generateRelativePackage(cls, null, ".");
packagePath = packagePath.substring(1);
String filename = name + getFileExtension();
int lastIndex = -1;
do {
File f = new File (path);
if (!f.isDirectory()) {
if (!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();
}
path += packagePath.substring(lastIndex + 1, index)
+ CodeGenerator.FILE_SEPARATOR;
lastIndex = index;
} while (true);
String pathname = path + filename;
//LOG.info("-----" + pathname + "-----");
return pathname;
}
/* Returns true if the given object is a class (or interface) within
* another class (not within a package).
*/
private static boolean isAInnerClass(Object cls) {
Object parent = getFacade().getNamespace(cls);
return parent != null && !getFacade().isAPackage(parent);
}
/** 2002-12-07 Achim Spangler
* @return date
*/
private String getDate() {
GregorianCalendar cal = new GregorianCalendar();
DateFormat df;
df = DateFormat.getDateInstance(DateFormat.DEFAULT);
return df.format(cal.getTime());
}
/** 2002-12-07 Achim Spangler
* @return year
*/
private String getYear() {
GregorianCalendar cal = new GregorianCalendar();
return Integer.toString(cal.get(Calendar.YEAR));
}
/** 2002-12-07 Achim Spangler
* write template content on top of file
*/
private void writeTemplate(Object cls, String path, BufferedWriter fos) {
String templatePathName = path + "/templates/";
String fileName = getFacade().getName(cls);
String tagTemplatePathName = getFacade().getTaggedValueValue(
cls, TV_NAME_TEMPLATE_PATH);
String authorTag = getFacade().getTaggedValueValue(cls,
TV_NAME_AUTHOR);
String emailTag = getFacade().getTaggedValueValue(cls,
TV_NAME_EMAIL);
if (tagTemplatePathName != null && tagTemplatePathName.length() > 0)
templatePathName = tagTemplatePathName;
if (generatorPass == HEADER_PASS) {
templatePathName = templatePathName + "header_template";
fileName = fileName + ".h";
}
else {
templatePathName = templatePathName + "cpp_template";
fileName = fileName + ".cpp";
}
File templateFile = new File(templatePathName);
if (templateFile.exists()) {
boolean eof = false;
BufferedReader templateFileReader = null;
try {
// TODO: This is using the default platform character encoding
// specifying an encoding will produce more predictable results
templateFileReader =
new BufferedReader(new FileReader(
templateFile.getAbsolutePath()));
while (!eof) {
String lineStr = templateFileReader.readLine();
if (lineStr == null) {
eof = true;
} else {
StringBuffer line = new StringBuffer(lineStr);
replaceToken(line, "|FILENAME|", fileName);
replaceToken(line, "|DATE|", getDate());
replaceToken(line, "|YEAR|", getYear());
replaceToken(line, "|AUTHOR|", authorTag);
replaceToken(line, "|EMAIL|", emailTag);
fos.write(line + LINE_SEPARATOR);
}
}
templateFileReader.close();
}
catch (IOException exp) { }
finally {
try {
if (templateFileReader != null) templateFileReader.close();
}
catch (IOException exp) {
LOG.severe("FAILED: " + templateFile.getPath());
}
}
}
}
/**
* Replace the first occurrences of tokenName with tokenValue.
*
* @param line is the line where we do the replacing.
* @param tokenName is the string we search for.
* @param tokenValue is the value we replace.
*/
private void replaceToken(StringBuffer line, String tokenName,
String tokenValue) {
int tokenStart;
tokenStart = line.toString().indexOf(tokenName);
if ((tokenStart != -1)
&& (tokenValue != null && tokenValue.length() > 0)) {
line.replace(tokenStart,
tokenStart + tokenName.length(),
tokenValue);
}
}
/** 2002-11-28 Achim Spangler
* separate constant Header Top into function
*/
private String generateHeaderTop(String pathname) {
StringBuffer sb = new StringBuffer(80);
//TODO: add user-defined copyright
if (verboseDocs) {
// FIXME: replace hard-coded path separator with
// CodeGenerator.FILE_SEPARATOR
sb.append("// FILE: ").append(pathname.replace('\\', '/'));
sb.append(LINE_SEPARATOR).append(LINE_SEPARATOR);
}
return sb.toString();
}
/**
* Helper for checkIncludeNeeded4Element. Returns true if
* an #include is needed.
*/
private boolean checkInclude4UsageIndirection(boolean isIndirect,
String usageTag) {
if (isIndirect) {
// needs only to be included in the .cpp
if (usageTag.indexOf("header") != -1) {
// but user explicitly requested its presence in the header
return generatorPass == HEADER_PASS;
} else {
return generatorPass == SOURCE_PASS;
}
}
// must be included in the header, whatever usageTag is
return generatorPass == HEADER_PASS;
}
private boolean checkIncludeNeeded4Element(Object cls) {
String usageTag = "";
boolean predeclareCandidate = false;
Iterator iter = getFacade().getTaggedValues(cls);
while (iter.hasNext()) {
Object tv = iter.next();
String tag = getFacade().getTagOfTag(tv);
if (tag != null) {
if (tag.equals(TV_NAME_USAGE)) {
usageTag = getFacade().getValueOfTag(tv);
}
if (tag.indexOf(TV_NAME_REFERENCE) != -1 || tag.equals("&")
|| tag.indexOf(TV_NAME_POINTER) != -1
|| tag.equals("*")) {
predeclareCandidate = true;
}
}
}
return checkInclude4UsageIndirection(predeclareCandidate, usageTag);
}
private StringBuffer generateIncludes(StringBuffer sb) {
for (Iterator it = systemInc.iterator(); it.hasNext(); ) {
String inc = (String) it.next();
sb.append("#include <");
sb.append(inc).append('>').append(LINE_SEPARATOR);
}
// separate system from external headers
if (systemInc.size() > 0) sb.append(LINE_SEPARATOR);
for (Iterator it = extInc.iterator(); it.hasNext(); ) {
String inc = (String) it.next();
sb.append("#include <");
sb.append(inc).append('>').append(LINE_SEPARATOR);
}
// separate external from local headers
if (extInc.size() > 0) sb.append(LINE_SEPARATOR);
for (Iterator it = localInc.iterator(); it.hasNext(); ) {
String inc = (String) it.next();
sb.append("#include \"").append(inc).append("\"" + LINE_SEPARATOR);
}
return sb;
}
private StringBuffer generatePredeclare(StringBuffer sb) {
for (Iterator it = predeclCls.iterator(); it.hasNext(); ) {
Object cls = it.next();
String name = getFacade().getName(cls);
sb.append(generateHeaderPackageStart(cls));
sb.append("class ").append(name);
sb.append(";").append(LINE_SEPARATOR);
}
sb.append(generateHeaderPackageEnd());
return sb;
}
/**
* Parses header_incl or source_incl tags and adds the
* user-specified headers to localInc or systemInc.
* @param cls The classifier which code is being generated for.
* @param source if true parses source_incl tags, else header_incl.
*/
private void addUserHeaders(Object cls, boolean source) {
Iterator iter = getFacade().getTaggedValues(cls);
String tagPrefix;
if (source)
tagPrefix = TV_NAME_SOURCE_INCL;
else
tagPrefix = TV_NAME_HEADER_INCL;
while (iter.hasNext()) {
Object tv = iter.next();
String tag = getFacade().getTagOfTag(tv);
if (tag != null && tag.equals(tagPrefix)) {
String name = getFacade().getValueOfTag(tv);
if (name.length() > 2 && name.charAt(0) == '<') {
systemInc.add(name.substring(1, name.length() - 1));
} else if (name.length() > 2 && name.charAt(0) == '"') {
localInc.add(name.substring(1, name.length() - 1));
} else if (name.length() > 0) { // skip empty values
localInc.add(name);
}
}
}
}
/**
* Adds dep to the set of dependencies of currClass. If predecl
* then only a predeclaration is generated, else an #include.
* @param dep The classifier whose currClass depends on.
* @param predecl If true then only a predeclaration is needed
*/
private void addDependency(Object dep, boolean predecl) {
if (generatorPass == NONE_PASS) {
return; // skip dependencies if generating notation
}
if (!(getFacade().isAClass(dep)) && !(getFacade().isAInterface(dep))) {
// Do nothing for things such as datatypes, etc.
// TODO: check for namespace when using directives are implemented
return;
}
if (predecl && !includeCls.contains(dep)) {
if (generatorPass == HEADER_PASS) {
predeclCls.add(dep);
}
} else {
if (predeclCls.contains(dep)) {
predeclCls.remove(dep);
}
if (includeCls.add(dep)) {
// dep was not already in includeCls
Object ns = getFacade().getNamespace(currClass);
String name = getFacade().getName(dep);
// use '/', not FILE_SEPARATOR (this is intentional)
String path =
generateRelativePackage(dep, ns, "/");
Set inc = localInc;
if (path.startsWith("/")) { // external include
path = path.substring(1); // remove leading /
inc = extInc;
}
if (path.length() > 0) {
inc.add(path + "/" + name + ".h");
} else {
inc.add(name + ".h");
}
}
}
}
private String generateHeaderPackageStartSingle(Object pkg) {
StringBuffer sb = new StringBuffer(30);
String packageName = getFacade().getName(pkg);
StringTokenizer st = new StringTokenizer(packageName, ".");
String token = "";
sb.append(generateTaggedValues(pkg, DOC_COMMENT_TAGS));
while (st.hasMoreTokens()) {
token = st.nextToken();
// create line: namespace FOO {"
sb.append("namespace ").append(token).append(" {")
.append(LINE_SEPARATOR);
}
return sb.toString();
}
private String generateHeaderPackageEndSingle(Object pkg) {
StringBuffer sb = new StringBuffer(30);
String packageName = getFacade().getName(pkg);
StringTokenizer st = new StringTokenizer(packageName, ".");
String token = "";
while (st.hasMoreTokens()) {
token = st.nextToken();
StringBuffer tempBuf = new StringBuffer(20);
String absoluteName = generatePackageAbsoluteName(pkg);
if (absoluteName.indexOf(token) != -1) {
absoluteName =
absoluteName.substring(0,
(absoluteName.indexOf(token)
+ token.length()));
}
// create line: namespace FOO {"
tempBuf.append("} /* End of namespace ").append(absoluteName);
tempBuf.append(" */").append(LINE_SEPARATOR);
sb.insert(0, tempBuf.toString());
}
return sb.toString();
}
private String generatePackageAbsoluteName(Object pkg) {
String pack = generateRelativePackage(pkg, null, "::");
pack = pack.substring(2); // remove leading ::
if (pack.length() > 0) {
pack += "::";
}
return pack + getFacade().getName(pkg);
}
/** Generate the name of item with package prefix relative to localPkg.
* localPkg may be null, meaning the global namespace.
*/
private String generateNameWithPkgSelection(Object item, Object localPkg) {
if (item == null) {
return "void ";
}
Object pkg = null;
if (getFacade().isADataType(item)) {
return getFacade().getName(item);
} else if (getFacade().isAParameter(item)
|| getFacade().isAAttribute(item)
|| getFacade().isAAssociationEnd(item)
|| getFacade().isAClassifier(item)) {
pkg = getNamespaceWithoutModel(item);
}
if (pkg == null) {
return getFacade().getName(item);
}
String packPrefix = generateRelativePackage(item, localPkg, "::");
if (packPrefix.length() > 0) {
packPrefix += "::";
}
return packPrefix + getFacade().getName(item);
}
/** Generate name with package specs, relative to actualNamespace.
*/
private String generateNameWithPkgSelection(Object item) {
Object pkg = actualNamespace;
String name = generateNameWithPkgSelection(item, pkg);
if (name.startsWith("::")) {
name = name.substring(2); // remove leading ::
// the leading :: is just ugly to see, but it could
// be left there to emphasize that item is in the
// global namespace
}
return name;
}
/** Generate the code to go from the current namespace to cls's one
*/
private String generateHeaderPackageStart(Object cls) {
StringBuffer sb = new StringBuffer(80);
if (actualNamespace != null) {
Object lastSearch = actualNamespace;
// iterate while fromSearch != null, but iterate one time
// when it is null too, because it's the global namespace
for (Object fromSearch = actualNamespace;
fromSearch != null;
lastSearch = getNamespaceWithoutModel(fromSearch)) {
fromSearch = lastSearch;
StringBuffer contPath = new StringBuffer(80);
Object toSearch = getNamespaceWithoutModel(cls);
for (; (toSearch != null) && (toSearch != fromSearch);
toSearch = getNamespaceWithoutModel(toSearch)) {
contPath.insert(0,
generateHeaderPackageStartSingle(toSearch));
}
if (toSearch == fromSearch) {
sb.append(contPath.toString());
break;
}
// close one namespace
sb.append(generateHeaderPackageEndSingle(fromSearch));
}
}
else { // initial start
for (Object toSearch = getNamespaceWithoutModel(cls);
toSearch != null;
toSearch = getNamespaceWithoutModel(toSearch)) {
sb.insert(0, generateHeaderPackageStartSingle(toSearch));
}
}
actualNamespace = getNamespaceWithoutModel(cls);
return sb.toString();
}
/**
* Retrieve the namespace of the given model element, excluding the model,
* which shouldn't be considered a namespace.
* @param me the model element for which to get the namespace
* @return the namespace if it exists or null if the model is the
* containing namespace for <code>me</code>
*/
private Object getNamespaceWithoutModel(Object me) {
Object parent = getFacade().getNamespace(me);
if (parent != null && getFacade().getNamespace(parent) != null)
return parent;
return null;
}
private String generateHeaderPackageEnd() {
StringBuffer sb = new StringBuffer(20);
for (Object closeIt = actualNamespace;
closeIt != null;
closeIt = getNamespaceWithoutModel(closeIt)) {
sb.append(generateHeaderPackageEndSingle(closeIt));
}
actualNamespace = null;
return sb.toString();
}
/* This generates the file internal header, not the .h file!
* That is, things before the class declaration.
*/
private String generateHeader(Object cls) {
StringBuffer sb = new StringBuffer(240);
addUserHeaders(cls, generatorPass == SOURCE_PASS);
if (getNamespaceWithoutModel(cls) != null) {
String pkgstart = generateHeaderPackageStart(cls);
if (pkgstart.length() > 0) {
sb.append(LINE_SEPARATOR);
sb.append(pkgstart);
}
}
return sb.toString();
}
/*
* This generates all the things that go after the class declaration.
*/
private String generateFooter() {
StringBuffer sb = new StringBuffer(80);
sb.append(generateHeaderPackageEnd());
if (sb.length() > 0) {
sb.insert(0, LINE_SEPARATOR);
}
return sb.toString();
}
/** 2002-11-28 Achim Spangler
* separate generation of Operation Prefix from generateOperation
* so that generateOperation is language independent
*/
private String generateOperationPrefix(Object op) {
StringBuffer sb = new StringBuffer(80);
// c++ doesn't have any builtin construct for concurrency
//sb.append(generateConcurrency(op));
if (generatorPass != SOURCE_PASS) {
// make all operations to virtual - as long as they are not "leaf"
if (getFacade().isLeaf(op) && !getFacade().isRoot(op)) {
// there's no way to make a leaf method that it's not root in
// c++, so warn the user and ignore the 'root' attribute
// (or it may be better to ignore the 'leaf' attribute?)
LOG.warning(op + " is leaf but not root: "
+ "C++ can't handle this properly");
LOG.warning(" Ignoring the 'root' attribute");
}
// generate a function as virtual, if it can be overridden
// or override another function AND if this function is
// not marked as static, which disallows "virtual"
// alternatively every abstract function is defined as
// virtual
if ((!getFacade().isLeaf(op) && !getFacade().isConstructor(op)
&& (!(getFacade().isStatic(op))))
|| (getFacade().isAbstract(op))) {
sb.append("virtual ");
}
sb.append(generateScope(op));
}
return sb.toString();
}
/** 2002-11-28 Achim Spangler
* separate generation of Operation Suffix from generateOperation
* so that generateOperation is language independent
*/
private String generateOperationSuffix(Object op) {
StringBuffer sb = new StringBuffer(80);
sb.append(generateOperationChangeability(op));
sb.append(generateAbstractness(op));
return sb.toString();
}
/** 2002-11-28 Achim Spangler
* separate generation of Operation Name from generateOperation
* so that generateOperation is language independent
* -> for C++: if we create .cpp we must prepend Owner name
*
* @param sb Where to put the result.
*/
private void generateOperationNameAndTestForConstructor(Object op,
StringBuffer sb) {
if (generatorPass == SOURCE_PASS) {
Object cls = getFacade().getOwner(op);
String prefix = new String();
while (!getFacade().isAPackage(cls)) {
prefix = getFacade().getName(cls) + "::" + prefix;
cls = getFacade().getNamespace(cls);
}
sb.append(prefix);
}
String name;
if (getFacade().isConstructor(op)) {
name = getFacade().getName(getFacade().getOwner(op));
} else if (isDestructor(op)) {
name = "~" + getFacade().getName(getFacade().getOwner(op));
} else {
name = getFacade().getName(op);
}
sb.append(name);
}
private boolean isDestructor(Object op) {
return getExtensionMechanismsHelper().hasStereotype(op, "destroy");
}
/**
* Generate code for an operation.
*
* 2002-11-28 Achim Spangler
* modified version from Jaap Branderhorst
* -> generateOperation is language independent and separates
* different tasks
* @param op The operation for which to generate code.
* @param documented If the documentation should be included in the
* generated code or not.
* @return The C++ code for the operation.
*/
public String generateOperation(Object op, boolean documented) {
// generate nothing for abstract functions, if we generate the
// source .cpp file at the moment
if ((generatorPass == SOURCE_PASS) && (getFacade().isAbstract(op))) {
return "";
}
StringBuffer sb = new StringBuffer(80);
StringBuffer nameBuffer = new StringBuffer(20);
String operationIndent = (generatorPass == HEADER_PASS) ? indent : "";
generateOperationNameAndTestForConstructor(op, nameBuffer);
// if generating a file always document
if (documented || generatorPass != NONE_PASS) {
// generate DocComment from tagged values
String tv = generateTaggedValues (op, DOC_COMMENT_TAGS);
if (tv != null && tv.length() > 0) {
sb.append (LINE_SEPARATOR).append(operationIndent).append (tv);
}
}
sb.append(operationIndent)
.append(generateOperationPrefix(op));
// pick out return type
Collection returnParams = getCoreHelper().getReturnParameters(op);
Object rp;
if (returnParams.size() == 0) {
rp = null;
} else {
rp = returnParams.iterator().next();
}
if (returnParams.size() > 1) {
LOG.warning("C++ generator only handles one return parameter"
+ " - Found " + returnParams.size()
+ " for " + getFacade().getName(op));
}
if (!getFacade().isConstructor(op) && !isDestructor(op)) {
Inline inlineStyle = Inline.getInlineOperationModifierType(op);
sb.append(inlineStyle.getInlineKeyword4Declaration());
if (rp != null) {
Object returnType = getFacade().getType(rp);
if (returnType == null) {
sb.append("void ");
}
else if (returnType != null) {
sb.append(generateNameWithPkgSelection(returnType))
.append(' ');
/* fixing 2862 - apply modifiers,
* i.e. pointer or reference TV */
sb.append(generateAttributeParameterModifier(rp));
}
boolean predecl = !checkIncludeNeeded4Element(rp);
addDependency(returnType, predecl);
}
}
// name and params
Vector params = new Vector(getFacade().getParameters(op));
params.remove(rp); // If there are several return parameters, just
// the one found above will be removed.
sb.append(nameBuffer.toString()).append('(');
if (params != null) {
boolean first = true;
for (int i = 0; i < params.size(); i++) {
Object p = params.elementAt (i);
if (!first) sb.append(", ");
sb.append(generateParameter(p));
first = false;
}
}
String suffix = generateOperationSuffix(op);
if (suffix.equals(""))
sb.append(")");
else
sb.append(") ").append(suffix);
return sb.toString();
}
/** 2002-12-06 Achim Spangler
* check if a parameter is tagged as pointer or reference (not
* part of UML - as far as author knows - but important for C++
* developers)
* @param elem element to check
* @return one of NORMAL_MOD, REFERENCE_MOD, POINTER_MOD, or -1 if
* no specific tag is found
*/
private int getAttributeModifierType(Object elem) {
// first check whether the parameter shall be a pointer of reference
Iterator iter = getFacade().getTaggedValues(elem);
while (iter.hasNext()) {
Object tv = iter.next();
String tag = getFacade().getTagOfTag(tv);
String val = getFacade().getValueOfTag(tv);
if (tag != null) {
if (tag.equals(TV_NAME_REFERENCE) || tag.equals("&")) {
return val.equals("false") ? NORMAL_MOD
: REFERENCE_MOD;
} else if (tag.equals(TV_NAME_POINTER) || tag.equals("*")) {
return val.equals("false") ? NORMAL_MOD
: POINTER_MOD;
}
}
}
return -1; /* no tag found */
}
private String generateAttributeParameterModifier(Object attr,
String def) {
int modType = getAttributeModifierType(attr);
// if attr has an abstract type it must be pointer or reference
if (modType == NORMAL_MOD || modType == -1) {
// this is used for association classes to; skip them
if (!getFacade().isAAssociationClass(attr)) {
Object type = getFacade().getType(attr);
if (type == null) {
// model corrupt (this really happened -- aslo)
LOG.severe(attr + " has no type!");
return "";
}
if (getFacade().isAbstract(type)
|| getFacade().isAInterface(type)) {
if (modType == NORMAL_MOD) {
// user explicitly requested no modifier
LOG.warning("Requested no reference or pointer "
+ "modifier, but");
LOG.warning("\t" + type + " cannot be instantiated, "
+ "using reference");
}
modType = REFERENCE_MOD;
}
}
}
if (modType == NORMAL_MOD) {
return "";
} else if (modType == REFERENCE_MOD) {
return "&";
} else if (modType == POINTER_MOD) {
return "*";
} else if (def.length() == 0) {
if (getFacade().isAParameter(attr)
&& (getDirectionKind().getOutParameter().equals(
getFacade().getKind(attr))
|| getDirectionKind().getInOutParameter().equals(
getFacade().getKind(attr)))) {
// out or inout parameters are defaulted to reference if
// not specified else
return "&";
}
}
return def;
}
/**
* @param elem element to check
* @return CONST_MOD or -1 if no specific tag is found
*/
private int getConstAttributeModifierType(Object elem) {
Iterator iter = getFacade().getTaggedValues(elem);
while (iter.hasNext()) {
Object tv = iter.next();
String tag = getFacade().getTagOfTag(tv);
String val = getFacade().getValueOfTag(tv);
if (tag != null) {
if (tag.equals(TV_NAME_CONST)) {
return val.equals("false") ? NORMAL_MOD : CONST_MOD;
}
}
}
return -1; /* no tag found */
}
private String generateConstAttributeParameterModifier(Object attr) {
if (getConstAttributeModifierType(attr) == CONST_MOD) {
return "const";
}
return null;
}
private String generateAttributeParameterModifier(Object attr) {
return generateAttributeParameterModifier(attr, "");
}
public String generateAttribute(Object attr, boolean documented) {
StringBuffer sb = new StringBuffer(80);
// list tagged values for documentation
if (documented || generatorPass != NONE_PASS) {
String tv = generateTaggedValues (attr, DOC_COMMENT_TAGS);
if (tv != null && tv.length() > 0) {
sb.append (LINE_SEPARATOR).append(indent).append(tv).
append(indent);
}
}
// cat.info("generate Visibility for Attribute");
sb.append(generateVisibility(attr));
sb.append(generateScope(attr));
sb.append(generateStructuralFeatureChangeability(attr));
sb.append(
generateMultiplicity(attr, getFacade().getName(attr),
getFacade().getMultiplicity(attr),
generateAttributeParameterModifier(attr)));
sb.append(";");
if (generatorPass != NONE_PASS)
sb.append(LINE_SEPARATOR);
// add the type of the attribute in the dependency list
boolean predecl = !checkIncludeNeeded4Element(attr);
addDependency(getFacade().getType(attr), predecl);
return sb.toString();
}
private String generateParameter(Object param) {
StringBuffer sb = new StringBuffer(20);
//TODO: qualifiers (e.g., const)
// generate const for references or pointers which are
// defined as IN - other qualifiers are not important for
// C++ parameters
Object type = getFacade().getType(param);
sb.append(generateParameterChangeability(param));
//TODO: stereotypes...
String constModifier = generateConstAttributeParameterModifier(param);
if (constModifier != null) {
sb.append(constModifier).append(' ');
}
sb.append(generateNameWithPkgSelection(type));
sb.append(' ');
sb.append(generateAttributeParameterModifier(param));
sb.append(getFacade().getName(param));
// insert default value, if we are generating the header or notation
if (generatorPass != SOURCE_PASS) {
Object defvalObj = getFacade().getDefaultValue(param);
if (defvalObj != null) {
String defval =
getFacade().getBody(defvalObj).toString().trim();
if (defval.length() > 0) {
sb.append(" = ").append(defval);
}
}
}
// add the type of the parameter in the dependency list
boolean predecl = !checkIncludeNeeded4Element(param);
addDependency(type, predecl);
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) {
StringBuffer sb = new StringBuffer (80);
// Add the comments for this classifier first.
sb.append(LINE_SEPARATOR)
.append (DocumentationManager.getComments(cls));
// list tagged values for documentation
String tv = generateTaggedValues (cls, DOC_COMMENT_TAGS);
if (tv != null && tv.length() > 0) {
sb.append (LINE_SEPARATOR).append (indent).append (tv);
}
sb.append(generateClassifierNameAndAncestors(cls));
// add opening brace
if (lfBeforeCurly) {
sb.append(LINE_SEPARATOR).append('{');
} else if (generatorPass != SOURCE_PASS) {
sb.append(" {");
}
// list tagged values for documentation
tv = generateTaggedValues (cls, ALL_BUT_DOC_TAGS);
if (tv != null && tv.length() > 0) {
sb.append(LINE_SEPARATOR).append (indent).append (tv);
}
return sb;
}
/**
* Generate the classifier name and ancestors, i.e., the keyword defining
* the class, the class name and the name of the classes from which this
* one derives.
*
* @param cls the classifier for which to generate the name and ancestors
*
* @return the generated name and ancestors
*/
public StringBuffer generateClassifierNameAndAncestors(Object cls) {
StringBuffer sb = new StringBuffer (80);
// don't create class-Start for implementation in .cpp
if (generatorPass == SOURCE_PASS) return sb;
String sClassifierKeyword;
if (getFacade().isAClass(cls) || getFacade().isAInterface(cls)) {
sClassifierKeyword = "class";
} else {
return null; // actors, use cases etc.
}
boolean hasBaseClass = false;
// add classifier keyword and classifier name
sb.append(sClassifierKeyword).append(" ");
sb.append(getFacade().getName(cls));
// add base class/interface
String baseClass =
generateGeneralization(getFacade().getGeneralizations(cls));
if (!baseClass.equals ("")) {
sb.append (" : ")
.append (baseClass);
hasBaseClass = true;
}
// add implemented interfaces, if needed (uml: realizations)
if (getFacade().isAClass(cls)) {
String interfaces = generateSpecification(cls);
if (!interfaces.equals ("")) {
if (!hasBaseClass) sb.append (" : ");
else sb.append (", ");
sb.append (interfaces);
}
}
return sb;
}
private StringBuffer generateClassifierEnd(Object cls) {
StringBuffer sb = new StringBuffer();
if (getFacade().isAClass(cls) || getFacade().isAInterface(cls)) {
if ((verboseDocs) && (generatorPass != SOURCE_PASS)) {
String classifierkeyword = null;
if (getFacade().isAClass(cls)) {
classifierkeyword = "class";
} else {
classifierkeyword = "class";
}
sb.append(LINE_SEPARATOR)
.append("//end of ")
.append(classifierkeyword)
.append(" ").append(getFacade().getName(cls))
.append(LINE_SEPARATOR);
}
if (generatorPass != SOURCE_PASS)
sb.append("};").append(LINE_SEPARATOR);
}
return sb;
}
/**
* Generate three parts under public, protected and private visibility,
* adding the visibility keywords on top of each part.
* @param parts the parts to output
* @return the composed parts
*/
private String generateAllParts(StringBuffer[] parts) {
StringBuffer sb = new StringBuffer();
// generate all parts in order: public, protected, private
for (int i = 0; i < ALL_PARTS.length; i++) {
if (parts[i].toString().trim().length() > 0) {
if (generatorPass != SOURCE_PASS) {
if (i != 0) sb.append(LINE_SEPARATOR);
sb.append(' ').append(PART_NAME[i]).append(':');
sb.append(LINE_SEPARATOR);
}
sb.append(parts[i]);
}
}
return sb.toString();
}
private int getVisibilityPart(Object o) {
if (getFacade().isPublic(o)) {
return PUBLIC_PART;
} else if (getFacade().isProtected(o)) {
return PROTECTED_PART;
} else if (getFacade().isPrivate(o)) {
return PRIVATE_PART;
} else {
LOG.warning(getFacade().getName(o)
+ " is not public, nor protected, "
+ "nor private!!! (ignored)");
return -1;
}
}
/* Indent each line of the given string by n indent spaces.
*/
private String indentString(String s, int n) {
String ind = new String();
for (; n > 0; n--)
ind += indent;
// This works only with jdk 1.5: return s.replace("\n", "\n" + ind);
StringBuffer result = new StringBuffer();
// FIXME: replace hard-coded '\n' with LINE_SEPARATOR
for (int i = s.indexOf('\n'); i != -1; i = s.indexOf('\n')) {
result.append(ind).append(s.substring(0, i + 1));
s = s.substring(i + 1);
}
if (s.length() > 0) {
result.append(ind).append(s);
}
return result.toString();
}
/*
* Generates code for a classifier, for classes and interfaces only
* at the moment.
*/
public String generateClassifier(Object cls) {
// If we're in the notation pane, do a special trick
// to show both header and source
if (generatorPass == NONE_PASS && (getFacade().isAClass(cls)
|| getFacade().isAInterface(cls))) {
// for inner classes, show source of top level class
// TODO: don't know if this is the best thing to do
while (isAInnerClass(cls)) {
cls = getFacade().getNamespace(cls);
}
StringBuffer sb = new StringBuffer();
String name = getFacade().getName(cls);
sb.append("// ").append(name).append(".h");
sb.append(LINE_SEPARATOR);
sb.append(generateH(cls));
if (getFacade().isAClass(cls)) {
sb.append(LINE_SEPARATOR);
sb.append("// ").append(name).append(".cpp");
sb.append(LINE_SEPARATOR);
sb.append(generateCpp(cls));
}
return sb.toString();
}
StringBuffer returnValue = new StringBuffer();
StringBuffer start = generateClassifierStart(cls);
if (((start != null) && (start.length() > 0))
|| (generatorPass == SOURCE_PASS)) {
StringBuffer typedefs = generateGlobalTypedefs(cls);
StringBuffer body = generateClassifierBody(cls);
StringBuffer end = generateClassifierEnd(cls);
returnValue.append((typedefs != null) ? typedefs.toString() : "");
returnValue.append(start);
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();
}
/** 2002-12-12 Achim Spangler
* generate global typedefs
*/
private StringBuffer generateGlobalTypedefs(Object cls) {
StringBuffer sb = new StringBuffer();
if (getFacade().isAClass(cls) || getFacade().isAInstance(cls)) {
// add typedefs
if (generatorPass == HEADER_PASS) {
Collection globalTypedefStatements =
findTagValues(cls, "typedef_global_header");
if (!globalTypedefStatements.isEmpty()) {
sb.append("// global type definitions for header defined "
+ "by Tag entries in ArgoUML");
sb.append(LINE_SEPARATOR);
sb.append("// Result: typedef <typedef_global_header> "
+ "<tag_value>;");
sb.append(LINE_SEPARATOR);
Iterator typedefEnum =
globalTypedefStatements.iterator();
while (typedefEnum.hasNext()) {
sb.append("typedef ").append(typedefEnum.next());
sb.append(";").append(LINE_SEPARATOR);
}
}
}
else {
Collection globalTypedefStatements =
findTagValues(cls, "typedef_global_source");
if (!globalTypedefStatements.isEmpty()) {
sb.append("// global type definitions for class "
+ "implementation in source file defined by Tag "
+ "entries in ArgoUML");
sb.append(LINE_SEPARATOR);
sb.append("// Result: typedef <typedef_global_source> "
+ "<tag_value>;");
sb.append(LINE_SEPARATOR);
Iterator typedefEnum = globalTypedefStatements.iterator();
while (typedefEnum.hasNext()) {
sb.append("typedef ").append(typedefEnum.next());
sb.append(";").append(LINE_SEPARATOR);
}
}
}
}
return sb;
}
/**
* Generates the attributes of the body of a class or interface.
* @param cls
* @param sb Where to put the result.
*/
private void generateClassifierBodyAttributes(Object cls,
StringBuffer sb) {
Collection attrs = getFacade().getAttributes(cls);
if (attrs.isEmpty() || (generatorPass != HEADER_PASS)) {
return;
}
String tv = null; // helper for tagged values
sb.append(LINE_SEPARATOR);
if (verboseDocs && getFacade().isAClass(cls)) {
sb.append(indent).append("// Attributes").append(LINE_SEPARATOR);
}
// generate attributes in order public, protected, private
StringBuffer part[] = new StringBuffer[ALL_PARTS.length];
for (int i = 0; i < part.length; i++)
part[i] = new StringBuffer(80);
Iterator attrIter = attrs.iterator();
while (attrIter.hasNext()) {
Object attr = attrIter.next();
int i = getVisibilityPart(attr);
part[i].append(indent).append(generateAttribute(attr, false));
tv = generateTaggedValues(attr, ALL_BUT_DOC_TAGS);
if (tv != null && tv.length() > 0) {
part[i].append(indent).append(tv);
}
}
sb.append(generateAllParts(part));
}
/**
* Generates the association ends of the body of a class or interface.
* @param cls The classifier to generate.
* @param sb Where to put the result.
*/
private void generateClassifierBodyAssociations(Object cls,
StringBuffer sb) {
if (generatorPass == SOURCE_PASS)
return;
Collection ends = getFacade().getAssociationEnds(cls);
if (!ends.isEmpty()) {
sb.append(LINE_SEPARATOR);
if (verboseDocs && getFacade().isAClass(cls)) {
sb.append(indent).append("// Associations").append(
LINE_SEPARATOR);
}
StringBuffer part[] = new StringBuffer[3];
for (int i = 0; i < ALL_PARTS.length; i++)
part[i] = new StringBuffer(80);
Iterator endEnum = ends.iterator();
while (endEnum.hasNext()) {
Object ae = endEnum.next();
Object a = getFacade().getAssociation(ae);
AssociationEndHandler aeHandler = new AssociationEndHandler(
ae);
try {
aeHandler.pre();
generateAssociationFrom(a, ae, part);
} finally {
aeHandler.post();
}
}
sb.append(generateAllParts(part));
}
// if this is an association class, generate attributes for
// all the AssociationEnds
if (getFacade().isAAssociationClass(cls)) {
if (verboseDocs) {
sb.append(LINE_SEPARATOR).append(indent);
sb.append("// AssociationClass associated classes");
}
// make all ends public... does it make sense?
// should we declare friend all the associated and make
// these protected? (private is too restrictive anyway, IMHO)
// TODO: make it configurable, with a tag
sb.append(LINE_SEPARATOR).append(" public:").append(LINE_SEPARATOR);
ends = getFacade().getConnections(cls);
Iterator iter = ends.iterator();
while (iter.hasNext()) {
Object ae = iter.next();
sb.append(LINE_SEPARATOR);
String comment = generateConstraintEnrichedDocComment(cls, ae);
if (comment.length() > 0)
sb.append(comment).append(indent);
String n = getFacade().getName(ae);
String name;
Object type = getFacade().getType(ae);
if (n != null && n.length() > 0) {
name = n;
} else {
name = "my" + generateClassifierRef(type);
}
sb.append(generateNameWithPkgSelection(type));
sb.append(generateAttributeParameterModifier(ae));
sb.append(" ").append(name);
sb.append(";").append(LINE_SEPARATOR);
// add the type of the association end in the dependency list
addDependency(type, !checkIncludeNeeded4Element(type));
String tv = generateTaggedValues(ae, ALL_BUT_DOC_TAGS);
if (tv != null && tv.length() > 0) {
sb.append(indent).append(tv);
}
}
}
}
/**
* Check whether an operation body shall be generated within the actual
* pass. This is normally done during the implementation path.
* But if the Tag "inline" exists, the method body shall be defined as
* as inline in header file
* @return true -> generate body in actual path
*/
private boolean checkGenerateOperationBody(Object op) {
boolean result = !((generatorPass == HEADER_PASS)
|| (getFacade().isAbstract(op))
|| (getFacade().isAInterface(getFacade().getOwner(op))));
// if this operation has Tag "inline" the method shall be
// generated in header
Inline inlineStyle = Inline.getInlineOperationModifierType(op);
if (generatorPass == HEADER_PASS) {
result = inlineStyle.isMethodBodyInsideClass();
} else if (generatorPass == NONE_PASS) {
result = inlineStyle.isMethodBodyOutsideClass();
}
return result;
}
/** 2002-12-13 Achim Spangler
* generate a single set function for a given attribute and StringBuffer
*/
private void generateSingleAttributeSet(Object attr, StringBuffer sb) {
if (getFacade().getType(attr) == null) {
return;
}
// generate for attributes with class-type:
// "indent void set_<name>( const <type> &value ) { <name> = value; };"
// generate for other (small) data types:
// "indent void set_<name>( <type> value ) { <name> = value; };"
// generate: "indent void set_<name>( "
sb.append(LINE_SEPARATOR).append(indent);
sb.append("/** simple access function to set the attribute ");
sb.append(getFacade().getName(attr));
sb.append(" by function").append(LINE_SEPARATOR).append(indent);
sb.append(" * @param value value to set for the attribute ");
sb.append(getFacade().getName(attr)).append(LINE_SEPARATOR);
sb.append(indent).append(" */").append(LINE_SEPARATOR);
sb.append(indent);
sb.append("void set_").append(getFacade().getName(attr))
.append("( ");
String modifier = generateAttributeParameterModifier(attr);
if (modifier != null && modifier.length() > 0) {
// generate: "const <type> <modifier>value"
if (modifier.equals("&")) sb.append("const ");
sb.append(generateClassifierRef(getFacade().getType(attr)))
.append(' ').append(modifier).append("value");
} else if (getFacade().isAClass(getFacade().getType(attr))) {
// generate: "const <type> &value"
sb.append("const ");
sb.append(generateClassifierRef(getFacade().getType(attr)));
sb.append(" &value");
} else {
// generate: "<type> value"
sb.append(generateClassifierRef(getFacade().getType(attr)))
.append(" value");
}
// generate: " ) { <name> = value; };"
sb.append(" ) { ").append(getFacade().getName(attr));
sb.append(" = value; };").append(LINE_SEPARATOR);
}
/** 2002-12-13 Achim Spangler
* generate a single get function for a given attribute and StringBuffer
*/
private void generateSingleAttributeGet(Object attr, StringBuffer sb) {
if (getFacade().getType(attr) == null) return;
// generate for attributes with class-type:
// "const <type>& get_<name>( void ) { return <name>; };"
// generate for other (small) data types
// "<type> get_<name>( void ) { return <name>; };"
// generate: "indent"
sb.append(LINE_SEPARATOR).append(indent);
sb.append("/** simple access function to get the attribute ");
sb.append(getFacade().getName(attr));
sb.append(" by function */").append(LINE_SEPARATOR).append(indent);
String modifier = generateAttributeParameterModifier(attr);
if (modifier != null && modifier.length() > 0) {
// generate: "const <type><modifier>"
sb.append("const ");
sb.append(generateClassifierRef(getFacade().getType(attr)));
sb.append(modifier);
} else if (getFacade().isAClass(getFacade().getType(attr))) {
// generate: "const <type>&"
sb.append("const ");
sb.append(generateClassifierRef(getFacade().getType(attr)));
sb.append("&");
} else {
// generate: "<type>"
sb.append(generateClassifierRef(getFacade().getType(attr)));
}
// generate: " get_<name>( void ) const { return <name>; };"
sb.append(" get_").append(getFacade().getName(attr));
sb.append("( void ) const { return ")
.append(getFacade().getName(attr));
sb.append("; };").append(LINE_SEPARATOR);
}
/**
* Generates the attributes of the body of a class or interface.
* @param cls
*/
private void generateClassifierBodyTaggedAccess4Attributes(
Object cls,
StringBuffer funcPrivate,
StringBuffer funcProtected,
StringBuffer funcPublic) {
Collection strs = getFacade().getAttributes(cls);
if (strs.isEmpty() || (generatorPass != HEADER_PASS)) {
return;
}
String accessTag = null;
Iterator strEnum = strs.iterator();
while (strEnum.hasNext()) {
Object attr = strEnum.next();
accessTag = getFacade().getTaggedValueValue(attr,
TV_NAME_SET);
if (accessTag != null && accessTag.length() > 0) {
if (accessTag.indexOf("public") != -1) {
generateSingleAttributeSet(attr, funcPublic);
}
if (accessTag.indexOf("protected") != -1) {
generateSingleAttributeSet(attr, funcProtected);
}
if (accessTag.indexOf("private") != -1) {
generateSingleAttributeSet(attr, funcPrivate);
}
}
accessTag = getFacade().getTaggedValueValue(attr,
TV_NAME_GET);
if (accessTag != null && accessTag.length() > 0) {
if (accessTag.indexOf("public") != -1) {
generateSingleAttributeGet(attr, funcPublic);
}
if (accessTag.indexOf("protected") != -1) {
generateSingleAttributeGet(attr, funcProtected);
}
if (accessTag.indexOf("private") != -1) {
generateSingleAttributeGet(attr, funcPrivate);
}
}
}
}
/**
* Generates the association ends of the body of a class or interface.
* @param cls
* @param sb Where to put the result.
*/
private void generateClassifierBodyOperations(Object cls,
StringBuffer sb) {
Collection behs = getFacade().getOperations(cls);
if (behs.isEmpty()) return;
sb.append(LINE_SEPARATOR);
if (verboseDocs) {
sb.append(indent).append("// Operations").append(LINE_SEPARATOR);
}
// generate tag controlled access functions for attributes
StringBuffer[] funcs = new StringBuffer[3];
funcs[0] = new StringBuffer(80);
funcs[1] = new StringBuffer(80);
funcs[2] = new StringBuffer(80);
generateClassifierBodyTaggedAccess4Attributes(cls, funcs[PRIVATE_PART],
funcs[PROTECTED_PART],
funcs[PUBLIC_PART]);
Iterator behEnum = behs.iterator();
while (behEnum.hasNext()) {
Object bf = behEnum.next();
StringBuffer tb = null;
int p = getVisibilityPart(bf);
if (p < 0) continue;
tb = funcs[p];
boolean mustGenBody = checkGenerateOperationBody(bf);
if (tb != null && ((generatorPass == HEADER_PASS) || mustGenBody)) {
tb.append(LINE_SEPARATOR);
tb.append(generateOperation(bf, false));
// helper for tagged values
String tv = generateTaggedValues(bf, ALL_BUT_DOC_TAGS);
if (mustGenBody && (getFacade().isAClass(cls))
&& (getFacade().isAOperation(bf))
&& (!getFacade().isAbstract(bf))) {
// there is no ReturnType in behavioral feature (uml)
tb.append(LINE_SEPARATOR).append(generateMethodBody(bf));
} else {
tb.append(";").append(LINE_SEPARATOR);
if (tv.length() > 0) {
tb.append(indent).append(tv).append(LINE_SEPARATOR);
}
}
}
} // end loop through all operations
sb.append(generateAllParts(funcs));
}
/**
* Generates the association ends of the body of a class or interface.
* @param cls
* @param sb Where to put the result.
*/
private void generateClassifierBodyTypedefs(Object cls, StringBuffer sb) {
if (generatorPass == HEADER_PASS) {
Collection publicTypedefStatements =
findTagValues(cls, TV_NAME_TYPEDEF_PUBLIC);
Collection protectedTypedefStatements =
findTagValues(cls, TV_NAME_TYPEDEF_PROTECTED);
Collection privateTypedefStatements =
findTagValues(cls, TV_NAME_TYPEDEF_PRIVATE);
if (!publicTypedefStatements.isEmpty()) {
sb.append(LINE_SEPARATOR).append(" public:")
.append(LINE_SEPARATOR).append(indent);
sb.append("// public type definitions for header defined "
+ "by Tag entries in ArgoUML").append(LINE_SEPARATOR);
sb.append(indent);
sb.append("// Result: typedef <typedef_public> "
+ "<tag_value>;").append(LINE_SEPARATOR);
Iterator typedefEnum = publicTypedefStatements.iterator();
while (typedefEnum.hasNext()) {
sb.append(indent).append("typedef ");
sb.append(typedefEnum.next())
.append(";").append(LINE_SEPARATOR);
}
}
if (!protectedTypedefStatements.isEmpty()) {
sb.append(LINE_SEPARATOR).append(" protected:")
.append(LINE_SEPARATOR).append(indent);
sb.append("// protected type definitions for header defined "
+ "by Tag entries in ArgoUML").append(LINE_SEPARATOR);
sb.append(indent);
sb.append("// Result: typedef <typedef_protected> "
+ "<tag_value>;").append(LINE_SEPARATOR);
Iterator typedefEnum = protectedTypedefStatements.iterator();
while (typedefEnum.hasNext()) {
sb.append(indent).append("typedef ");
sb.append(typedefEnum.next())
.append(";").append(LINE_SEPARATOR);
}
}
if (!privateTypedefStatements.isEmpty()) {
sb.append(LINE_SEPARATOR).append(" private:")
.append(LINE_SEPARATOR).append(indent);
sb.append("// private type definitions for header defined "
+ "by Tag entries in ArgoUML").append(LINE_SEPARATOR);
sb.append(indent);
sb.append("// Result: typedef <typedef_private> "
+ "<tag_value>;").append(LINE_SEPARATOR);
Iterator typedefEnum = privateTypedefStatements.iterator();
while (typedefEnum.hasNext()) {
sb.append(indent).append("typedef ");
sb.append(typedefEnum.next()).append(";")
.append(LINE_SEPARATOR);
}
}
}
}
/**
* Generates a virtual destructor when the classifier is an interface.
* @param cls the classifier object
* @param sb the buffer to where the generate code goes
*/
private void generateClassifierDestructor(Object cls, StringBuffer sb) {
if (getFacade().isAInterface(cls)
&& generatorPass == HEADER_PASS) {
sb.append(LINE_SEPARATOR).append("public:").append(LINE_SEPARATOR);
sb.append(indent).append("// virtual destructor for interface ")
.append(LINE_SEPARATOR);
sb.append(indent).append("virtual ").append('~').append(
getFacade().getName(cls)).append("() { }")
.append(LINE_SEPARATOR);
}
}
private void generateClassifierInnerClasses(Object cls, StringBuffer sb) {
StringBuffer part[] = new StringBuffer[ALL_PARTS.length];
for (int i = 0; i < part.length; i++)
part[i] = new StringBuffer(80);
Collection inners = getFacade().getOwnedElements(cls);
for (Iterator it = inners.iterator(); it.hasNext();) {
Object inner = it.next();
if (getFacade().isAClass(inner)
|| getFacade().isAInterface(inner)) {
String innerCode = generateClassifier(inner);
int p = getVisibilityPart(inner);
part[p].append(LINE_SEPARATOR);
if (generatorPass == HEADER_PASS) {
part[p].append(indentString(innerCode, 1));
} else {
part[p].append(innerCode);
}
part[p].append(LINE_SEPARATOR);
}
}
sb.append(generateAllParts(part));
}
/**
* Generates the body of a class or interface.
* @param cls
* @return a StringBuffer with the result.
*/
private StringBuffer generateClassifierBody(Object cls) {
StringBuffer sb = new StringBuffer();
if (getFacade().isAClass(cls) || getFacade().isAInterface(cls))
{
// Inner classes
generateClassifierInnerClasses(cls, sb);
// add operations
// TODO: constructors
generateClassifierBodyOperations(cls, sb);
// fixing issue #2587
generateClassifierDestructor(cls, sb);
// add attributes
generateClassifierBodyAttributes(cls, sb);
// add attributes implementing associations
generateClassifierBodyAssociations(cls, sb);
// add typedefs
generateClassifierBodyTypedefs(cls, sb);
}
return sb;
}
/**
* 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.
*/
private String generateMethodBody(Object op) {
if (op != null) {
StringBuffer sb = new StringBuffer(80);
Collection methods = getFacade().getMethods(op);
Iterator i = methods.iterator();
Object method = null;
boolean methodFound = false;
String tv = generateTaggedValues(op, ALL_BUT_DOC_TAGS);
String operationIndent =
(generatorPass == HEADER_PASS) ? indent : "";
// append tags which are not Doc-Comments
if (tv.length() > 0) {
sb.append(operationIndent).append(tv).append(LINE_SEPARATOR);
}
// place the curly braces within the protected area, to
// allow placement of preserved constructor initializers in
// this area otherwise all possible constructor-attribute
// initializers would have to be autogenerated with an
// army of special tags
sb.append(generateSectionTop(op, operationIndent))
.append(operationIndent).append("{").append(LINE_SEPARATOR);
while (i != null && i.hasNext()) {
method = i.next();
if (method != null) {
if ((getFacade().getBody(method) != null)
&& (!methodFound)) {
Object body = getFacade().getBody(method);
sb.append(getFacade().getBody(body));
methodFound = true;
break;
}
}
}
if (!methodFound) {
// pick out return type as default method body
Collection returnParams = getCoreHelper()
.getReturnParameters(op);
Object rp;
if (returnParams.size() == 0) {
rp = null;
} else {
rp = returnParams.iterator().next();
}
if (returnParams.size() > 1) {
LOG.warning("C++ generator only handles one return"
+ " parameter - Found " + returnParams.size()
+ " for " + getFacade().getName(op));
}
if (rp != null) {
Object returnType = getFacade().getType(rp);
sb.append(generateDefaultReturnStatement(returnType));
}
}
sb.append(operationIndent).append("}").append(LINE_SEPARATOR)
.append(generateSectionBottom(op, operationIndent));
return sb.toString();
}
return generateDefaultReturnStatement (null);
}
private String generateSectionTop(Object op, String localIndent) {
String id = UUIDHelper.getUUID(op);
return Section.generateTop(id, localIndent);
}
private String generateSectionBottom(Object op, String localIndent) {
String id = UUIDHelper.getUUID(op);
return Section.generateBottom(id, localIndent);
}
private String generateDefaultReturnStatement(Object cls) {
if (cls == null) return "";
String clsName = getFacade().getName(cls);
String res = null;
if (clsName.equals("void")) res = "";
else if (clsName.equals("char")) res = "return 'x';";
else if (clsName.equals("int")) res = "return 0;";
else if (clsName.equals("bool")) res = "return false;";
else if (clsName.equals("byte")) res = "return 0;";
else if (clsName.equals("long")) res = "return 0;";
else if (clsName.equals("float")) res = "return 0.0;";
else if (clsName.equals("double")) res = "return 0.0;";
if (res == null) {
return ""; // in doubt, let the choice to the user
}
return indent + res + LINE_SEPARATOR;
}
private String generateTaggedValues(Object e, int tagSelection) {
Iterator iter = getFacade().getTaggedValues(e);
if (!iter.hasNext()) {
return "";
}
StringBuffer buf = new StringBuffer();
boolean first = true;
String s = null;
while (iter.hasNext()) {
s = generateTaggedValue(iter.next(), tagSelection);
if (s != null && s.length() > 0) {
if (first) {
if (tagSelection == DOC_COMMENT_TAGS) {
// insert main documentation for DocComment at first
String doc =
(DocumentationManager.hasDocs(e))
? DocumentationManager.getDocs(e, indent)
: null;
if (doc != null && doc.trim().length() > 0) {
buf.append(doc.substring(0, doc.indexOf("*/") + 1));
buf.append(" ");
}
else {
buf.append("/** ");
}
}
else {
buf.append("/* {");
}
first = false;
} // end first
else {
if (tagSelection == DOC_COMMENT_TAGS) {
buf.append(LINE_SEPARATOR)
.append(indent).append(" * ");
}
else {
buf.append(", ");
}
} // end not first tag
buf.append(s);
} // end tag not empty
} // end while
if (!first) {
if (tagSelection == DOC_COMMENT_TAGS) {
buf.append(LINE_SEPARATOR).append(indent).append(" */")
.append(LINE_SEPARATOR);
} else {
buf.append ("}*/").append(LINE_SEPARATOR);
}
}
else if (tagSelection == DOC_COMMENT_TAGS) {
// create at least main documentation field, if no other tag found
String doc = (DocumentationManager.hasDocs(e))
? DocumentationManager.getDocs(e, indent)
: null;
if (doc != null && doc.trim().length() > 0) {
buf.append(doc).append(LINE_SEPARATOR);
}
}
return buf.toString();
}
private String generateTaggedValue(Object tv, int tagSelection) {
if (tv == null) return "";
String s = generateUninterpreted(getFacade().getValueOfTag(tv));
String tagName = getFacade().getTagOfTag(tv);
if (s == null || s.length() == 0 || s.equals("/** */")
|| tagName == null
|| (tagName.indexOf("include") != -1)
|| (tagName.indexOf("_incl") != -1)) {
return "";
}
if ((tagSelection == DOC_COMMENT_TAGS)
&& (isDocCommentTag(tagName))) {
return generateDocComment4Tag(tagName) + s;
} else if (((tagSelection == ALL_BUT_DOC_TAGS)
&& (!isDocCommentTag(tagName))
&& (!tagName.equals("documentation"))
&& (!tagName.equals("javadocs"))
)
|| (tagSelection == ALL_TAGS)) {
return tagName + "=" + s;
} else {
return "";
}
}
private Collection findTagValues(Object item, String searchedName) {
Collection result = new Vector();
Iterator iter = getFacade().getTaggedValues(item);
String s = null;
while (iter.hasNext()) {
Object tag = iter.next();
String tagOfTag = getFacade().getTagOfTag(tag);
if (tagOfTag != null && tagOfTag.equals(searchedName)) {
s = getFacade().getValueOfTag(tag);
if (s != null && s.length() != 0) result.add(s);
}
}
return result;
}
private boolean isDocCommentTag(String tagName) {
boolean result = false;
if (tagName.equals ("inv")) {
result = true;
}
else if (tagName.equals ("post")) {
result = true;
}
else if (tagName.equals ("pre")) {
result = true;
}
else if (tagName.equals ("author")) {
result = true;
}
else if (tagName.equals ("version")) {
result = true;
}
else if (tagName.equals ("see")) {
result = true;
}
else if (tagName.equals ("param")) {
result = true;
}
return result;
}
private String generateDocComment4Tag(String tagName) {
if (tagName.equals ("inv")) {
return "@invariant ";
}
else if (tagName.equals ("post")) {
return "@postcondition ";
}
else if (tagName.equals ("pre")) {
return "@precondition ";
}
else if (tagName.equals ("author")) {
return "@author ";
}
else if (tagName.equals ("version")) {
return "@version ";
}
else if (tagName.equals ("see")) {
return "@see ";
}
else if (tagName.equals ("param")) {
return "@param ";
}
else return "";
}
/**
* Enhance/Create the doccomment 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) {
// list tagged values for documentation
String s = generateTaggedValues (me, DOC_COMMENT_TAGS);
if (getFacade().getUpper(ae) != 1) {
// 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 doccomment
if (!(s == null || "".equals(s))) {
// Just remove closing "*/"
sDocComment.append(indent)
.append(s.substring(0, s.indexOf("*/") + 1));
}
else {
sDocComment.append(indent).append("/**").append(LINE_SEPARATOR);
sDocComment.append(indent).append(" *");
}
// Build doccomment
Object type = getFacade().getType(ae);
if (type != null) {
sDocComment.append(" @element-type ");
sDocComment.append(getFacade().getName(type));
}
sDocComment.append(LINE_SEPARATOR).append(indent)
.append(" */").append(LINE_SEPARATOR);
return sDocComment.toString();
}
return (s != null) ? s : "";
}
/**
*
* @param a association object
* @param ae association end attached to the classifier
* for which the code is to be generated
* @param parts the buffers associated with the public, protected
* and private parts, where the code is to be written.
*/
private void generateAssociationFrom(Object a, Object ae,
StringBuffer[] parts) {
// TODO: does not handle n-ary associations
Collection connections = getFacade().getConnections(a);
Iterator connEnum = connections.iterator();
while (connEnum.hasNext()) {
Object ae2 = connEnum.next();
if (ae2 != ae) {
int p = getVisibilityPart(ae2);
if (p >= 0) {
StringBuffer sb = parts[p];
sb.append(LINE_SEPARATOR);
String assend = generateAssociationEnd(ae2);
if (assend.length() > 0) {
String comment =
generateConstraintEnrichedDocComment(a, ae2);
if (comment.length() > 0)
sb.append(comment);
// both comment and assend ends with simple newline
sb.append(indent).append(assend);
}
String tv = generateTaggedValues(a, ALL_BUT_DOC_TAGS);
if (tv != null && tv.length() > 0) {
sb.append(indent).append(tv);
}
}
}
}
}
private String generateAssociationEnd(Object ae) {
if (!getFacade().isNavigable(ae)) {
return "";
}
if (getFacade().isAbstract(
getFacade().getAssociation(ae))) {
return "";
}
StringBuffer sb = new StringBuffer(80);
if (getFacade().isStatic(ae)) {
sb.append("static ");
}
String n = getFacade().getName(ae);
Object asc = getFacade().getAssociation(ae);
String ascName = getFacade().getName(asc);
String name = null;
if (n != null && n.length() > 0) {
name = n;
} else if (ascName != null && ascName.length() > 0) {
name = ascName;
} else {
name = "my" + generateClassifierRef(getFacade().getType(ae));
}
String modifier;
if (getFacade().isAAssociationClass(asc)) {
// With an association class, we actually make an association
// between us and the association class itself.
// Usually, this is a pointer or a reference, so default
// to a pointer.
modifier = generateAttributeParameterModifier(asc, "*");
// add the association class in the dependency list
addDependency(asc, !checkIncludeNeeded4Element(ae));
} else {
modifier = generateAttributeParameterModifier(ae);
// add the type of the association end in the dependency list
boolean predecl = !checkIncludeNeeded4Element(ae);
addDependency(getFacade().getType(ae), predecl);
}
sb.append(generateMultiplicity(ae, name,
getFacade().getMultiplicity(ae),
modifier));
return (sb.append(";").append(LINE_SEPARATOR)).toString();
}
private String generateGeneralization(Collection generalizations) {
if (generalizations == null) {
return "";
}
StringBuffer sb = new StringBuffer(80);
Iterator genEnum = generalizations.iterator();
while (genEnum.hasNext()) {
Object generalization = genEnum.next();
Object ge = getFacade().getGeneral(generalization);
if (ge != null) {
if (sb.length() > 0) sb.append(", ");
String visTag =
getFacade().getTaggedValueValue(generalization,
TV_NAME_INHERITANCE_VISIBILITY).trim();
if (visTag != null && !visTag.equals("")) {
sb.append(visTag).append(" ");
} else {
if (getFacade().isAInterface(ge)) {
sb.append("virtual public ");
} else {
sb.append("public ");
}
}
sb.append(generateNameWithPkgSelection(ge));
// add the type of the base class in the dependency list
addDependency(ge, false);
}
}
return sb.toString();
}
private String generateSpecification(Object cls) {
Collection deps = getFacade().getClientDependencies(cls);
Iterator depIterator = deps.iterator();
StringBuffer sb = new StringBuffer(80);
while (depIterator.hasNext()) {
Object dependency = depIterator.next();
if (getFacade().isAAbstraction(dependency)
&& getFacade().isRealize(dependency)) {
if (sb.length() > 0) sb.append(", ");
Object iFace = getFacade().getSuppliers(dependency)
.iterator().next();
String visTag =
getFacade().getTaggedValueValue(dependency,
TV_NAME_INHERITANCE_VISIBILITY).trim();
if (visTag != null && !visTag.equals("")) {
sb.append(visTag).append(" ");
} else {
sb.append("virtual public ");
}
sb.append(generateNameWithPkgSelection(iFace));
// add the type of the interface in the dependency list
addDependency(iFace, false);
}
}
return sb.toString();
}
private String generateVisibility(Object o) {
if (getFacade().isAAttribute(o)) {
return "";
}
// cut'n'pasted from GeneratorJava.java
if (getFacade().isAFeature(o)) {
// TODO: The src_visibility tag doesn't appear to be created
// anywhere by ArgoUML currently
Object tv = getFacade().getTaggedValue(o, "src_visibility");
if (tv != null) {
String tagged = (String) getFacade().getValue(tv);
if (tagged != null) {
if (tagged.trim().equals("")
|| tagged.trim().toLowerCase().equals("default")
// We should never get "package", but just in case
|| tagged.trim().toLowerCase().equals("package")) {
return "";
}
return tagged + ": ";
}
}
}
if (getFacade().isAModelElement(o)) {
if (getFacade().isPublic(o))
return "public: ";
if (getFacade().isPrivate(o))
return "private: ";
if (getFacade().isProtected(o))
return "protected: ";
if (getFacade().isPackage(o))
// TODO: is default visibility the right thing here?
return "";
}
if (getFacade().isAVisibilityKind(o)) {
if (getVisibilityKind().getPublic().equals(o))
return "public: ";
if (getVisibilityKind().getPrivate().equals(o))
return "private: ";
if (getVisibilityKind().getProtected().equals(o))
return "protected: ";
if (getVisibilityKind().getPackage().equals(o))
// TODO: is default visibility the right thing here?
return "";
}
return "";
}
/**
* @param me The model element to generate scope for.
* @return The generated text representing the scope.
*/
private String generateScope(Object me) {
if (getFacade().isStatic(me)) {
return "static ";
}
return "";
}
/**
* Generate "abstract" keyword for an abstract operation.
* In C++, since it does not have an explicit "interface" keyword, we must
* check against this and set the operation to abstract if so.
*/
private String generateAbstractness(Object op) {
// use Model subsystem Facade to check if the operation is
// owned by an interface
Object opOwner = getFacade().getOwner(op);
if (getFacade().isAbstract(op) || getFacade().isAInterface(opOwner)) {
return " = 0";
}
return "";
}
/**
* Generate "const" keyword for query operations.
*/
private String generateOperationChangeability(Object op) {
if (getFacade().isQuery(op)) {
return "const ";
}
return "";
}
/**
* Generate "const" keyword for const pointer/reference parameters.
*/
private String generateParameterChangeability(Object par) {
int parType = getAttributeModifierType(par);
if (parType != -1 && parType != NORMAL_MOD
&& getFacade().getKind(par) != null
&& (getFacade().getKind(par)).equals(
getDirectionKind().getInParameter())) {
return "const ";
}
return "";
}
private String generateStructuralFeatureChangeability(Object sf) {
if (getFacade().isReadOnly(sf)) {
return "const ";
}
return "";
}
private String generateMultiplicity(Object item, String name,
Object m, String modifier) {
String type = null;
String containerType = null;
Object typeCls = null;
if (getFacade().isAAssociationEnd(item)) {
// take into account association classes
Object assoc = getFacade().getAssociation(item);
if (getFacade().isAAssociationClass(assoc)) {
typeCls = assoc;
name += "Assoc";
}
else typeCls = getFacade().getType(item);
} else if (getFacade().isAAttribute(item)) {
typeCls = getFacade().getType(item);
} else if (getFacade().isAClassifier(item)) {
typeCls = item;
} else {
type = "";
}
if (typeCls != null) {
type = generateNameWithPkgSelection(typeCls);
}
if (m == null) {
return (type + " " + modifier + name);
}
StringBuffer sb = new StringBuffer(80);
int countUpper = getFacade().getUpper(m);
int countLower = getFacade().getLower(m);
if (countUpper == 1 && countLower == 1) {
// simple generate identifier for default 1:1 multiplicity
sb.append(type).append(' ').append(modifier).append(name);
} else if (countUpper == 1 && countLower == 0) {
// use a simple pointer for 0:1 multiplicity
// TODO: use an auto_ptr in case of attributes or compositions
sb.append(type).append(' ').append(modifier)
.append("* ").append(name);
} else if (countUpper == countLower) {
// fixed array -> <type> <name>[<count>];
sb.append(type).append(' ').append(modifier).append(name)
.append("[ " + countUpper + "]");
} else {
// variable association -> if no tag found use vector
// else search for tag:
// <MultipliciyType> : vector|list|slist|map|stack|stringmap
String multType =
getFacade().getTaggedValueValue(item,
TV_NAME_MULTIPLICITY_TYPE);
if (multType != null && multType.length() > 0) {
if (multType.equals("vector")) {
containerType = "vector";
} else if (multType.equals("list")) {
containerType = "list";
} else if (multType.equals("slist")) {
containerType = "slist";
} else if (multType.equals("map")) {
// FIXME: map does not work this way, needs a index type
containerType = "map";
} else if (multType.equals("stack")) {
containerType = "stack";
} else if (multType.equals("stringmap")) {
systemInc.add("string");
systemInc.add("map");
sb.append(stdPrefix + "map<" + stdPrefix + "string, ");
if (modifier.indexOf('&') != -1) {
LOG.warning("cannot generate STL container "
+ "with references, using pointers");
modifier = "*";
}
sb.append(type).append(modifier);
sb.append(" > ").append(name);
} else {
LOG.warning("unknown " + TV_NAME_MULTIPLICITY_TYPE + " \""
+ multType + "\", using default");
containerType = "vector";
}
} else {
containerType = "vector";
}
if (containerType != null) {
// these container are declared the same except the name
systemInc.add(containerType);
sb.append(stdPrefix).append(containerType).append("< ");
if (modifier.indexOf('&') != -1) {
LOG.warning("cannot generate STL container "
+ "with references, using pointers");
modifier = "*";
}
sb.append(type).append(modifier);
sb.append(" > ").append(name);
}
}
return sb.toString();
}
/**
* Load configurable parameters.
* TODO: Why is this public? - tfm
*/
public void loadConfig() {
int indWidth = Configuration.getInteger(KEY_CPP_INDENT, 4);
char[] ind = new char[indWidth];
for (int i = 0; i < indWidth; i++)
ind[i] = ' ';
this.indent = new String(ind);
lfBeforeCurly =
Configuration.getBoolean(KEY_CPP_LF_BEFORE_CURLY, false);
verboseDocs = Configuration.getBoolean(KEY_CPP_VERBOSE_COMM, false);
int useSect = Configuration.getInteger(KEY_CPP_SECT,
Section.SECT_NORMAL);
Section.setUseSect(useSect);
hdrGuardUpperCase = Configuration.getBoolean(
KEY_CPP_HEADER_GUARD_UPPERCASE, false);
hdrGuardGUID = Configuration.getBoolean(
KEY_CPP_HEADER_GUARD_GUID, false);
int defaultInlineStyle = Configuration.getInteger(
KEY_CPP_DEFAULT_INLINE, Inline.getDefaultDefaultStyle());
Inline.setDefaultStyle(defaultInlineStyle);
}
// Methods used by Settings dialog
/**
* @return true if the generator outputs a newline before the
* curly brace starting a class declaration.
*/
public boolean isLfBeforeCurly() {
return lfBeforeCurly;
}
/**
* Sets whether to output a newline character before the curly
* brace starting a class declaration.
* @param beforeCurly true to generate the '{' on a line on its own.
*/
public void setLfBeforeCurly(boolean beforeCurly) {
this.lfBeforeCurly = beforeCurly;
Configuration.setBoolean(KEY_CPP_LF_BEFORE_CURLY, beforeCurly);
}
/**
* @return If the generator generates verbose comments and docs or not.
*/
public boolean isVerboseDocs() {
return verboseDocs;
}
/**
* Tells whether to generate verbose comments and docs or not.
* @param verbose true to generate more verbose comments.
*/
public void setVerboseDocs(boolean verbose) {
this.verboseDocs = verbose;
Configuration.setBoolean(KEY_CPP_VERBOSE_COMM, verbose);
}
/**
* @return The current width of the indentation.
*/
public int getIndent() {
return indent.length();
}
/**
* Sets the indentation width.
* @param indWidth The number of spaces to use for indentation.
*/
public void setIndent(int indWidth) {
char[] ind = new char[indWidth];
for (int i = 0; i < indWidth; i++)
ind[i] = ' ';
this.indent = new String(ind);
Configuration.setInteger(KEY_CPP_INDENT, indWidth);
}
/**
* @return Whether sections are generated and how.
* @see #setUseSect(int)
*/
public int getUseSect() {
return Section.getUseSect();
}
/**
* Tells the generator if and how to generate sections.
* @param use Must be one of:
* <ul>
* <li>SECT_NONE: sections are not generated;</li>
* <li>SECT_NORMAL: sections are generated;</li>
* <li>SECT_BRIEF: sections are generated, but the "do not delete..."
* line is skipped.</li>
* </ul>
*/
public void setUseSect(int use) {
Section.setUseSect(use);
Configuration.setInteger(KEY_CPP_SECT, use);
}
/**
* @return true if the generator outputs a header guard in upper case.
*/
public boolean isHeaderGuardUpperCase() {
return hdrGuardUpperCase;
}
/**
* Sets how to output header guard.
* @param upperCase true to generate upper case header guard.
*/
public void setHeaderGuardUpperCase(boolean upperCase) {
this.hdrGuardUpperCase = upperCase;
Configuration.setBoolean(KEY_CPP_HEADER_GUARD_UPPERCASE, upperCase);
}
/**
* @return true if the generator should add GUID to header guard.
*/
public boolean isHeaderGuardGUID() {
return hdrGuardGUID;
}
/**
* Sets how to output header guard.
* @param addGUID true to add GUID to header guard.
*/
public void setHeaderGuardGUID(boolean addGUID) {
this.hdrGuardGUID = addGUID;
Configuration.setBoolean(KEY_CPP_HEADER_GUARD_GUID, addGUID);
}
public int getDefaultInlineStyle() {
return Inline.getDefaultStyle();
}
public void setDefaultInlineStyle(int inline) {
Inline.setDefaultStyle(inline);
Configuration.setInteger(KEY_CPP_DEFAULT_INLINE, inline);
}
/*
* Set of already-generated classifiers.
*/
private Set generatedFiles = null;
/*
* Number of calls to startGenerateFile non closed.
*/
private int generateRecur = 0;
/* implementation of CodeGenerator */
/*
* Start generating files. Needed to track dependencies.
*/
private void startFileGeneration() {
if (generateRecur++ == 0) {
generatedFiles = new HashSet();
}
}
/*
* End generating files. Needed to track dependencies.
*/
private void endFileGeneration() {
if (--generateRecur == 0) {
generatedFiles = null;
}
}
/*
* Generate files for element 'o' (and dependencies, eventually).
* Return the collection of files (as Strings).
* Do nothing (and return an empty collection) it 'o' is in generatedFiles.
*/
private Collection generateFilesForElem(Object o,
String path, boolean deps) {
Vector ret = new Vector();
if (generatedFiles.contains(o)) {
return ret; // generated already
}
if (!getFacade().isAClass(o) && !getFacade().isAInterface(o)) {
return ret; // not a class or interface
}
while (isAInnerClass(o)) {
o = getFacade().getNamespace(o);
}
String pathname = null;
// use unique section for both passes -> allow move of
// normal function body to inline and vice versa
if (Section.getUseSect() != Section.SECT_NONE) {
sect = new Section();
/*
* 2002-11-28 Achim Spangler
* first read header and source file into global/unique section
*/
for (generatorPass = HEADER_PASS;
generatorPass <= SOURCE_PASS;
generatorPass++) {
pathname = createDirectoriesPathname(o, path);
//String pathname = path + filename;
// TODO: package, project basepath, tagged values to configure
File f = new File(pathname);
if (f.exists()) {
LOG.info("Generating (updated) " + f.getPath());
sect.read(pathname);
File bakFile = new File(pathname + ".bak");
if (bakFile.exists()) {
bakFile.delete();
}
f.renameTo(bakFile);
} else {
LOG.info("Generating (new) " + f.getPath());
}
}
}
Set dependencies = null;
if (deps) dependencies = new TreeSet();
/**
* 2002-11-28 Achim Spangler
* run basic generation function two times for header and implementation
*/
for (generatorPass = HEADER_PASS;
generatorPass <= SOURCE_PASS;
generatorPass++) {
pathname = createDirectoriesPathname(o, path);
String fileContent = generateFileAsString(o, pathname);
if (fileContent.length() == 0) continue;
BufferedWriter fos = null;
//String pathname = path + filename;
// TODO: package, project basepath, tagged values to configure
File f = new File(pathname);
try {
// TODO: This is using the default platform character encoding
// specifying an encoding will produce more predictable results
fos = new BufferedWriter (new FileWriter (f));
writeTemplate(o, path, fos);
fos.write(fileContent);
fos.newLine();
}
catch (IOException exp) { }
finally {
try {
if (fos != null) fos.close();
}
catch (IOException exp) {
LOG.severe("FAILED: " + f.getPath());
}
}
LOG.info("written: " + pathname);
if (Section.getUseSect() != Section.SECT_NONE) {
// output lost sections only in the second path
// -> sections which are moved from header(inline) to source
// file are prevented to be outputted in header pass
File outFile = new File(pathname + ".out");
if (outFile.exists()) {
outFile.delete(); // remove junk
}
if (generatorPass == HEADER_PASS) {
sect.write(pathname, indent, false);
} else {
sect.write(pathname, indent, true);
}
if (outFile.exists()) {
assert f.exists();
f.delete();
outFile.renameTo(f);
LOG.info("added sections to: " + pathname);
}
}
LOG.info("----- end updating " + pathname + "-----");
ret.add(pathname);
if (deps) {
dependencies.add(includeCls);
dependencies.add(predeclCls);
}
}
cleanupGenerator();
// reset generator pass to NONE for the notation to be correct
generatorPass = NONE_PASS;
generatedFiles.add(o);
if (deps) {
Iterator it = dependencies.iterator();
while (it.hasNext()) {
ret.add(generateFilesForElem(it.next(), path, deps));
}
}
return ret;
}
/*
* @see org.argouml.uml.generator.CodeGenerator#generate(java.util.Collection, boolean)
*/
public Collection generate(Collection elements, boolean deps) {
List ret = new ArrayList();
startFileGeneration();
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Object elem = it.next();
String path = generatePath(elem);
Set dependencies = null;
if (deps) dependencies = new TreeSet();
for (generatorPass = HEADER_PASS;
generatorPass <= SOURCE_PASS;
generatorPass++) {
String name = getFacade().getName(elem) + getFileExtension();
String content = generateFileAsString(elem, path + name);
SourceUnit su = new SourceUnit(name, path, content);
ret.add(su);
}
generatorPass = NONE_PASS;
generatedFiles.add(elem);
if (deps) {
ret.add(generate(dependencies, deps));
}
}
endFileGeneration();
return ret;
}
/*
* @see org.argouml.uml.generator.CodeGenerator#generateFiles(java.util.Collection, java.lang.String, boolean)
*/
public Collection generateFiles(Collection elements, String path,
boolean deps) {
List ret = new ArrayList();
startFileGeneration();
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Object elem = it.next();
ret.addAll(generateFilesForElem(elem, path, deps));
}
endFileGeneration();
return ret;
}
/*
* @see org.argouml.uml.generator.CodeGenerator#generateFileList(java.util.Collection, boolean)
*/
public Collection generateFileList(Collection elements, boolean deps) {
List ret = new ArrayList();
startFileGeneration();
for (Iterator it = elements.iterator(); it.hasNext(); ) {
Object elem = it.next();
// FIXME: check for interfaces, inner classes, deps, etc.
ret.add(getFacade().getName(elem) + ".cpp");
ret.add(getFacade().getName(elem) + ".h");
}
endFileGeneration();
return null;
}
private static String generateUninterpreted(String un) {
if (un == null)
return "";
return un;
}
private static String generateClassifierRef(Object cls) {
if (cls == null)
return "";
return getFacade().getName(cls);
}
/**
*
* @param cls object
* @return String with generated methods
*/
private String generateInlinedMethodsOutsideClass(Object cls) {
String s = new String();
Collection op = getFacade().getOperations(cls);
if (op.isEmpty()) {
return null;
}
s = LINE_SEPARATOR;
if (verboseDocs) {
s += indent + "// Operations" + LINE_SEPARATOR;
}
int tmpGeneratorPass = generatorPass;
generatorPass = SOURCE_PASS;
String opString = new String();
Iterator opIterator = op.iterator();
while (opIterator.hasNext()) {
Object bf = opIterator.next();
if (!getFacade().isAbstract(bf)) {
Inline inlineStyle = Inline.getInlineOperationModifierType(bf);
if (inlineStyle.isMethodBodyOutsideClass()) {
opString += generateOperation(bf, false);
opString += LINE_SEPARATOR + generateMethodBody(bf)
+ LINE_SEPARATOR;
}
}
} // end loop through all operations
generatorPass = tmpGeneratorPass;
if (opString.length() != 0) {
s += opString;
return s;
} else {
return null;
}
}
} /* end class GeneratorCpp */