//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
*
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.framework.datamodel;
import com.nexirius.framework.FWLog;
import com.nexirius.framework.jdbc.JnexPreparedStatement;
import com.nexirius.framework.command.Command;
import com.nexirius.util.*;
import com.nexirius.util.assertion.Assert;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Enumeration;
/**
* A class that represents any kind of viewable and/or editable data
*
* @author Marcel Baumann
*/
public abstract class DataModel
implements
DataModelTreeNodeAttributes
, Externalizable
, Sorter
, Editable
, Viewable
, Serializable {
public static int instanceCount = 0;
public static String FIELD_NAME_SEPARATOR = new String(";");
public static char FIELD_NAME_SEPARATOR_CHAR = ';';
public static final String FIELD_NAME_INSTANCE_NAME = "&"; // access the instance name with getChildText
public static final String FIELD_NAME_CAPTION = "_"; // access the caption string with getChildText
public static final String FIELD_NAME_THIS_1 = "this"; // field name to access this
public static final String FIELD_NAME_THIS_2 = "null"; // field name to access this
public static final String FIELD_NAME_THIS_3 = "."; // field name to access this
public static final String FIELD_NAME_PARENT_MODEL = ".."; // field name to access the parent data model
Object listenerList = null;
private WeakReference parent = null;
protected DataModelTreeNode treeHook = null;
String instanceName = null;
boolean dirtyFlag = false;
FieldName fieldName = null;
Object value = null;
Validator validator = null;
DataModelCommandVector methods = null;
boolean fireEvents = true;
private ModelStatus status = new ModelStatus();
Object oldValue = null; // set while editing
private ModelStatus oldStatus = null; // set while editing
/**
* Creates an instance and initializes its value
*
* @param value The initial value
*/
public DataModel(Object value) {
++instanceCount;
//System.out.println(getClass().getName());
if (value == null) {
throw new IllegalArgumentException("DataModel value may not be null");
}
this.value = value;
}
/**
* Creates an instance and initializes its value and the field name
*
* @param value The initial value
* @param fieldName The field name (must not contain ';')
*/
public DataModel(Object value, String fieldName) {
this(value);
setFieldName(fieldName);
}
/*
protected void finalize()
{
System.out.println("Finalize " + getClass().getName());
--instanceCount;
}
*/
/*
* remove the contents
*/
public abstract void clear();
/*
* reset the contents
*/
public void reset() {
}
/**
* Set the changed flag on the data model if it (or one of its parents) has getInstanceName() != null.
*
* @return if the changed flag is toggled to true this method returns this or the parent data model, which has an instance name != null
*/
public DataModel setDirtyFlag() {
if (getInstanceName() != null) {
if (!dirtyFlag) {
dirtyFlag = true;
return this;
}
}
if (getParentDataModel() != null) {
return getParentDataModel().setDirtyFlag();
}
return null;
}
public boolean getDirtyFlag() {
return dirtyFlag;
}
public void resetDirtyFlag() {
dirtyFlag = false;
}
/**
* Get the containing parent model
*
* @return The parent model or null (not part of any container)
* @see com.nexirius.framework.datamodel.DataModelContainer#append
*/
public DataModel getParentDataModel() {
return parent == null ? null : (DataModel) parent.get();
}
/**
* Get the containing parent model
*
* @return The parent model or null (not part of any container)
* @see com.nexirius.framework.datamodel.DataModelContainer#append
*/
public DataModelContainer getParentDataModelContainer() {
return parent == null ? null : (DataModelContainer) parent.get();
}
/**
* Set the parent data model container (should only happen once in a DataModels life time)
*
* @param newParent The container which this model is added to
* @see DataModelContainer#append
*/
public void setParentDataModelContainer(DataModelContainer newParent) {
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null && newParent != null) {
String info = "---------The parent " + parentDataModel.getFieldName() + " lost child " + getFieldName();
FWLog.debug(info);
new Exception(info).printStackTrace();
}
//FIX Assert.pre(parent == null, "The current parent of a DataModel must be null (remove it from its original DataModelContainer) (The same DataModel instance cannot be added twice to a DataModelContainer)");
parent = new WeakReference(newParent);
}
/**
* The registered DataModelListener only receive events when fireEvents is true (default)
*
* @param on If true events are fired
* @see DataModelListener
*/
public void setFireEvents(boolean on) {
fireEvents = on;
}
/**
* The registered DataModelListeners only receive events when fireEvents is true (default)
*
* @return If events are fired (default: true)
* @see DataModelListener
*/
public boolean getFireEvents() {
return fireEvents;
}
/**
* Return and creates (with the first call only) the holder which is needed to display the
* DataModel in a tree
*/
public DataModelTreeNode getTreeHook() {
if (treeHook == null) {
treeHook = new TreeHook(this);
}
return treeHook;
}
/**
* Persistance interface read function
*
* @param in The stream where the DataModel reads from
* @throws Exception If the stream is not interpreted correctly
*/
public void readDataFrom(PushbackInputStream in) throws Exception {
clearStatus();
status.readDataFrom(in, this);
}
/**
* Persistance interface write function
*
* @param out The stream where the DataModel writes to
* @throws Exception If the stream is not writeable
*/
public void writeDataTo(OutputStream out) throws Exception {
status.writeDataTo(out);
}
/**
* Read new value from a String (DRAG-DROP interface)
*
* @param value The new value which is assigned
* @throws Exception If the value is not interpreted correctly
*/
public void dropData(String value)
throws Exception {
readDataFrom(TextToken.createPushbackInputStream(value));
}
/**
* Write value to a String (DRAG-DROP interface)
*
* @return A portable String which can be used to set the value of another DataModel (dropData)
* @throws Exception If the value is not persistant
*/
public String dragData()
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
writeDataTo(out);
out.close();
return out.toString();
}
/**
* Define a field name
*
* @param fieldName the new fieldName (must not contain ';')
*/
public void setFieldName(String fieldName) {
this.fieldName = new BasicFieldName(fieldName);
}
/**
* Define a field name
*
* @param index the new fieldName for array members
*/
public void setFieldName(int index) {
this.fieldName = new BasicFieldName(index);
}
/**
* Get the field name
*/
public String getFieldName() {
return (fieldName == null ? "" : fieldName.toString());
}
public abstract String getCaption();
/**
* Get all the fieldnames of all the children recursively (used for StructModel initialisation!)
*/
public FieldName getFullFieldName() {
Assert.pre(this.fieldName != null, "this.fieldName is not null (" + this.getClass().getName() + ")");
DataModelEnumeration e = getEnumeration();
if (e.size == 0) {
return this.fieldName;
}
FullFieldName fn = new FullFieldName(this.fieldName.toString());
while (e.hasMore()) {
DataModel child = e.next();
fn.append(child.getFullFieldName());
}
return fn;
}
/**
* Get the fieldnames of the direct children as StringVector
*/
public StringVector getChildrenFieldNames() {
DataModelEnumeration e = getEnumeration();
StringVector ret = new StringVector();
while (e.hasMore()) {
DataModel child = e.next();
ret.append(child.fieldName.toString());
}
return ret;
}
/**
* Get an iterater which helps to traverse the list of children
*
* @return A DataModelEnumeration (maybe empty but never null)
*/
public DataModelEnumeration getEnumeration() {
return new DataModelEnumeration();
}
/**
* Get a eventually hierachically deeply nested child
*
* @param child The name evetually separated by ';'
* @return true if child is part of this model
*/
public boolean hasSubChild(DataModel child) {
try {
getSubchildIndex(child);
return true;
} catch (Exception ex) {
}
return false;
}
/**
* Get the index for a subchild (compares with == not with equals!)
*
* @param child The child or subchild which is looked for
* @return The index of the direct child which holds the subchild
* @throws Exception If the child is not part of this model
*/
public int getSubchildIndex(DataModel child)
throws Exception {
DataModelEnumeration e = getEnumeration();
int i = 0;
while (e.hasMore()) {
DataModel model = e.next();
if (model == child) {
return i;
} else if (model.hasSubChild(child)) {
return i;
}
++i;
}
throw new Exception("No subchild " + child.getFieldName());
}
/**
* Get the index of a direct child
*
* @param child The child which is looked for
* @return The position of the child
* @throws Exception If the child is not a direct child of this model
*/
public synchronized int getChildIndex(DataModel child)
throws Exception {
Assert.pre(child != null, "No index defined for null child");
DataModelEnumeration e = getEnumeration();
int i = 0;
while (e.hasMore()) {
if (e.next() == child) {
return i;
}
++i;
}
throw new Exception("No child " + child.getFieldName());
}
/**
* Look for a child by fieldName
*
* @param fieldName An eventually hierarchical fieldName (containing ';') while null, "null" and "this" and "." result in returning the selfreference this. ".." result in returning getParentDataModel()
* @return The first child which is found
* @throws Exception If the specified name is not one of the children or subchildren
*/
public synchronized DataModel getChild(String fieldName)
throws Exception {
if (fieldName == null || fieldName.equals(FIELD_NAME_THIS_1) || fieldName.equals(FIELD_NAME_THIS_2) || fieldName.equals(FIELD_NAME_THIS_3))
{
return this;
}
if (fieldName.equals(FIELD_NAME_PARENT_MODEL)) {
return getParentDataModel();
}
if (fieldName.equals(FIELD_NAME_INSTANCE_NAME)) {
return new StringModel(getInstanceName());
}
if (fieldName.equals(FIELD_NAME_CAPTION)) {
return new StringModel(getCaption());
}
if (fieldName.indexOf(FIELD_NAME_SEPARATOR_CHAR) >= 0) {
return getChild(getFieldNameArray(fieldName));
}
DataModelEnumeration e = getEnumeration();
while (e.hasMore()) {
DataModel child = e.next();
if (fieldName.equalsIgnoreCase(child.getFieldName())) {
return child;
}
}
throw new Exception("No field called '" + fieldName + "' in " + getFieldName());
}
/**
* Convenience method to access a child of type SimpleModel and call getText on it
*
* @return null if child is not there or if it is not of type SimpleModel
*/
public String getChildText(String childName) {
if (FIELD_NAME_INSTANCE_NAME.equals(childName)) {
return getInstanceName();
}
if (FIELD_NAME_CAPTION.equals(childName)) {
return getCaption();
}
try {
return ((SimpleModel) getChild(childName)).getText();
} catch (Exception ex) {
FWLog.debug("Cannot access SimpleModel child:" + childName, ex);
}
return null;
}
/**
* Convenience method to access a child of type SimpleModel and call setText on it
*
* @param childName The name of the child
* @param text The text to be assigned to the child or null to just access the SimpleModel
* @return null if child is not there or if it is not of type SimpleModel
*/
public SimpleModel setChildText(String childName, String text) {
try {
SimpleModel child = (SimpleModel) getChild(childName);
if (text != null) {
child.setText(text);
}
return child;
} catch (Exception ex) {
FWLog.debug("Cannot access (write) SimpleModel child:" + childName, ex);
}
return null;
}
/**
* Make an array of strings for a hierarchical field name (containing ';')
*
* @return An array of one or more entries
*/
public static String[] getFieldNameArray(String fieldName) {
StringVector sv = new StringVector(fieldName, FIELD_NAME_SEPARATOR);
return sv.getArray();
}
/**
* Get a child or subchild via a field name array
*
* @param fieldName an array of one or more segments of a hierarchical field name
* @return The appropriate child or subchild
* @throws Exception If the child or subchild was not found
*/
public DataModel getChild(String fieldName[])
throws Exception {
return getChild(fieldName, 0);
}
/**
* Method for recursive seek for subchildren
*
* @param fieldName an array of one or more segments of a hierarchical field name
* @param level The current level of child hierarchie
* @throws Exception If subchild was not found
*/
private DataModel getChild(String fieldName[], int level)
throws Exception {
DataModel child = getChild(fieldName[level]);
++level;
if (level == fieldName.length) {
return child;
}
return child.getChild(fieldName, level);
}
/**
* Get subchild or DataModelCommand by name
*
* @param fieldName The hierarchical field name plus eventually the method name
* @return The subchild or the method
* @throws Exception If no subchild or method with that name was found
*/
public Viewable getViewableChild(String fieldName)
throws Exception {
if (fieldName == null) {
return this;
}
if (fieldName.indexOf(FIELD_NAME_SEPARATOR_CHAR) >= 0) {
return getViewableChild(getFieldNameArray(fieldName));
}
try {
return getChild(fieldName);
} catch (Exception ex) {
}
try {
return getMethod(fieldName);
} catch (Exception ex) {
}
throw new Exception("No viewable called '" + fieldName + "' in " + getFieldName());
}
/**
* Get subchild or DataModelCommand by name
*
* @param fieldName The hierarchical field name plus eventually the method name
* @return The subchild or the method
* @throws Exception If no subchild or method with that name was found
*/
public Viewable getViewableChild(String fieldName[])
throws Exception {
return getViewableChild(fieldName, 0);
}
/**
* Get subchild or DataModelCommand by name
*
* @param fieldName The hierarchical field name plus eventually the method name
* @param level The child level on which the recursion is actually seeking
* @return The subchild or the method
* @throws Exception If no subchild or method with that name was found
*/
private Viewable getViewableChild(String fieldName[], int level)
throws Exception {
Viewable child = getViewableChild(fieldName[level]);
++level;
if (level == fieldName.length) {
return child;
}
return ((DataModel) child).getViewableChild(fieldName, level);
}
/**
* The programmer can freely use this name for his own purpose
*
* @param instanceName The name which is assigend to the DataModel
*/
public void setInstanceName(String instanceName) {
if (this.instanceName == null && instanceName == null) {
return;
}
if (this.instanceName != null) {
if (this.instanceName.equals(instanceName)) {
return;
}
}
this.instanceName = instanceName;
fireEditEvent(this, DataModelEvent.INSTANCE_NAME_CHANGED);
}
/**
* Get the instance name (default null)
*/
public String getInstanceName() {
return this.instanceName;
}
/**
* Attach a validation rule to the model. The validation is only performed after finishEdit if
* allowInvalid is false
*
* @param validator The new validation rule
*/
public void setValidator(Validator validator) {
this.validator = validator;
}
/**
* Add a new listener to this model
*/
public synchronized void addDataModelListener(DataModelListener l) {
this.listenerList = EventMulticaster.addEventListener(this.listenerList, l);
}
/**
* Add a new listener to this model (the listener is held as a SoftReference)
*
* @param l the given parameter will be held as a SoftReference
*/
public synchronized void addSoftDataModelListener(DataModelListener l) {
this.listenerList = EventMulticaster.addEventListener(this.listenerList, new SoftReference(l));
}
/**
* Remove a listener (If the listener is not part of the listener list then nothing happens).
*/
public synchronized void removeDataModelListener(DataModelListener l) {
this.listenerList = EventMulticaster.removeEventListener(this.listenerList, l);
}
/**
* Set the new status (list of status flags)
*
* @param s The new status (not null)
*/
public void setStatus(ModelStatus s) {
if (status != null && !status.equals(s)) {
status.assign(s);
if (!isEditing()) {
fireValueChange();
}
}
}
/**
* Clear all flags on this model
*/
public void clearStatus() {
setStatus(new ModelStatus());
}
/**
* Clear all flags on this model and on all children
*/
public void clearStatusRecursive() {
clearStatus();
if (hasChildren()) {
for (DataModel i = getChildren().firstItem(); i != null; i = getChildren().nextItem()) {
i.clearStatusRecursive();
}
}
}
/**
* Get the actual status (never null)
*/
public ModelStatus getStatus() {
return this.status;
}
/**
* Set a status flag (on or off) for this DataModel.
* If the flag value changes, then a value change event (subtype DataModelEvent.SUBTYPE_FLAG_CHANGED) is fired.
*
* @return true if flags boolean value has changed
*/
public synchronized boolean setFlag(ModelFlag flag, boolean on) {
if (getStatus() != null) {
if (getStatus().setFlag(flag, on)) {
if (!isEditing()) {
fireValueChange(DataModelEvent.SUBTYPE_FLAG_CHANGED, flag.getName());
}
return true;
}
}
return false;
}
/**
* Sets a flag on all children recusively
*/
public void setFlagRecursive(ModelFlag flag, boolean on) {
setFlag(flag, on);
if (hasChildren()) {
for (DataModel i = getChildren().firstItem(); i != null; i = getChildren().nextItem()) {
i.setFlagRecursive(flag, on);
}
}
}
/**
* Set the flag recursively for all parents to the same state (only if flag exists).
*
* @param flag The flag which is propagated to the parents
*/
public void propagateFlag(ModelFlag flag) {
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null && hasFlag(flag)) {
parentDataModel.setFlag(flag, getFlag(flag));
parentDataModel.propagateFlag(flag);
}
}
/**
* Returns the state of the flag (if the flag does not exist it returns false)
*
* @param flag The flag which is checked
*/
public boolean getFlag(ModelFlag flag) {
if (getStatus() == null) {
return false;
}
return getStatus().getFlag(flag);
}
/**
* Check wheter the given flag exists for this model
*/
public boolean hasFlag(ModelFlag flag) {
if (getStatus() == null) {
return false;
}
return getStatus().hasFlag(flag);
}
/**
* Check wheter this model is member of a container
*/
public boolean hasParent() {
return this.parent != null && this.parent.get() != null;
}
/**
* Check wheter this model has children (only containers can have children)
*/
public boolean hasChildren() {
return false;
}
/**
* Recurse the parents and add their tree hooks to the given tree path
*
* @param tree_path This path gets filled recursively with all parents in reverse order
*/
public void fillTreePath(SortedVector tree_path) {
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.fillTreePath(tree_path);
}
tree_path.addElement(this.getTreeHook());
}
/**
* Recursively calculate the nesting level of a child witin its parent containers
*
* @return 0 if their is no parent model else a positive number
*/
public int getLevel() {
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
return 1 + parentDataModel.getLevel();
}
return 0;
}
/**
* If this function returns true then the container editor has the ability
* to edit the direct children.
*
* @return true if the associated editor enables child editing
*/
public boolean isChildEditor() {
return false;
}
/**
* Return the child vector (do not manipulate this list outside of the DataModel)
*
* @return A vector instance or null (if the model is no container)
*/
public DataModelVector getChildren() {
return null;
}
/**
* Get all children recusively in one list.
*
* @return a DataModelVector (eventually empty but not null)
*/
public DataModelVector getChildrenRecursive() {
DataModelVector ret = new DataModelVector();
getChildrenRecursive(ret);
return ret;
}
/**
* Get all children recusively in one list.
*/
private void getChildrenRecursive(DataModelVector v) {
if (hasChildren()) {
for (DataModel i = getChildren().firstItem(); i != null; i = getChildren().nextItem()) {
v.append(i);
i.getChildrenRecursive(v);
}
}
}
/**
* Create a string representation of this data model. Mainly used for debugging purposes.
*/
public String toString() {
return this.value == null ? "null" : this.value.toString();
}
/**
* Fire value change events to all registered listeners. (Only if fireEvents is true).
* Value change events are translated into child value change events and fired by the
* parent data models which receive these value change events.
*
* @param source Where the event was created
*/
public void fireValueChange(DataModel source) {
fireValueChange(source, null);
}
/**
* Fire value change events to all registered listeners. (Only if fireEvents is true).
* Value change events are translated into child value change events and fired by the
* parent data models which receive these value change events.
*
* @param source where the event was created
*/
public void fireValueChange(DataModel source, DataModelEvent event) {
if (!getFireEvents()) {
return;
}
if (event == null) {
event = new DataModelEvent(source, DataModelEvent.VALUE_CHANGE);
}
setDirtyFlag();
event.setTransmitter(this);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.fireChildValueChange(this, event);
}
}
/**
* Fire child value change events to all registered listeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers.
*
* @param source Where the event was created
*/
public void fireChildValueChange(DataModel source, DataModelEvent event) {
if (!getFireEvents()) {
return;
}
if (event == null) {
event = new DataModelEvent(source, DataModelEvent.CHILD_VALUE_CHANGE, true);
} else {
event = event.duplicate();
event.setId(DataModelEvent.CHILD_VALUE_CHANGE);
}
event.setTransmitter(this);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.propagateChildEvent(event);
}
}
/**
* Fire structure change events to all registered listeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers. Structure change events from
* DataModelArrays are translated into value change events!
*
* @param source Where the event was created
*/
public void fireStructureChange(DataModel source) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(source, DataModelEvent.STRUCTURE_CHANGE);
event.setTransmitter(this);
fireEvent(event);
}
/**
* Fire edit events to all registered DataModelEditListeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers.
*
* @param source Where the event was created
*/
public void fireEditEvent(DataModel source, int eventType) {
fireEditEvent(source, eventType, null);
}
/**
* Fire edit events to all registered DataModelEditListeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers.
*
* @param source Where the event was created
* @param viewer The viewer Object which originated the event (may be null)
*/
public void fireEditEvent(DataModel source, int eventType, Object viewer) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(source, eventType);
event.setTransmitter(this);
event.setViewer(viewer);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.propagateChildEvent(event);
}
}
/**
* Use this method to fire an EditEvent of type CALL_METHOD. Check with DataModelEvent.isMethodCall(methodName)
*
* @param methodName this information is stored in DataModelEvent.getData()
*/
public void fireCallMethod(String methodName) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(this, DataModelEvent.CALL_METHOD);
event.setTransmitter(this);
event.setData(methodName);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.propagateChildEvent(event);
}
}
/**
* Fire grab focus events to all registered listeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers which translate them into fire
* grab child focus events.
*
* @param source Where the event was created
*/
public void fireGrabFocus(DataModel source) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(source, DataModelEvent.GRAB_FOCUS);
event.setTransmitter(this);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.fireGrabChildFocus(this);
}
}
/**
* Fire grab child focus events to all registered listeners. (Only if fireEvents is true).
* These events propagate to all parent data model containers.
*
* @param source Where the event was created
*/
public void fireGrabChildFocus(DataModel source) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(source, DataModelEvent.GRAB_CHILD_FOCUS, true);
event.setTransmitter(this);
fireEvent(event);
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.propagateChildEvent(event);
}
}
/**
* Fire any DataModelEvent. (Only if fireEvents is true).
*
* @param event The event which will be fired.
*/
public void fireEvent(DataModelEvent event) {
if (!getFireEvents()) {
return;
}
Object listeners[] = EventMulticaster.getArray(listenerList);
boolean needGarbageCollect = false;
if (listeners != null) {
for (int i = 0; i < listeners.length; ++i) {
if (listeners[i] == null) {
needGarbageCollect = true;
} else {
event.sendTo((DataModelListener) listeners[i]);
}
}
}
if (needGarbageCollect) {
listenerList = EventMulticaster.garbageCollect(listenerList);
}
}
/**
* Duplicate and extend the event and fire it to the listeners. (Only if fireEvents is true).
* Recursively calls propagateChildEvent on the parent DataModelContainer (if not null)
*
* @param event The event which gets fired.
*/
public void propagateChildEvent(DataModelEvent event) {
if (!getFireEvents()) {
return;
}
boolean needGarbageCollect = false;
Object listeners[] = EventMulticaster.getArray(listenerList);
event = event.duplicate();
event.setTransmitter(this);
if (listeners != null) {
for (int i = 0; i < listeners.length; ++i) {
if (listeners[i] == null) {
needGarbageCollect = true;
} else {
event.sendTo((DataModelListener) listeners[i]);
}
}
}
if (needGarbageCollect) {
listenerList = EventMulticaster.garbageCollect(listenerList);
}
DataModel parentDataModel = getParentDataModel();
if (parentDataModel != null) {
parentDataModel.propagateChildEvent(event);
}
}
/**
* Fire child value change event (source = this)
*/
public void fireChildValueChange() {
if (!getFireEvents()) {
return;
}
fireChildValueChange(this, null);
}
/**
* Fire value change event (source = this)
*/
public void fireValueChange() {
fireValueChange(this, null);
}
/**
* Fire value change event (source = this)
*/
public void fireValueChange(int subtype, String data) {
if (!getFireEvents()) {
return;
}
DataModelEvent event = new DataModelEvent(this, DataModelEvent.VALUE_CHANGE, subtype, -1, null);
event.setData(data);
fireValueChange(this, event);
}
/**
* Fire value change event (source = this)
*/
public void fireValueChange(int subtype) {
if (!getFireEvents()) {
return;
}
fireValueChange(this, new DataModelEvent(this, DataModelEvent.VALUE_CHANGE, subtype, -1, null));
}
/**
* Fire edit value event (source = this)
*/
public void fireValueEdit() {
if (!getFireEvents()) {
return;
}
fireEditEvent(this, DataModelEvent.VALUE_EDIT);
}
/**
* Fire structure change event (source = this)
*/
public void fireStructureChange() {
if (!getFireEvents()) {
return;
}
fireStructureChange(this);
}
/**
* Fire grab focus event (source = this)
*/
public void grabFocus() {
fireGrabFocus(this);
}
/**
* Fire grab child focus event (source = this)
*/
public void fireGrabChildFocus() {
fireGrabChildFocus(this);
}
/**
* Compare own value to a given object
*
* @param other The value wich is used for comparison (usually from DataModel.getValue())
* @return true if values are equal
*/
public boolean sameValue(Object other) {
throw new RuntimeException("sameValue() not implemented for: " + getClass().getName());
}
// Sorter interface
/**
* @return if o1>o2 -> -1 if o1<o2 -> 1 if o1==o2 -> 0
*/
public int compare(Object o1, Object o2) {
return -1;
}
// Editable interface
/**
* Return whether or not finish edit can complete successfully with an invalid value
*
* @return boolean
*/
public boolean allowInvalid() {
return true;
}
/**
* Terminate edit mode and restore old value to current value
*/
public void cancelEdit() {
cancelEdit(null);
}
/**
* Terminate edit mode and restore old value to current value
*
* @param viewer The viewer Object which called finishEdit (or null)
*/
public void cancelEdit(Object viewer) {
this.value = this.oldValue;
this.oldValue = null;
setStatus(oldStatus);
this.oldStatus = null;
fireEditEvent(this, DataModelEvent.CANCEL_EDIT, viewer);
}
/**
* If value is valid then exit editing status, else throw an exception
*/
public synchronized void finishEdit() {
finishEdit(null);
}
/**
* If value is valid then exit editing status, else throw an exception
*
* @param viewer The viewer Object which called finishEdit (or null)
*/
public synchronized void finishEdit(Object viewer) {
if (!isEditing()) {
return;
}
if (!allowInvalid()) {
if (!isValid()) {
setFlag(ModelFlag.ERROR, true);
throw new InvalidValueException("Invalid value after editing", getValue(), this);
}
}
Object v = value;
value = oldValue;
oldValue = null;
assignValue(v);
ModelStatus st = status;
status = oldStatus;
setStatus(st);
if (getFlag(ModelFlag.ERROR)) {
setFlag(ModelFlag.ERROR, false);
}
oldStatus = null;
fireEditEvent(this, DataModelEvent.FINISH_EDIT, viewer);
}
/**
* Return the value before editing status was entered
*/
public Object getOldValue() {
return this.oldValue;
}
/**
* Return the current value
*
* @return java.lang.Object
*/
public Object getValue() {
return this.value;
}
/**
* Return true if in editing status
*
* @return boolean
*/
public boolean isEditing() {
return this.oldValue != null;
}
/**
* This method was created in VisualAge
*
* @return boolean
*/
public boolean isValid() {
if (this.validator != null) {
return this.validator.isValid(this);
}
return true;
}
/**
* Set the current value of the object
*/
public void setValue(Object newvalue) {
if (newvalue == null) {
throw new IllegalArgumentException("value may not be null");
}
if (!value.equals(newvalue)) {
value = newvalue;
if (!isEditing()) {
fireValueChange();
} else {
fireValueEdit();
}
}
}
/**
* This method has to be redifined for classes which distinguish between
* assignment of a new value and replacement of intance
*/
public void assignValue(Object newvalue) {
setValue(newvalue);
}
/**
* only used by duplicate
*
* @param orig
* @param copyPairs
*/
protected void assignModel(DataModel orig, CopyPairs copyPairs) {
assignValue(orig.cloneValue(copyPairs));
setStatus(orig.getStatus());
}
public void assignDuplicate(DataModel duplicate, CopyPairs copyPairs) {
setValue(duplicate.getValue());
setStatus(duplicate.getStatus());
}
/**
* Place the editable object in editing 'status'
* and copy the current value to oldValue
*/
public void startEdit() {
startEdit(null);
}
/**
* Place the editable object in editing 'status'
* and copy the current value to oldValue
*
* @param viewer The viewer which calls the startEdit (or null)
*/
public void startEdit(Object viewer) {
if (isEditing()) {
return;
}
Object o = cloneValue(new CopyPairs());
oldValue = value;
value = o;
oldStatus = status;
status = new ModelStatus(oldStatus);
fireEditEvent(this, DataModelEvent.START_EDIT, viewer);
}
/**
* create an exact copy instance of the actual value
*/
public abstract Object cloneValue(CopyPairs copyPairs);
/**
* create an exact copy instance of this (the duplicate() method has to be invoked
* for the parent class with a non null instance)
*/
public DataModel duplicate(DataModel instance, CopyPairs copyPairs) {
if (instance == null) {
throw new IllegalArgumentException(getClass().getName() + " duplicate instance may not be null");
}
copyPairs.put(this, instance);
//instance.this.parent = this.parent;
//instance.instanceName = this.instanceName;
instance.fieldName = this.fieldName;
instance.value = cloneValue(copyPairs);
instance.setStatus(status);
//instance.appendMethods(getMethods());
return instance;
}
/**
* Check whether any method has been registered for this model (appendMethod)
*/
public boolean hasMethods() {
return methods != null && methods.size() > 0;
}
/**
* Append a new method to this model.
*
* @param method The command which is appended (it will automatically get this model as the new target)
*/
public void appendMethod(DataModelCommand method) {
if (methods == null) {
methods = new DataModelCommandVector();
}
methods.append(method);
method.setDataModel(this);
method.addPropertyChangeListener(new MethodPropertyChangeListener());
}
/**
* Append a list of new methods to this model.
*
* @param methods The list of data model commands which is appended (they will automatically get this model as the new target)
*/
public void appendMethods(DataModelCommandVector methods) {
if (methods != null) {
for (DataModelCommand m = methods.firstItem(); m != null; m = methods.nextItem()) {
appendMethod(m.duplicate());
}
}
}
/**
* Get a list of all registered methods for this data model
*
* @return A DataModelCommandVector or null (no methods)
*/
public DataModelCommandVector getMethods() {
return methods;
}
/**
* Get a registered method by name.
*
* @param name The command name (getCommandName) of the method
* @throws Exception If the method is not part of this model
*/
public DataModelCommand getMethod(String name)
throws Exception {
if (name.indexOf(FIELD_NAME_SEPARATOR_CHAR) >= 0) {
return getMethod(getFieldNameArray(name));
}
if (methods != null) {
// look for the method name
for (DataModelCommand m = methods.firstItem(); m != null; m = methods.nextItem()) {
if (m.getCommandName().equals(name)) {
return m;
}
}
}
throw new Exception("Method " + name + " not found in " + getFieldName());
}
/**
* Enable or disable a method
*
* @param name The command name of the method
* @param on Enable when true else disable
*/
public void setEnableMethod(String name, boolean on)
throws Exception {
getMethod(name).setEnabled(on);
}
/**
* Get a registered method by name.
*
* @param name The hierarical field name plus the command name (getCommandName) of the method
* @throws Exception If the method is not part of this model
*/
public DataModelCommand getMethod(String name[])
throws Exception {
return getMethod(name, 0);
}
/**
* Get a registered method by name.
*
* @param name The hierarical field name plus the command name (getCommandName) of the method
* @param level The actual level of recursion
* @throws Exception If the method is not part of this model
*/
private DataModelCommand getMethod(String name[], int level)
throws Exception {
if (name.length == 1) {
return getMethod(name[0]);
}
String fieldName[] = new String[name.length - 1];
for (int i = 0; i < fieldName.length; ++i) {
fieldName[i] = name[i];
}
return getChild(fieldName).getMethod(name[fieldName.length]);
}
// DataModelTreeNodeAttributes interface
/**
* The label which is displayed in the tree.
*/
public String getLabel() {
return getFieldName();
}
/**
* Additional information about a DataModel
*/
public String getInfo() {
return "INFO";
}
/**
* @return A tree path to the parent root
*/
public TreePath getTreePath() {
SortedVector tree_path = new SortedVector();
fillTreePath(tree_path);
return new TreePath(tree_path.getObjectArray());
}
/**
* @return The number of columns to be displayed
*/
public int getColumns() {
return 0;
}
/**
* @return The label at the specified column index
*/
public String getLabel(int index) {
return null;
}
/**
* @return The overall background color id
*/
public String getBackgroundColorId() {
return null;
}
/**
* Is this model EMPTY or ERROR.
*
* @return true if the status is in an exceptional state (error or empty)
*/
public boolean isExceptional() {
return getStatus() != null ? getStatus().isExceptional() : false;
}
/**
* Is this model EMPTY.
*
* @return true if the status is empty
*/
public boolean isEmpty() {
return getStatus() != null ? getStatus().isEmpty() : false;
}
/**
* Is this model ERROR.
*
* @return true if the status is error
*/
public boolean isError() {
return getStatus() != null ? getStatus().isError() : false;
}
public void setGray(boolean on) {
setFlag(ModelFlag.GRAY, on);
}
/**
* Is this model GRAY.
*
* @return true if the status is error
*/
public boolean isGray() {
return getStatus() != null ? getStatus().isGray() : false;
}
public void beep() {
Toolkit.getDefaultToolkit().beep();
fireEditEvent(this, DataModelEvent.FORMAT_ERROR);
}
/**
* Is this model TRANSIENT
*
* @return true if the status is transient
*/
public boolean isTransient() {
return getStatus() != null ? getStatus().isTransient() : false;
}
public void setTransient(boolean on) {
setFlag(ModelFlag.TRANSIENT, on);
}
/**
* @return The column specific background color id
*/
public String getBackgroundColorId(int index) {
return null;
}
/**
* @return the instance specific icon id which is displayed in the tree viewer
*/
public String getIconId() {
if (getStatus() != null) {
return getStatus().getIconId(this);
}
return null;
}
// end DataModelTreeNodeAttributes interface
class MethodPropertyChangeListener implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent ev) {
if (Command.ENABLED.equals(ev.getPropertyName())) {
boolean on = ((Boolean) ev.getNewValue()).booleanValue();
Command command = (Command) ev.getSource();
if (on != ((Boolean) ev.getOldValue()).booleanValue()) {
int type;
if (on) {
type = DataModelEvent.ENABLE_METHOD;
} else {
type = DataModelEvent.DISABLE_METHOD;
}
DataModelEvent event = new DataModelEvent(DataModel.this, type, true);
event.setTransmitter(DataModel.this);
event.setData(command.getCommandName());
fireEvent(event);
}
}
}
}
// Externalizable implementation
public void readExternal(ObjectInput in)
throws IOException {
try {
byte b[] = (byte[]) in.readObject();
dropData(new String(b));
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
public void writeExternal(ObjectOutput out)
throws IOException {
try {
String s = dragData();
out.writeObject(s.getBytes());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Access the child name of a model relative to its parent (this).
* Walks up the parents of the
* given child model until the actual root model (or null) is reached.
*
* @param childModel
*/
public String getChildName(DataModel childModel) {
StringBuffer stringBuffer = new StringBuffer();
StringVector fieldNames = new StringVector();
while (childModel != null) {
if (childModel == this) {
break;
}
fieldNames.add(0, childModel.getFieldName());
childModel = childModel.getParentDataModel();
}
Enumeration e = fieldNames.elements();
while (e.hasMoreElements()) {
stringBuffer.append((String) e.nextElement());
if (e.hasMoreElements()) {
stringBuffer.append(DataModel.FIELD_NAME_SEPARATOR_CHAR);
}
}
String ret = stringBuffer.toString();
if (ret.length() == 0) {
ret = "this";
}
return ret;
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
try {
out.writeUTF(dragData());
} catch (Exception e) {
out.writeUTF("");
}
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
try {
dropData(in.readUTF());
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
public boolean hasChild(String fieldName) {
boolean ret = false;
DataModelEnumeration en = getEnumeration();
while(en.hasMore()) {
String fieldNamecurrent = en.next().getFieldName();
if(fieldNamecurrent.equalsIgnoreCase(fieldName)) {
return true;
}
}
return ret;
}
//todo check if sp call ok
public Object getStoredProcedureParameterValue(String parameterName, JnexPreparedStatement.STOREDPROCEDURETYPE callType) {
try {
if(hasChild(parameterName)) {
return getChild(parameterName).getValue();
} else {
if(this.getClass() == DataModel.class) {
return null;
} else {
DataModelEnumeration en = getEnumeration();
while(en.hasMore()) {
DataModel dm = en.next();
Object ret = dm.getStoredProcedureParameterValue(parameterName, callType);
if(ret != null) {
return ret;
}
}
return null;
}
}
} catch (Exception e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return null;
}
}