/***************************************************************************
                          javawriter.cpp  -  description
    This is the "old" code generator that does not support code editing
    in the Modeller but uses significantly less file space because the
    source code is not replicated in the XMI file.
                             -------------------
    copyright            : (C) 2003 Brian Thomas brian.thomas@gsfc.nasa.gov
         (C) 2004  Umbrello UML Modeller Authors <uml-devel@ uml.sf.net>
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

// own header
#include "javawriter.h"
// qt includes
#include <qfile.h>
#include <qtextstream.h>
#include <qregexp.h>
// kde includes
#include <kdebug.h>
// app includes
#include "../umldoc.h"
#include "../class.h"
#include "../interface.h"
#include "../operation.h"
#include "../attribute.h"
#include "../association.h"

JavaWriter::JavaWriter( UMLDoc *parent, const char *name ) : SimpleCodeGenerator(parent, name) {
	startline = m_endl + m_indentation;
}

JavaWriter::~JavaWriter() {}

QString JavaWriter::getLanguage() {
	return "Java";
}

bool JavaWriter::isType (QString & type)
{
	if (type == "JavaCodeGenerator")
		return true;
	return false;
}

void JavaWriter::writeClass(UMLClassifier *c)
{

	if (!c) {
		kdDebug()<<"Cannot write class of NULL concept!\n";
		return;
	}

	if(dynamic_cast<UMLInterface*>(c))
		isInterface = true;
	else
		isInterface = false;

	QString fileName = cleanName(c->getName().lower());

	//find an appropriate name for our file
	fileName = findFileName(c,".java");
	if (!fileName) {
		emit codeGenerated(c, false);
		return;
	}

	// check that we may open that file for writing
	QFile file;
	if ( !openFile(file,fileName+".java") ) {
		emit codeGenerated(c, false);
		return;
	}

	// Preparations
	//

	// sort attributes by Scope
	UMLAttributeList  atl;
	UMLAttributeList  atpub, atprot, atpriv;
	UMLAttributeList  final_atpub, final_atprot, final_atpriv;
	atpub.setAutoDelete(false);
	final_atpub.setAutoDelete(false);
	atprot.setAutoDelete(false);
	final_atprot.setAutoDelete(false);
	atpriv.setAutoDelete(false);
	final_atpriv.setAutoDelete(false);

	UMLClass * myClass = dynamic_cast<UMLClass *>(c);

	if(myClass) {
		UMLAttributeList atl = myClass->getAttributeList();
		for (UMLAttribute *at = atl.first(); at ; at = atl.next()) {
			switch(at->getScope())
			{
			case Uml::Public:
				if(at->getStatic())
					final_atpub.append(at);
				else
					atpub.append(at);
				break;
			case Uml::Protected:
				if(at->getStatic())
					final_atprot.append(at);
				else
					atprot.append(at);
				break;
			case Uml::Private:
				if(at->getStatic())
					final_atprot.append(at);
				else
					atpriv.append(at);
				break;
			}
		}
	}

	// another preparation, determine what we have
	UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
	UMLAssociationList aggregations = c->getAggregations();
	UMLAssociationList compositions = c->getCompositions();

	bool hasAssociations = aggregations.count() > 0 || associations.count() > 0 || compositions.count() > 0;
	bool hasAttributes = (atl.count() > 0);
	bool hasAccessorMethods = hasAttributes || hasAssociations;
	bool hasOperationMethods = (c->getOpList().count() > 0);
	// this is a bit too simplistic..some associations are for
	// SINGLE objects, and WONT be declared as Vectors, so this
	// is a bit overly inclusive
	bool hasVectorFields = hasAssociations ? true : false;

	// open text stream to file
	QTextStream java(&file);

	//try to find a heading file (license, coments, etc)
	QString str;
	str = getHeadingFile(".java");
	if(!str.isEmpty()) {
		str.replace(QRegExp("%filename%"),fileName);
		str.replace(QRegExp("%filepath%"),file.name());
		java<<str<<m_endl;
	}

	if(!c->getPackage().isEmpty())
		java<<"package "<<c->getPackage()<<";"<<m_endl;

	// IMPORT statements
	// Q: Why all utils? Isnt just List and Vector the only classes we are using?
    // A: doesn't matter at all; its more readable to just include '*' and java compilers
    //    don't slow down or anything. (TZ)
	if (hasVectorFields )
	{
		writeBlankLine(java);
		java<<"import java.util.*;"<<m_endl;
	}

	//only import classes in a different package as this class
	UMLClassifierList imports;
	findObjectsRelated(c,imports);
	for (UMLClassifier *con = imports.first(); con; con = imports.next()) {
		if (con->getBaseType() == Uml::ot_Datatype)
			continue;
		QString pkg = con->getPackage();
		if (!pkg.isEmpty() && pkg != c->getPackage())
			java << "import " << pkg << "." << cleanName(con->getName()) << ";"
			     << m_endl;
	}
	writeBlankLine(java);

	// write the opening declaration for the class incl any documentation,
	// interfaces and/or inheritence issues we have
	writeClassDecl(c, java);

	// start body of class
	java<<" {"<<m_endl;

	// ATTRIBUTES
	//

	// write comment for section IF needed
	if (forceDoc() || hasAccessorMethods)
	{
		writeComment("", m_indentation, java);
		writeComment("Fields", m_indentation, java);
		writeComment("", m_indentation, java);
		writeBlankLine(java);
	}

	writeAttributeDecls(final_atpub, final_atprot, final_atpriv, java);
	writeAttributeDecls(atpub, atprot, atpriv, java);

	writeAssociationDecls(associations, c->getID(), java);
	writeAssociationDecls(aggregations, c->getID(), java);
	writeAssociationDecls(compositions, c->getID(), java);

	// Constructors: anything we more we need to do here ?
	//
	if(!isInterface)
		writeConstructor(c, java);

	// METHODS
	//

	// write comment for section IF needed
	if (forceDoc() || hasAccessorMethods || hasOperationMethods)
	{

		java<<startline;
		writeComment("", m_indentation, java);
		writeComment("Methods", m_indentation, java);
		writeComment("", m_indentation, java);
		writeBlankLine(java);
		writeBlankLine(java);
	}

	// write comment for sub-section IF needed
	if (forceDoc() || hasAccessorMethods )
	{
		writeComment("", m_indentation, java);
		writeComment("Accessor methods", m_indentation, java);
		writeComment("", m_indentation, java);
		writeBlankLine(java);
	}

	// Accessors for attributes
	writeAttributeMethods(final_atpub, Uml::Public, java);
	writeAttributeMethods(final_atprot, Uml::Protected, java);
	writeAttributeMethods(final_atpriv, Uml::Private, java);
	writeAttributeMethods(atpub, Uml::Public, java);
	writeAttributeMethods(atprot, Uml::Protected, java);
	writeAttributeMethods(atpriv, Uml::Private, java);

	// accessor methods for associations

	// first: determine the name of the other class
	writeAssociationMethods(associations, c, java);
	writeAssociationMethods(aggregations, c, java);
	writeAssociationMethods(compositions, c, java);

	// Other operation methods
	// all other operations are now written

	// write comment for sub-section IF needed
	if (forceDoc() || hasOperationMethods)
	{
		writeComment("", m_indentation, java);
		writeComment("Other methods", m_indentation, java);
		writeComment("", m_indentation, java);
		writeBlankLine(java);
	}
	writeOperations(c,java);

	writeBlankLine(java);
	java<<"}"<<m_endl; // end class

	file.close();
	emit codeGenerated(c, true);
}

void JavaWriter::writeClassDecl(UMLClassifier *c, QTextStream &java)
{

	QString classname = cleanName(c->getName()); // our class name

	// write documentation for class, if any, first
	if(forceDoc() || !c->getDoc().isEmpty())
	{
		if(isInterface)
			writeDocumentation("Interface "+classname,c->getDoc(),"","",java);
		else
			writeDocumentation("Class "+classname,c->getDoc(),"","",java);

		writeBlankLine(java);
	}

	// Now write the actual class declaration
	QString scope = ""; // = scopeToJavaDecl(c->getScope());
	if (c->getScope() != Uml::Public) {
		// We should emit a warning in here .. java doesnt like to allow
		// private/protected classes. The best we can do (I believe)
		// is to let these declarations default to "package visibility"
		// which is a level between traditional "private" and "protected"
		// scopes. To get this visibility level we just print nothing..
	} else
		scope = "public ";

	java<<(c->getAbstract()?QString("abstract "):QString(""))<<scope;
	if(isInterface)
		java<<"interface ";
	else
		java<<"class ";

	java<<classname;


	// write inheritances out
	UMLClassifier *concept;
	UMLClassifierList superclasses = c->findSuperClassConcepts();

	if(superclasses.count()>0)
		java<<" extends ";

    // Q: Where is 'implements' ??

	int i = 0;
	for (concept= superclasses.first(); concept; concept = superclasses.next())
	{
		java<<cleanName(concept->getName())<<(i>0?", ":"");
		i++;
	}

}

void JavaWriter::writeAttributeDecls(UMLAttributeList &atpub, UMLAttributeList &atprot,
				     UMLAttributeList &atpriv, QTextStream &java )
{
	UMLAttribute *at;

	for(at=atpub.first(); at; at=atpub.next())
	{
		QString documentation = at->getDoc();
		QString staticValue = at->getStatic() ? "static " : "";
		QString typeName = fixTypeName(at->getTypeName());
		QString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName);
		if(!documentation.isEmpty())
			writeComment(documentation, m_indentation, java, true);
		java<<startline<<staticValue<<"public "<<typeName<<" "<<cleanName(at->getName())
		    <<(initialValue.isEmpty()?QString(""):QString(" = ") + initialValue)<<";";
	}

	for(at=atprot.first();at;at=atprot.next())
	{
		QString documentation = at->getDoc();
		QString typeName = fixTypeName(at->getTypeName());
		QString staticValue = at->getStatic() ? "static " : "";
		QString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName);
		if(!documentation.isEmpty())
			writeComment(documentation, m_indentation, java, true);
		java<<startline<<staticValue<<"protected "<<typeName<<" "<<cleanName(at->getName())
		    <<(initialValue.isEmpty()?QString(""):QString(" = ") + initialValue)<<";";
	}

	for(at=atpriv.first();at;at=atpriv.next())
	{
		QString documentation = at->getDoc();
		QString typeName = fixTypeName(at->getTypeName());
		QString staticValue = at->getStatic() ? "static " : "";
		QString initialValue = fixInitialStringDeclValue(at->getInitialValue(), typeName);
		if(!documentation.isEmpty())
			writeComment(documentation, m_indentation, java, true);
		java<<startline<<staticValue<<"private "<<typeName<<" "<<cleanName(at->getName())
		    <<(initialValue.isEmpty()?QString(""):QString(" = ") + initialValue)<<";";
	}

}

void JavaWriter::writeAttributeMethods(UMLAttributeList &atpub, Uml::Scope visibility, QTextStream &java)
{

	UMLAttribute *at;
	for(at=atpub.first(); at; at=atpub.next())
	{
		QString fieldName = cleanName(at->getName());
		// force capitalizing the field name, this is silly,
		// from what I can tell, this IS the default behavior for
		// cleanName. I dunno why its not working -b.t.
		fieldName.stripWhiteSpace();
		fieldName.replace(0,1,fieldName.at(0).upper());

		writeSingleAttributeAccessorMethods(at->getTypeName(),
						    cleanName(at->getName()),
						    fieldName,
						    at->getDoc(),
						    visibility, Uml::chg_Changeable, at->getStatic(), java);
	}

}

void JavaWriter::writeComment(QString comment, QString myIndent, QTextStream &java, bool javaDocStyle)
{
	// in the case we have several line comment..
	// NOTE: this part of the method has the problem of adopting UNIX newline,
	// need to resolve for using with MAC/WinDoze eventually I assume
	if (comment.contains(QRegExp("\n"))) {

		if(javaDocStyle)
			java << myIndent << "/**" << m_endl;
		QStringList lines = QStringList::split( "\n", comment);
		for(uint i= 0; i < lines.count(); i++)
		{
			writeBlankLine(java);
			if(javaDocStyle)
				java<<myIndent<<" * ";
			else
				java<<myIndent<<"// ";
			java << lines[i];
		}
		if(javaDocStyle)
			java << myIndent << " */" << m_endl;
	} else {
		// this should be more fancy in the future, breaking it up into 80 char
		// lines so that it doesnt look too bad
		writeBlankLine(java);
		if(javaDocStyle)
			java << myIndent << "/**" << m_endl << myIndent << " *";
		else
		    java<<myIndent<<"//";
		if(comment.length() > 0)
			java << " " << comment;
		if(javaDocStyle)
			java << m_endl << myIndent << " */";
	}
}

void JavaWriter::writeDocumentation(QString header, QString body, QString end, QString indent, QTextStream &java)
{
	writeBlankLine(java);
	java<<indent<<"/**"<<m_endl;
	if (!header.isEmpty())
		java<<formatDoc(header, indent+" * ");
	if (!body.isEmpty())
		java<<formatDoc(body, indent+" * ");
	if (!end.isEmpty())
	{
		QStringList lines = QStringList::split( "\n", end);
		for(uint i= 0; i < lines.count(); i++)
			java<<formatDoc(lines[i], indent+" * ");
	}
	java<<indent<<" */";
}

void JavaWriter::writeAssociationDecls(UMLAssociationList associations, Uml::IDType id, QTextStream &java)
{

	if( forceSections() || !associations.isEmpty() )
	{
		bool printRoleA = false, printRoleB = false;
		for(UMLAssociation *a = associations.first(); a; a = associations.next())
		{
			// it may seem counter intuitive, but you want to insert the role of the
			// *other* class into *this* class.
			if (a->getObjectId(Uml::A) == id)
				printRoleB = true;

			if (a->getObjectId(Uml::B) == id)
				printRoleA = true;

			// First: we insert documentaion for association IF it has either role AND some documentation (!)
			if ((printRoleA || printRoleB) && !(a->getDoc().isEmpty()))
				writeComment(a->getDoc(), m_indentation, java);

			// print RoleB decl
			if (printRoleB)
			{
				QString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::B)));
				writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::B), a->getMulti(Uml::B), a->getRoleDoc(Uml::B), a->getVisibility(Uml::B), java);
			}

			// print RoleA decl
			if (printRoleA)
			{
				QString fieldClassName = cleanName(getUMLObjectName(a->getObject(Uml::A)));
				writeAssociationRoleDecl(fieldClassName, a->getRoleName(Uml::A), a->getMulti(Uml::A), a->getRoleDoc(Uml::A), a->getVisibility(Uml::A), java);
			}
		}
	}
}

void JavaWriter::writeAssociationRoleDecl(QString fieldClassName,
					QString roleName, QString multi,
					QString doc, Uml::Scope visib, QTextStream &java)
{
	// ONLY write out IF there is a rolename given
	// otherwise its not meant to be declared in the code
	if (roleName.isEmpty())
		return;

	QString scope = scopeToJavaDecl(visib);

	// always put space between this and prior decl, if any
	writeBlankLine(java);

	if (!doc.isEmpty())
		writeComment(doc, m_indentation, java);

	// declare the association based on whether it is this a single variable
	// or a List (Vector). One day this will be done correctly with special
	// multiplicity object that we dont have to figure out what it means via regex.
	if(multi.isEmpty() || multi.contains(QRegExp("^[01]$")))
	{
		QString fieldVarName = roleName.replace(0, 1, roleName.left(1).lower());
		java<<startline<<scope<<" "<<fieldClassName<<" "<<fieldVarName<<";";
	}
	else
	{
		QString fieldVarName = roleName.lower() + "Vector";
		java<<startline<<scope<<" Vector "<<fieldVarName<<" = new Vector();";
		// from here we could initialize default values, or put in an init() section
		// of the constructors
	}

}

void JavaWriter::writeAssociationMethods (UMLAssociationList associations, UMLClassifier *thisClass, QTextStream &java)
{
	if( forceSections() || !associations.isEmpty() )
	{
		for(UMLAssociation *a = associations.first(); a; a = associations.next())
		{

			// insert the methods to access the role of the other
			// class in the code of this one
			if (a->getObjectId(Uml::A) == thisClass->getID())
			{
				// only write out IF there is a rolename given
				if(!a->getRoleName(Uml::B).isEmpty()) {
					QString fieldClassName = getUMLObjectName(a->getObject(Uml::B));
					writeAssociationRoleMethod(fieldClassName,
								   a->getRoleName(Uml::B),
								   a->getMulti(Uml::B), a->getRoleDoc(Uml::B),
								   a->getVisibility(Uml::B),
								   a->getChangeability(Uml::B), java);
				}
			}

			if (a->getObjectId(Uml::B) == thisClass->getID())
			{
				// only write out IF there is a rolename given
				if(!a->getRoleName(Uml::A).isEmpty()) {
					QString fieldClassName = getUMLObjectName(a->getObject(Uml::A));
					writeAssociationRoleMethod(fieldClassName, a->getRoleName(Uml::A),
								   a->getMulti(Uml::A),
								   a->getRoleDoc(Uml::A),
								   a->getVisibility(Uml::A),
								   a->getChangeability(Uml::A),
								   java);
				}
			}

		}
	}
}

void JavaWriter::writeAssociationRoleMethod (QString fieldClassName, QString roleName, QString multi,
					     QString description, Uml::Scope visib, Uml::Changeability_Type change,
					     QTextStream &java)
{
	if(multi.isEmpty() || multi.contains(QRegExp("^[01]$")))
	{
		QString fieldVarName = "m_" + roleName.lower();
		writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName,
						    description, visib, change, false, java);
	}
	else
	{
		QString fieldVarName = roleName.lower() + "Vector";
		writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName,
						    description, visib, change, java);
	}
}

void JavaWriter::writeVectorAttributeAccessorMethods (QString fieldClassName, QString fieldVarName,
						      QString fieldName, QString description,
						      Uml::Scope visibility, Uml::Changeability_Type changeType,
						      QTextStream &java)
{

	fieldClassName = fixTypeName(fieldClassName);
	fieldName = capitaliseFirstLetter(fieldName);
	QString strVis = scopeToJavaDecl(visibility);

	// ONLY IF changeability is NOT Frozen
	if (changeType != Uml::chg_Frozen)
	{
		writeDocumentation("Add a "+fieldName+" object to the "+fieldVarName+" List",description,"",m_indentation,java);
		java<<startline<<strVis<<" void add"<<fieldName<<" ( "<<fieldClassName<<" new_object ) {";
		java<<startline<<m_indentation<<fieldVarName<<".add(new_object);";
		java<<startline<<"}"<<m_endl;
	}

	// ONLY IF changeability is Changeable
	if (changeType == Uml::chg_Changeable)
	{
		writeDocumentation("Remove a "+fieldName+" object from "+fieldVarName+" List",description,"",m_indentation,java);
		java<<startline<<strVis<<" void remove"<<fieldName<<" ( "<<fieldClassName<<" new_object )";
		java<<startline<<"{";
		java<<startline<<m_indentation<<fieldVarName<<".remove(new_object);";
		java<<startline<<"}"<<m_endl;
	}

	// always allow getting the list of stuff
	writeDocumentation("Get the List of "+fieldName+" objects held by "+fieldVarName,description,"@return List of "+fieldName+" objects held by "+fieldVarName,m_indentation,java);
	java<<startline<<strVis<<" List get"<<fieldName<<"List ( ) {";
	java<<startline<<m_indentation<<"return (List) "<<fieldVarName<<";";
	java<<startline<<"}"<<m_endl;
	writeBlankLine(java);
}


void JavaWriter::writeSingleAttributeAccessorMethods(QString fieldClassName, QString fieldVarName,
						     QString fieldName, QString description,
						     Uml::Scope visibility, Uml::Changeability_Type change,
						     bool isFinal, QTextStream &java)
{

	QString strVis = scopeToJavaDecl(visibility);
	fieldClassName = fixTypeName(fieldClassName);
	fieldName = capitaliseFirstLetter(fieldName);

	// set method
	if (change == Uml::chg_Changeable && !isFinal) {
		writeDocumentation("Set the value of "+fieldVarName,description,"@param newVar the new value of "+fieldVarName,m_indentation,java);
		java<<startline<<strVis<<" void set"<<fieldName<<" ( "<<fieldClassName<<" newVar ) {";
		java<<startline<<m_indentation<<fieldVarName<<" = newVar;";
		java<<startline<<"}"<<m_endl;
	}

	// get method
	writeDocumentation("Get the value of "+fieldVarName,description,"@return the value of "+fieldVarName,m_indentation,java);
	java<<startline<<strVis<<" "<<fieldClassName<<" get"<<fieldName<<" ( ) {";
	java<<startline<<m_indentation<<"return "<<fieldVarName<<";";
	java<<startline<<"}";
	writeBlankLine(java);
}

void JavaWriter::writeConstructor(UMLClassifier *c, QTextStream &java)
{

	if (forceDoc())
	{
		java<<startline;
		writeComment("", m_indentation, java);
		writeComment("Constructors", m_indentation, java);
		writeComment("", m_indentation, java);
		writeBlankLine(java);
	}

	// write the first constructor
	QString className = cleanName(c->getName());
	java<<m_indentation<<"public "<<className<<" () { };";

}

// IF the type is "string" we need to declare it as
// the Java Object "String" (there is no string primative in Java).
// Same thing again for "bool" to "boolean"
QString JavaWriter::fixTypeName(QString string)
{
	if (string.isEmpty())
		return "void";
	string.replace(QRegExp("^string$"),"String");
	string.replace(QRegExp("^bool$"),"boolean");
	return string;
}

void JavaWriter::writeOperations(UMLClassifier *c, QTextStream &java) {
	UMLOperationList opl;
	UMLOperationList oppub,opprot,oppriv;
	oppub.setAutoDelete(false);
	opprot.setAutoDelete(false);
	oppriv.setAutoDelete(false);

	//sort operations by scope first and see if there are abstrat methods
	opl = c->getOpList();
	for (UMLOperation *op = opl.first(); op; op = opl.next()) {
		switch(op->getScope()) {
		case Uml::Public:
			oppub.append(op);
			break;
		case Uml::Protected:
			opprot.append(op);
			break;
		case Uml::Private:
			oppriv.append(op);
			break;
		}
	}

	// do people REALLY want these comments? Hmm.
	/*
	  if(forceSections() || oppub.count())
	  {
	  writeComment("public operations",m_indentation,java);
		writeBlankLine(java);
	  }
	*/
	writeOperations(oppub,java);

	/*
	  if(forceSections() || opprot.count())
	  {
	  writeComment("protected operations",m_indentation,java);
		writeBlankLine(java);
	  }
	*/
	writeOperations(opprot,java);

	/*
	  if(forceSections() || oppriv.count())
	  {
	  writeComment("private operations",m_indentation,java);
		writeBlankLine(java);
	  }
	*/
	writeOperations(oppriv,java);

}

void JavaWriter::writeOperations(UMLOperationList &oplist, QTextStream &java) {
	UMLOperation *op;
	UMLAttributeList *atl;
	UMLAttribute *at;
	int i,j;
	QString str;

	// generate method decl for each operation given
	for( op=oplist.first(); op ;op=oplist.next())
	{

		QString returnStr = "";
		// write documentation

		QString methodReturnType = fixTypeName(op->getTypeName());
		if(methodReturnType != "void")
			returnStr += "@return	"+methodReturnType+"\n";

		str = ""; // reset for next method
		str += ((op->getAbstract() || isInterface) ? "abstract ":"");
		str += (op->getStatic() ? "static":"");
		str += scopeToJavaDecl(op->getScope()) + " ";
		str += methodReturnType + " " +cleanName(op->getName()) + "( ";

		atl = op->getParmList();
		i= atl->count();
		j=0;
		for( at = atl->first(); at ;at = atl->next(),j++) {
			QString typeName = fixTypeName(at->getTypeName());
			QString atName = cleanName(at->getName());
			str += typeName + " " + atName +
			       (!(at->getInitialValue().isEmpty()) ?
			        (QString(" = ")+at->getInitialValue()) :
			        QString(""))
			       + ((j < i-1)?", ":"");
			returnStr += "@param	"+atName+" "+at->getDoc()+"\n";
		}
		str+= " )";

		// method only gets a body IF its not abstract
		if (op->getAbstract() || isInterface)
			str+=";\n\n"; // terminate now
		else
			str+=startline+"{\n\n"+m_indentation+"}\n\n"; // empty method body

		// write it out
		writeDocumentation("", op->getDoc(), returnStr, m_indentation, java);
		java<<startline<<str;
	}
}

QString JavaWriter::fixInitialStringDeclValue(QString value, QString type)
{
	// check for strings only
	if (!value.isEmpty() && type == "String") {
		if (!value.startsWith("\""))
			value.prepend("\"");
		if (!value.endsWith("\""))
			value.append("\"");
	}
	return value;
}

QString JavaWriter::scopeToJavaDecl(Uml::Scope scope)
{
	QString scopeString;
	switch(scope)
	{
		case Uml::Public:
			scopeString = "public";
			break;
		case Uml::Protected:
			scopeString = "protected";
			break;
		case Uml::Private:
		default:
			scopeString = "private";
			break;
	}
	return scopeString;
}

// methods like this _shouldnt_ be needed IF we properly did things thruought the code.
QString JavaWriter::getUMLObjectName(UMLObject *obj)
{
	return(obj!=0)?obj->getName():QString("NULL");
}

QString JavaWriter::capitaliseFirstLetter(QString string)
{
	// we could lowercase everything tostart and then capitalize? Nah, it would
	// screw up formatting like getMyRadicalVariable() to getMyradicalvariable(). Bah.
	QChar firstChar = string.at(0);
	string.replace( 0, 1, firstChar.upper());
	return string;
}

void JavaWriter::writeBlankLine(QTextStream &java)
{
	java<<m_endl;
}

