/***************************************************************************
                          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-2006  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 <tqfile.h>
#include <tqtextstream.h>
#include <tqregexp.h>
// kde includes
#include <kdebug.h>
// app includes
#include "codegen_utils.h"
#include "../umldoc.h"
#include "../classifier.h"
#include "../operation.h"
#include "../attribute.h"
#include "../association.h"
#include "../template.h"
#include "../umltemplatelist.h"

JavaWriter::JavaWriter() {
    startline = m_endl + m_indentation;
}

JavaWriter::~JavaWriter() {}

Uml::Programming_Language JavaWriter::getLanguage() {
    return Uml::pl_Java;
}

void JavaWriter::writeClass(UMLClassifier *c)
{

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

    isInterface = c->isInterface();

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

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

    // check that we may open that file for writing
    TQFile file;
    if ( !openFile(file, fileName) ) {
        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);

    if (!isInterface) {
        UMLAttributeList atl = c->getAttributeList();
        for (UMLAttribute *at = atl.first(); at ; at = atl.next()) {
            switch(at->getVisibility())
            {
              case Uml::Visibility::Public:
                if(at->getStatic())
                    final_atpub.append(at);
                else
                    atpub.append(at);
                break;
              case Uml::Visibility::Protected:
                if(at->getStatic())
                    final_atprot.append(at);
                else
                    atprot.append(at);
                break;
              case Uml::Visibility::Private:
                if(at->getStatic())
                    final_atpriv.append(at);
                else
                    atpriv.append(at);
                break;
              default:
                break;
            }
        }
    }

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

    UMLAssociationList aggregations = c->getAggregations();
    UMLAssociationList compositions = c->getCompositions();

    bool hasAssociations = aggregations.count() > 0 || associations.count() > 0 || compositions.count() > 0 || uniAssociations.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
    TQTextStream java(&file);

    //try to find a heading file (license, coments, etc)
    TQString str;
    str = getHeadingFile(".java");
    if(!str.isEmpty()) {
        str.replace(TQRegExp("%filename%"),fileName);
        str.replace(TQRegExp("%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
    UMLPackageList imports;
    findObjectsRelated(c,imports);
    for (UMLPackage *con = imports.first(); con; con = imports.next()) {
        if (con->getBaseType() == Uml::ot_Datatype)
            continue;
        TQString 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(uniAssociations, 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::Visibility::Public, java);
    writeAttributeMethods(final_atprot, Uml::Visibility::Protected, java);
    writeAttributeMethods(final_atpriv, Uml::Visibility::Private, java);
    writeAttributeMethods(atpub, Uml::Visibility::Public, java);
    writeAttributeMethods(atprot, Uml::Visibility::Protected, java);
    writeAttributeMethods(atpriv, Uml::Visibility::Private, java);

    // accessor methods for associations

    // first: determine the name of the other class
    writeAssociationMethods(associations, c, java);
    writeAssociationMethods(uniAssociations, 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, TQTextStream &java)
{

    TQString 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
    TQString scope = ""; // = scopeToJavaDecl(c->getVisibility());
    if (c->getVisibility() != Uml::Visibility::Public) {
        // We should emit a warning in here .. java doesn't 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() && !isInterface) ? TQString("abstract ") : TQString(""))<<scope;
    if(isInterface)
        java<<"interface ";
    else
        java<<"class ";

    java<<classname;

    // Generics
    UMLTemplateList template_params = c->getTemplateList();
    if (template_params.count()) {
        java << "<";
        for (UMLTemplate *t = template_params.first(); t; ) {
            TQString formalName = t->getName();
            java <<  formalName;
            TQString typeName = t->getTypeName();
            if (typeName != "class") {
                java << " extends " << typeName;
            }
            if ((t = template_params.next()) != NULL)
                java << ", ";
        }
        java << ">" << m_endl;
    }

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

    int i = 0;
    for (concept= superclasses.first(); concept; concept = superclasses.next())
    {
        if (i == 0)
        {
            java<< " extends ";
        }
        else
        {
            //The java generated code is wrong ! : No multiple inheritence of class
            java<< ", " ;
        }
        java<< cleanName(concept->getName());
        i++;
    }

    UMLClassifierList superInterfaces = c->findSuperClassConcepts(UMLClassifier::INTERFACE);
    i = 0;
    for (concept= superInterfaces.first(); concept; concept = superInterfaces.next())
    {
        if (i == 0)
        {
            if (isInterface)
                java<< " extends ";
            else
                java<< " implements ";
        }
        else
        {
            //The java generated code is OK ! : multiple inheritence of interface
            java<< ", " ;
        }
        java<< cleanName(concept->getName());
        i++;
    }

}

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

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

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

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

}

void JavaWriter::writeAttributeMethods(UMLAttributeList &atpub, Uml::Visibility visibility, TQTextStream &java)
{

    UMLAttribute *at;
    for(at=atpub.first(); at; at=atpub.next())
    {
        TQString 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(const TQString &comment, const TQString &myIndent,
                              TQTextStream &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(TQRegExp("\n"))) {

        if(javaDocStyle)
            java << myIndent << "/**" << m_endl;
        TQStringList lines = TQStringList::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 doesn't 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(TQString header, TQString body, TQString end, TQString indent, TQTextStream &java)
{
    writeBlankLine(java);
    java<<indent<<"/**"<<m_endl;
    if (!header.isEmpty())
        java<<formatDoc(header, indent+" * ");
    if (!body.isEmpty())
        java<<formatDoc(body, indent+" * ");
    if (!end.isEmpty())
    {
        TQStringList lines = TQStringList::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, TQTextStream &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)
            {
                TQString 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)
            {
                TQString 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(TQString fieldClassName,
        TQString roleName, TQString multi,
        TQString doc, Uml::Visibility visib, TQTextStream &java)
{
    // ONLY write out IF there is a rolename given
    // otherwise its not meant to be declared in the code
    if (roleName.isEmpty())
        return;

    TQString 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 don't have to figure out what it means via regex.
    if(multi.isEmpty() || multi.contains(TQRegExp("^[01]$")))
    {
        TQString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower());
        java<<startline<<scope<<" "<<fieldClassName<<" "<<fieldVarName<<";";
    }
    else
    {
        TQString 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, TQTextStream &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()) {
                    TQString 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()) {
                    TQString 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 (TQString fieldClassName, TQString roleName, TQString multi,
        TQString description, Uml::Visibility visib, Uml::Changeability_Type change,
        TQTextStream &java)
{
    if(multi.isEmpty() || multi.contains(TQRegExp("^[01]$")))
    {
        TQString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower());
        writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName,
                                            description, visib, change, false, java);
    }
    else
    {
        TQString fieldVarName = roleName.lower() + "Vector";
        writeVectorAttributeAccessorMethods(fieldClassName, fieldVarName, roleName,
                                            description, visib, change, java);
    }
}

void JavaWriter::writeVectorAttributeAccessorMethods (TQString fieldClassName, TQString fieldVarName,
        TQString fieldName, TQString description,
        Uml::Visibility visibility, Uml::Changeability_Type changeType,
        TQTextStream &java)
{

    fieldClassName = fixTypeName(fieldClassName);
    fieldName = Codegen_Utils::capitalizeFirstLetter(fieldName);
    TQString 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(TQString fieldClassName, TQString fieldVarName,
        TQString fieldName, TQString description,
        Uml::Visibility visibility, Uml::Changeability_Type change,
        bool isFinal, TQTextStream &java)
{

    TQString strVis = scopeToJavaDecl(visibility);
    fieldClassName = fixTypeName(fieldClassName);
    fieldName = Codegen_Utils::capitalizeFirstLetter(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, TQTextStream &java)
{

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

    // write the first constructor
    TQString 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"
TQString JavaWriter::fixTypeName(const TQString& string)
{
    if (string.isEmpty())
        return "void";
    if (string == "string")
        return "String";
    if (string == "bool")
        return "boolean";
    return string;
}

TQStringList JavaWriter::defaultDatatypes() {
    TQStringList l;
    l.append("int");
    l.append("char");
    l.append("boolean");
    l.append("float");
    l.append("double");
    l.append("byte");
    l.append("short");
    l.append("long");
    l.append("String");
    return l;
}


bool JavaWriter::compareJavaMethod(UMLOperation *op1, UMLOperation *op2)
{
    if (op1 == NULL || op2 == NULL)
        return false;
    if (op1 == op2)
        return true;
    if (op1->getName() != op2->getName())
        return false;
    UMLAttributeList atl1 = op1->getParmList();
    UMLAttributeList atl2 = op2->getParmList();
    if (atl1.count() != atl2.count())
        return false;
    UMLAttribute *at1;
    UMLAttribute *at2;
    for (at1  = atl1.first(), at2 = atl2.first(); at1 && at2 ; at1 = atl1.next(),at2 = atl2.next())
    {
        if (at1->getTypeName() != at2->getTypeName())
            return false;
    }
    return true;

}

bool JavaWriter::javaMethodInList(UMLOperation *umlOp, UMLOperationList &opl)
{
    for (UMLOperation *op = opl.first(); op; op = opl.next()) {
        if (JavaWriter::compareJavaMethod(op, umlOp)) {
            return true;
        }
    }
    return false;
}

void JavaWriter::getSuperImplementedOperations(UMLClassifier *c, UMLOperationList &yetImplementedOpList ,UMLOperationList &toBeImplementedOpList, bool noClassInPath)
{
    UMLClassifierList superClasses = c->findSuperClassConcepts();

    for (UMLClassifier *concept= superClasses.first(); concept; concept = superClasses.next())
    {
        getSuperImplementedOperations(concept, yetImplementedOpList, toBeImplementedOpList, (concept->isInterface() && noClassInPath));
        UMLOperationList opl = concept->getOpList();
        for (UMLOperation *op = opl.first(); op; op = opl.next()) {
            if (concept->isInterface() && noClassInPath) {
                if (!JavaWriter::javaMethodInList(op,toBeImplementedOpList))
                    toBeImplementedOpList.append(op);
            }
            else
            {
                if (!JavaWriter::javaMethodInList(op, yetImplementedOpList))
                    yetImplementedOpList.append(op);
            }
        }
    }

}

void JavaWriter::getInterfacesOperationsToBeImplemented(UMLClassifier *c, UMLOperationList &opList )
{
    UMLOperationList yetImplementedOpList;
    UMLOperationList toBeImplementedOpList;

    getSuperImplementedOperations(c,yetImplementedOpList, toBeImplementedOpList);
    for (UMLOperation *op = toBeImplementedOpList.first(); op; op = toBeImplementedOpList.next())
    {
        if ( ! JavaWriter::javaMethodInList(op, yetImplementedOpList) && ! JavaWriter::javaMethodInList(op, opList) )
            opList.append(op);
    }
}

void JavaWriter::writeOperations(UMLClassifier *c, TQTextStream &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 abstract methods
    opl = c->getOpList();
    if (! c->isInterface()) {
        getInterfacesOperationsToBeImplemented(c, opl);
    }
    for (UMLOperation *op = opl.first(); op; op = opl.next()) {
        switch(op->getVisibility()) {
          case Uml::Visibility::Public:
            oppub.append(op);
            break;
          case Uml::Visibility::Protected:
            opprot.append(op);
            break;
          case Uml::Visibility::Private:
            oppriv.append(op);
            break;
          default:
            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, TQTextStream &java) {
    UMLOperation *op;
    UMLAttributeList atl;
    UMLAttribute *at;
    int i,j;
    TQString str;

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

        TQString returnStr = "";
        // write documentation

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

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

        atl = op->getParmList();
        i= atl.count();
        j=0;
        for (at = atl.first(); at; at = atl.next(), j++) {
            TQString typeName = fixTypeName(at->getTypeName());
            TQString atName = cleanName(at->getName());
            str += typeName + ' ' + atName +
                   (!(at->getInitialValue().isEmpty()) ?
                    (TQString(" = ")+at->getInitialValue()) :
                    TQString(""))
                   + ((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;
    }
}

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

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

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

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

