/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package cb.generator;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;
import cb.petal.Association;
import cb.petal.ClassAttribute;
import cb.petal.Operation;
import cb.petal.Role;
import cb.petal.UsesRelationship;
import cb.util.Constants;
/**
* Factory for classes, methods, etc., it also contains methods to add relationships, like uses/realize relationships. If you don't like the
* way the default factory works just subclass it, and call setInstance() (before creating the generator, of course).
*
* @version $Id: Factory.java,v 1.2 2011/09/12 11:47:23 gpolet Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*/
public class Factory {
private static Factory instance = new Factory();
/**
* Register created objects by the quid of the petal object
*/
protected HashMap quid_map = new HashMap(); // Map<quid, QuidObject>
public void addObject(String quid, Node obj) {
quid_map.put(quid, obj);
}
public void removeObject(String quid) {
quid_map.remove(quid);
}
public Node getObject(String quid) {
return (Node) quid_map.get(quid);
}
public Collection getObjects() {
return quid_map.values();
}
protected Factory() {
}
public static Factory getInstance() {
return instance;
}
public static void setInstance(Factory f) {
instance = f;
}
protected String getClassName(Class clazz) {
if ("".equals(clazz.getPackage())) {
return clazz.getName();
} else {
return clazz.getPackage() + "." + clazz.getName();
}
}
/**
* Convert Rose identifier to normal one.
*/
protected String makeName(String name) {
char[] chars = name.toCharArray();
StringBuffer buf = new StringBuffer();
for (int i = 0; i < chars.length; i++) {
char ch = chars[i];
if (ch == ':' && chars[i + 1] == ':') {
buf.append('.');
i++;
} else {
if (Character.isLetterOrDigit(ch)) {
buf.append(ch);
} else {
buf.append('_');
}
}
}
return buf.toString();
}
/**
* Convert fully qualified rose name (with "foo::bar")
*
* @return tuple (class name, package name)
*/
protected String[] makeNames(String qual) {
String name, pack;
int index = qual.indexOf("::");
if (index < 0) { // weird ...
pack = "";
name = makeName(qual);
} else {
int index2 = qual.lastIndexOf("::");
if (index == index2) {
pack = "";
} else {
pack = makeName(qual.substring(index + 2, index2));
}
name = qual.substring(index2 + 2);
}
return new String[] { name, pack };
}
public Class createClass(cb.petal.Class clazz, String name, String pack, String acc, boolean is_interface) {
ClassImpl c = new ClassImpl();
c.setName(name);
c.setPackage(pack);
c.setAccess(acc);
c.isInterface(is_interface);
c.setClazz(clazz);
return c;
}
public Class createClass(cb.petal.Class clazz) {
String[] names = makeNames(clazz.getQualifiedName());
String acc = clazz.getExportControl();
if (acc == null) {
acc = "public";
}
return createClass(clazz, names[0], names[1], acc, clazz.isInterface());
}
public Method createMethod(Operation op, String name, String type, String acc, java.util.List params) {
MethodImpl method = new MethodImpl();
method.setName(name);
method.setAccess(acc);
method.setReturnType(type);
method.setParameters(params);
method.setOperation(op);
return method;
}
public Method createMethod(Operation op) {
String name = op.getNameParameter();
String acc = op.getExportControl();
if (acc == null) {
acc = "public";
}
String type = op.getResult();
if (type == null) {
type = "void";
}
ArrayList params = new ArrayList();
if (op.getParameters() != null) {
for (Iterator i = op.getParameters().getElements().iterator(); i.hasNext();) {
cb.petal.Parameter p = (cb.petal.Parameter) i.next();
params.add(createParameter(p));
}
}
return createMethod(op, name, type, acc, params);
}
public Parameter createParameter(String name, String type) {
ParameterImpl param = new ParameterImpl();
param.setName(name);
param.setType(type);
return param;
}
public Parameter createParameter(cb.petal.Parameter p) {
String type = p.getType();
String name = p.getNameParameter();
if (type == null) { // Sometimes type and name stick together "AType t"
int index = name.indexOf(' ');
if (index < 0) {
type = "Object";
} else {
type = name.substring(0, index);
name = name.substring(index + 1);
}
}
return createParameter(name, type);
}
public Field createField(ClassAttribute attr, String name, String type, String acc, String init) {
FieldImpl field = new FieldImpl();
field.setName(name);
field.setAccess(acc);
field.setInitialValue(init);
field.setType(type);
field.setAttribute(attr);
return field;
}
public Field createField(ClassAttribute attr) {
String name = attr.getNameParameter();
String acc = attr.getExportControl();
if (acc == null) {
acc = "private";
}
if (attr.getStatic()) {
acc += " static";
}
String type = attr.getType();
if (type == null) {
type = "Object";
}
String init = attr.getInitialValue();
return createField(attr, name, type, acc, init);
}
public void addSuperClass(Class c, Class super_class) {
c.addSuperClass(getClassName(super_class));
}
public final void addImplementedInterface(Class c, Class super_class) {
addRealizedClass(c, super_class);
}
/**
* Alias for addImplementedInterface()
*/
public void addRealizedClass(Class c, Class super_class) {
c.addImplementedInterface(getClassName(super_class));
// TODO: Add inherited methods
}
protected int counter;
public Method[] createSetGetMethods(String name, String type) {
String mname = Character.toUpperCase(name.charAt(0)) + name.substring(1);
Method set = createMethod(null, "set" + mname, "void", "public", Arrays.asList(new Parameter[] { createParameter(name, type) }));
set.setCode(Arrays.asList(new String[] { " this." + name + " = " + name + ";" }));
Method get = createMethod(null, "get" + mname, type, "public", Collections.EMPTY_LIST);
get.setCode(Arrays.asList(new String[] { " return this." + name + ";" }));
return new Method[] { set, get };
}
private static String map(String number) {
if ("n".equals(number.toLowerCase()) || "*".equals(number)) {
return "" + Integer.MAX_VALUE;
} else {
return number;
}
}
protected Dimension getCardinality(Role role) {
int from = 1, to = 1;
if (role.getClientCardinality() != null) {
String card = role.getClientCardinality().getStringValue();
StringTokenizer tok = new StringTokenizer(card, ".");
try {
from = Integer.parseInt(map(tok.nextToken()));
if (tok.hasMoreTokens()) {
to = Integer.parseInt(map(tok.nextToken()));
} else {
to = from;
}
} catch (Exception e) {
throw new RuntimeException("Invalid cardinality " + card);
}
} else {
Role other = role.getOtherRole();
if (other.isAggregate()) {
from = 0;
to = Integer.MAX_VALUE;
}
}
return new Dimension(from, to);
}
private Class createAssociationClass(Class class1, Class class2, Class assoc_class) {
String name = class1.getName() + "_" + class2.getName() + "_" + counter++;
Class clazz = createClass(null, name, class1.getPackage(), "public final", false);
if (assoc_class != null) {
name = assoc_class.getName();
clazz = assoc_class;
}
String name1 = class1.getQualifiedName();
String name2 = class2.getQualifiedName();
boolean equal = name1.equals(name2); // Self association?
String map1 = class1.getName() + "_" + class2.getName();
String map2 = class2.getName() + "_" + class1.getName();
clazz.addPrefixCode("import java.util.*;");
Field field = createField(null, map1, "HashMap", "private", "new HashMap()");
clazz.addField(field);
if (!equal) {
field = createField(null, map2, "HashMap", "private", "new HashMap()");
clazz.addField(field);
}
field = createField(null, "instance", name, "public static final", "new " + name + "()");
clazz.addField(field);
// Constructor
Method method = createMethod(null, name, "", "private", Collections.EMPTY_LIST);
if (!clazz.getMethods().contains(method)) {
clazz.addMethod(method);
}
method = createMethod(null, "lookup", "HashSet", "private static final",
Arrays.asList(new Parameter[] { createParameter("map", "HashMap"), createParameter("obj", "Object") }));
method.setCode(Arrays.asList(new String[] { " HashSet set = (HashSet)map.get(obj);\n", " if(set == null)",
" map.put(obj, set = new HashSet());\n", " return set;" }));
clazz.addMethod(method);
method = createConnectionMethod(map1, map2, equal, "add", name1, name2);
clazz.addMethod(method);
method = createConnectionMethod(map1, map2, equal, "remove", name1, name2);
clazz.addMethod(method);
method = createMethod(null, "getConnections", "Collection", "public final",
Arrays.asList(new Parameter[] { createParameter("a", name1) }));
method.setCode(Arrays.asList(new String[] { " return lookup(" + map1 + ", a);" }));
clazz.addMethod(method);
if (!equal) {
method = createMethod(null, "getConnections", "Collection", "public final",
Arrays.asList(new Parameter[] { createParameter("b", name2) }));
method.setCode(Arrays.asList(new String[] { " return lookup(" + map2 + ", b);" }));
clazz.addMethod(method);
}
return clazz;
}
private Method createConnectionMethod(String map1, String map2, boolean equal, String call, String name1, String name2) {
Method method = createMethod(null, call + "Connection", "void", "public final",
Arrays.asList(new Parameter[] { createParameter("a", name1), createParameter("b", name2) }));
ArrayList code = new ArrayList();
code.add(" HashSet set1 = lookup(" + map1 + ", a);");
code.add(" set1." + call + "(b);\n");
if (!equal) {
code.add(" HashSet set2 = lookup(" + map2 + ", b);");
code.add(" set2." + call + "(a);");
}
method.setCode(code);
return method;
}
/**
* Simply maps association to a newly generated class that maintains the connections. If the association has an association class no new
* class is generated. Also adds access methods to the corresponding classes.
*
* TODO: Does not regard multiple associations between classes, cardinality currently not checked.
*/
public void addAssociation(Class class1, Role role1, Class class2, Role role2, Class assoc_class) {
Dimension dim1 = getCardinality(role1);
Dimension dim2 = getCardinality(role2);
Association assoc = (Association) role1.getParent();
String name1 = class1.getQualifiedName();
String name2 = class2.getQualifiedName();
boolean equal = name1.equals(name2); // Self association?
Class clazz = createAssociationClass(class1, class2, assoc_class);
if (assoc_class != null) {
addObject(assoc.getQuid(), clazz);
}
Method method = createMethod(null, "add" + class2.getName(), "void", "public",
Arrays.asList(new Parameter[] { createParameter("a", name2) }));
method.setCode(Arrays.asList(new String[] { " " + clazz.getQualifiedName() + ".instance.addConnection(this, a);" }));
class1.addMethod(method);
method = createMethod(null, "remove" + class2.getName(), "void", "public",
Arrays.asList(new Parameter[] { createParameter("a", name2) }));
method.setCode(Arrays.asList(new String[] { " " + clazz.getQualifiedName() + ".instance.removeConnection(this, a);" }));
class1.addMethod(method);
method = createMethod(null, "get" + class2.getName() + "s", "java.util.Collection", "public", Collections.EMPTY_LIST);
method.setCode(Arrays.asList(new String[] { " return " + clazz.getQualifiedName() + ".instance.getConnections(this);" }));
class1.addMethod(method);
method = createMethod(null, "add" + class1.getName(), "void", "public",
Arrays.asList(new Parameter[] { createParameter("a", name1) }));
method.setCode(Arrays.asList(new String[] { " " + clazz.getQualifiedName() + ".instance.addConnection(a, this);" }));
class2.addMethod(method);
method = createMethod(null, "remove" + class1.getName(), "void", "public",
Arrays.asList(new Parameter[] { createParameter("a", name1) }));
method.setCode(Arrays.asList(new String[] { " " + clazz.getQualifiedName() + ".instance.removeConnection(a, this);" }));
class2.addMethod(method);
method = createMethod(null, "get" + class1.getName() + "s", "java.util.Collection", "public", Collections.EMPTY_LIST);
method.setCode(Arrays.asList(new String[] { " return " + clazz.getQualifiedName() + ".instance.getConnections(this);" }));
class2.addMethod(method);
}
public void addUsedClass(Class c, Class used_class, UsesRelationship rel) {
// Simply adds field and set/get methods here
String type = getClassName(used_class);
String name = Constants.makeName(rel.getLabel());
if (name == null) {
name = "uses" + counter++;
}
Field f = createField(null, name, type, "private", null);
c.addField(f);
Method[] m = createSetGetMethods(name, type);
c.addMethod(m[0]);
c.addMethod(m[1]);
}
public void addField(Class c, Field field) {
// Add set/get methods if the field is private and non-static
if (c.isInterface()) {
field.setAccess("public static final");
if (field.getInitialValue() == null) {
field.setInitialValue(Constants.getValueForType(field.getType()));
}
} else {
if (field.is("private") && !field.is("static")) {
Method[] m = createSetGetMethods(field.getName(), field.getType());
c.addMethod(m[0]);
c.addMethod(m[1]);
}
}
c.addField(field);
}
public void addMethod(Class c, Method m) {
if (c.isInterface() && !m.is("abstract")) {
m.setAccess(m.getAccess() + " abstract");
}
c.addMethod(m);
}
}