/*
* (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.petal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.TreeSet;
/**
* Super class for all petal objects which have a list of properties. Unfortunately, property names may occur multiply. This can happen,
* e.g., if there are multiple notes attached to a class. Thus it is not implemented with a HashMap as one might think.
*
* @version $Id: PetalObject.java,v 1.3 2011/09/12 11:46:47 gpolet Exp $
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
*/
public abstract class PetalObject implements PetalNode {
static final long serialVersionUID = 7215267546012147332L;
public static final ArrayList EMPTY = new ArrayList() {
static final long serialVersionUID = -3029611157463610527L;
@Override
public int size() {
return 0;
}
@Override
public boolean contains(java.lang.Object o) {
return false;
}
@Override
public java.lang.Object get(int i) {
throw new IndexOutOfBoundsException("Index: " + i);
}
};
private java.util.List<String> names = new ArrayList<String>();
private java.util.List<PetalNode> values = new ArrayList<PetalNode>();
private String name;
protected java.util.List<String> params = EMPTY;
private PetalNode parent;
/**
* @param parent
* node in the petal tree, either another PetalObject or PetalFile
* @param name
* of the object, e.g., "ClassCategory"
* @param params
* list of parameters, e.g., "Class" "Logical View::templates::Class"
*/
protected PetalObject(PetalNode parent, String name, java.util.List<String> params) {
setParent(parent);
setName(name);
setParameterList(params);
}
/**
* @param parent
* node in the petal tree, either another PetalObject or PetalFile
* @param name
* of the object, e.g., "ClassCategory"
* @param params
* list of parameters, e.g., "Class" "Logical View::templates::Class"
*/
protected PetalObject(PetalNode parent, String name, Collection<String> params) {
this(parent, name, new ArrayList<String>(params));
}
/**
* @param name
* of the object, e.g., "ClassCategory"
*/
protected PetalObject(String name) {
setName(name);
}
/**
* @return shallow copy of object, do not forget to assign a new quid if you want to use it within the same model.
*/
@Override
public java.lang.Object clone() {
PetalObject obj = null;
try {
obj = (PetalObject) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
obj.names = new ArrayList<String>(names);
obj.values = new ArrayList<PetalNode>(values);
obj.params = params == EMPTY ? EMPTY : new ArrayList<String>(params);
return obj;
}
/**
* @return true if the name and all properties are equal without regarding the order of the properties.
*/
public boolean equals(Object o) {
if (o != null && o.getClass() == this.getClass()) {
PetalObject obj = o;
if (!this.name.equals(obj.name)) {
return false;
}
if (this.values.size() != obj.values.size()) {
return false;
}
TreeSet n1 = new TreeSet(this.names);
TreeSet n2 = new TreeSet(obj.names);
if (!n1.equals(n2)) {
return false;
}
for (Iterator i = names.iterator(), j = values.iterator(); i.hasNext();) {
String name = (String) i.next();
PetalNode value1 = (PetalNode) j.next();
PetalNode value2 = obj.getProperty(name);
if (!value1.equals(value2)) {
return false;
}
}
return true;
} else {
return false;
}
}
@Override
public int hashCode() {
int h = 0;
for (String name : names) {
h += name.hashCode();
}
for (PetalNode o : values) {
h += o.hashCode();
}
for (String param : params) {
h += param.hashCode();
}
return h;
}
/**
* Perform any initial actions after all properties have been set up. Called by PetalParser when all properties have been defined or
* when a new object created by the user is added to the model.
*/
public void init() {
}
public void setParent(PetalNode p) {
parent = p;
}
public PetalNode getParent() {
return parent;
}
@Override
public String getKind() {
return "object";
}
public void setName(String n) {
name = n.intern();
}
public String getName() {
return name;
}
/**
* @return top-level root node of this model
*/
public final PetalFile getRoot() {
PetalNode parent = this.parent;
while (!(parent instanceof PetalFile)) {
parent = ((PetalObject) parent).parent;
}
return (PetalFile) parent;
}
/**
* Override property at i, value's "parent" reference points to this object afterwards
*/
public final void setProperty(int i, String name, PetalNode value) {
if (value == null) {
throw new RuntimeException("Value for " + name + " must not be null");
}
names.set(i, name.intern()); // Use intern() to save lots of memory
values.set(i, value);
}
/**
* Add a property (which may already exist, Petal files allow to define properties multiply).
*
* @return index of property
*/
public final int addProperty(String name, PetalNode value) {
if (value == null) {
throw new RuntimeException("Value for " + name + " must not be null");
}
names.add(name.intern()); // Use intern() to save lots of memory
values.add(value);
return names.size() - 1;
}
/**
* This method is strict in that it does not use equals() to search the list of properties but ==, since values, in particular literals
* may occur more than once.
*/
public final int indexOf(PetalNode value) {
int j = 0;
for (Iterator i = values.iterator(); i.hasNext(); j++) {
if (i.next() == value) {
return j;
}
}
return -1;
}
/**
* This method uses the strict indexOf method to find the value.
*/
public final String getPropertyName(PetalNode value) {
int i = indexOf(value);
if (i >= 0) {
return names.get(i);
} else {
return null;
}
}
/**
* @return number of properties
*/
public int getNoProperties() {
return names.size();
}
/**
* @return number of properties
*/
@Override
public int getChildCount() {
return names.size();
}
/**
* Override property if exists already or add it if not.
*
* @return index of property
*/
public final int defineProperty(String name, PetalNode value) {
int index = names.indexOf(name);
if (index >= 0) {
setProperty(index, name, value);
return index;
} else {
return addProperty(name, value);
}
}
/**
* @return property at given index
*/
public final PetalNode getProperty(int i) {
return values.get(i);
}
/**
* @return first occurrence of property name
*/
public final PetalNode getProperty(String name) {
int index = names.indexOf(name);
if (index >= 0) {
return values.get(index);
} else {
return null;
}
}
/**
* Override property if exists already or add it if not.
*/
public final void defineProperty(String name, String value) {
ArrayList list = new ArrayList();
StringTokenizer st = new StringTokenizer(value, "\r\n");
while (st.hasMoreTokens()) {
list.add(st.nextToken());
}
StringLiteral lit = new StringLiteral(list);
lit.setMulti(list.size() > 1);
defineProperty(name, lit);
}
/**
* @return given property, or null if it doesn't exist.
*/
public String getPropertyAsString(String name) {
StringLiteral s = (StringLiteral) getProperty(name);
if (s == null) {
// System.err.println("No such property: " + name + " for " + this);
return null;
}
return s.getValue();
}
/**
* Strict variant: If the property does not exist, an exception is thrown
*/
static String getPropertyAsString(PetalObject obj, String prop) {
StringLiteral s = (StringLiteral) obj.getProperty(prop);
if (s == null) {
throw new RuntimeException("No property named " + prop + " for " + obj);
}
return s.getValue();
}
/**
* @return given property, or Integer.MIN_VALUE if it doesn't exist.
*/
public int getPropertyAsInteger(String name) {
IntegerLiteral s = (IntegerLiteral) getProperty(name);
if (s == null) {
// System.err.println("No such property: " + name + " for " + this);
return Integer.MIN_VALUE;
}
return s.getValue();
}
/**
* Override property if exists already or add it if not.
*/
public void defineProperty(String name, int value) {
defineProperty(name, new IntegerLiteral(value));
}
/**
* @return given property, or false if it doesn't exist.
*/
public boolean getPropertyAsBoolean(String name) {
BooleanLiteral s = (BooleanLiteral) getProperty(name);
if (s == null) {
// System.err.println("No such property: " + name + " for " + this);
return false;
}
return s.getValue();
}
/**
* Override property if exists already or add it if not.
*/
public void defineProperty(String name, boolean value) {
defineProperty(name, new BooleanLiteral(value));
}
/**
* @return given property, or Double.MIN_VALUE if it doesn't exist.
*/
public double getPropertyAsFloat(String name) {
FloatLiteral s = (FloatLiteral) getProperty(name);
if (s == null) {
// System.err.println("No such property: " + name + " for " + this);
return Double.MIN_VALUE;
}
return s.getValue();
}
/**
* Override property if exists already or add it if not.
*/
public void defineProperty(String name, double value) {
defineProperty(name, new FloatLiteral(value));
}
/**
* Remove property with given name
*/
public void removeProperty(String name) {
removeProperty(names.indexOf(name));
}
/**
* Remove property at given index
*/
public void removeProperty(int index) {
if (index >= 0) {
names.remove(index);
values.remove(index);
}
}
/**
* Move property within list of properties, i.e. change order.
*/
public void moveProperty(int from, int to) {
if (from == to) {
return;
}
String name = names.get(from);
PetalNode value = values.get(from);
if (from < to) {
to--;
}
removeProperty(from);
names.add(to, name);
values.add(to, value);
}
/**
* @return the longest name in names list, needed for indentation issues
*/
public String getLongestName() {
String max = "";
for (Iterator i = names.iterator(); i.hasNext();) {
String s = (String) i.next();
if (s.length() > max.length()) {
max = s;
}
}
return max;
}
/**
* @return all properties with key "name"
*/
public ArrayList getProperties(String name) {
ArrayList list = new ArrayList();
for (Iterator i = names.iterator(), j = values.iterator(); i.hasNext();) {
String s = (String) i.next();
PetalNode o = (PetalNode) j.next();
if (s.equals(name)) {
list.add(o);
}
}
return list;
}
/**
* @return all property names
*/
public java.util.List<String> getNames() {
return new ArrayList<String>(names);
}
/**
* @return all property values
*/
public java.util.List<PetalNode> getPropertyList() {
return new ArrayList<PetalNode>(values);
}
/**
* @return array of properties, where obj[i][0] == name and obj[i][1] == property
*/
public java.lang.Object[][] getPropertyTuples() {
java.lang.Object[][] props = new java.lang.Object[names.size()][];
int k = 0;
for (Iterator i = names.iterator(), j = values.iterator(); i.hasNext(); k++) {
props[k] = new java.lang.Object[] { i.next(), j.next() };
}
return props;
}
public java.util.List<String> getParameterList() {
return params;
}
public void setParameterList(java.util.List<String> params) {
this.params = params;
}
/**
* Add object to some given list and create the list if necessary.
*/
protected void addToList(String prop_name, String list_name, PetalObject o) {
List list = (List) getProperty(prop_name);
if (list == null) {
list = new List(list_name);
defineProperty(prop_name, list);
}
list.add(o);
}
/**
* Remove object from some given list.
*/
protected void removeFromList(String prop_name, PetalObject o) {
List list = (List) getProperty(prop_name);
if (list != null) {
list.remove(o);
}
}
/**
* Get fully qualified name for an object that must implement the Named interface and is contained by further Named objects. Typical
* nesting of an object is (Design .. (ClassCategory ... (Class ...)))
*
* @see Named
* @see Class
* @see UseCase
* @return String like "Logical View::University::Professor"
*/
public String getQualifiedName() {
PetalNode n = this.getParent();
String result = ((Named) this).getNameParameter();
while (!(n instanceof Design) && n instanceof Named) {
result = ((Named) n).getNameParameter() + "::" + result;
n = ((PetalObject) n).getParent();
}
return result;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer("(object " + name);
for (Iterator i = params.iterator(); i.hasNext();) {
buf.append(" \"" + i.next() + "\"");
}
buf.append("\n");
for (Iterator i = names.iterator(), j = values.iterator(); i.hasNext();) {
buf.append(i.next() + "\t" + j.next());
if (i.hasNext()) {
buf.append("\n");
}
}
buf.append(")\n");
return buf.toString();
}
@Override
public abstract void accept(Visitor v);
}