Package com.nexirius.framework.datamodel

Source Code of com.nexirius.framework.datamodel.DataModel$MethodPropertyChangeListener

//{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;
    }
}
TOP

Related Classes of com.nexirius.framework.datamodel.DataModel$MethodPropertyChangeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.