Package com.nexirius.framework.datamodel

Source Code of com.nexirius.framework.datamodel.DataModelContainer

//{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.application.ErrorMessageException;
import com.nexirius.util.CopyPairs;
import com.nexirius.util.Sorter;
import com.nexirius.util.TextToken;
import com.nexirius.util.assertion.Assert;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PushbackInputStream;

/**
* Superclass of all DataModel classes which contain children DataModel items.
*
* @author Marcel Baumann
*/
public abstract class DataModelContainer extends DataModel
{
    public static final String TYPE = "TYPE";
    public static final char OPENB = '{';
    public static final char CLOSEB = '}';

    DataModelEditListener myDataModelEditListener = null;

    /**
     * Create a new empty DataModelContainer
     */
    public DataModelContainer() {
        this(new DataModelVector());
    }

    /**
     * Create a new DataModelContainer with an initial list of children
     *
     * @param v The vector which is used as the value of the new container
     */
    public DataModelContainer(DataModelVector v) {
        super(v);
    }

    /**
     * Try to create a new member by class name. The member is no appended but only returned
     *
     * @param className The full class name which is needed to instanciate a new child instanc
     * @param fieldName The field name of the new child
     * @throws Exception If the class name cannot be instanciated
     */
    public DataModel createMember(String className, String fieldName)
            throws Exception {
        DataModel member = (DataModel) Class.forName(className).newInstance();

        member.setFieldName(fieldName);

        return member;
    }

    /**
     * Create and return a new iterator
     *
     * @return Always returns a valid iterator instance (eventually empty)
     */
    public DataModelEnumeration getEnumeration() {
        if (hasChildren()) {
            DataModelVector v = getChildren();

            synchronized (v) {
                return new DataModelEnumeration(v.size(), v.iterator());
            }
        }

        return new DataModelEnumeration();
    }

    /**
     * Reads the persistent data from an input stream
     *
     * @param in The input where the data is read from
     * @throws Exception When the input stream is not readable or corrupt
     */
    public synchronized void readDataFrom(PushbackInputStream in)
            throws Exception {
        super.readDataFrom(in);

        TextToken token = TextToken.nextToken(in);

        if (!token.isChar(OPENB)) {
            throw new Exception("DataModelContainer is not initialized with:'" + OPENB + "' have:" + token);
        }

        while (true) {
            token = TextToken.nextToken(in);

            if (token.isChar(CLOSEB)) {
                break;
            } else if (token.isString()) {
                String name = token.getString();

                try {
                    DataModel m = getChild(name);

                    m.readDataFrom(in);
                } catch (IOException ioex) {
                    throw ioex;
                } catch (Exception ex) {
                    ex.printStackTrace();
                    ignoreChild(name, in);
                }

            } else {
                throw new Exception("Expecting " + CLOSEB + " or String literal but have:" + token);
            }
        }
    }

    /**
     * Writes the whole container recursively to the output stream (persistence).
     * Children which are transient (TRANSIENT flag) are skipped (so are their sub-children).
     * ArrayModels ignore the TRANSIENT flags on their children.
     *
     * @param out The output stream
     * @throws Exception If the stream is not writeable
     */
    public synchronized void writeDataTo(OutputStream out)
            throws Exception {
        super.writeDataTo(out);

        DataModelEnumeration e = getEnumeration();

        out.write('{');
        out.write('\n');

        while (e.hasMore()) {
            DataModel child = e.next();

            if (!child.isTransient()) {
                TextToken name = new TextToken(child.getFieldName());

                name.writeTo(out);
                out.write(' ');
                child.writeDataTo(out);
                out.write('\n');
            }
        }

        out.write('}');
        out.write('\n');
    }

    public void ignoreChild(String name, PushbackInputStream in)
            throws Exception {
        TextToken token;
        int nest = 0;

        FWLog.debug("Ignoring child " + name);

        while (true) {
            token = TextToken.nextToken(in);

            if (token == null) {

                break;
            }

            if (token.isChar(OPENB)) {
                ++nest;
            } else if (token.isChar(CLOSEB)) {
                --nest;
            }

            if (nest == 0) {
                break;
            }
        }
    }

    /**
     * Reads the persistent data from an input stream and creates new members
     *
     * @param in The input where the data is read from
     * @throws Exception When the input stream is not readable or corrupt
     */
    public synchronized void readInitDataFrom(PushbackInputStream in)
            throws Exception {
        TextToken token = TextToken.nextToken(in);

        removeChildren();

        if (!token.isChar(OPENB)) {
            throw new Exception("DataModelContainer is not initialized with:'" + OPENB + "' have:" + token);
        }

        while (true) {
            token = TextToken.nextToken(in);

            if (token.isIdentifier(TYPE)) {
                token = TextToken.nextToken(in);
                String type = token.getString();
                token = TextToken.nextToken(in);
                String name = token.getString();
                DataModel m = createMember(type, name);

                if (m instanceof DataModelContainer) {
                    ((DataModelContainer) m).readInitDataFrom(in);
                } else {
                    m.readDataFrom(in);
                }

                append(m);
            } else if (token.isChar(CLOSEB)) {
                break;
            } else {
                throw new Exception("Expecting " + TYPE + " or " + CLOSEB + " but have:" + token);
            }
        }
    }

    /**
     * Create a persistent text representation of this instance (used to initialize)
     *
     * @param out The stream to which the data is written
     * @throws Exception If the stream can not be accessed (write access)
     */
    public void writeInitDataTo(OutputStream out)
            throws Exception {
        TextToken type = new TextToken(TYPE, TextToken.IDENTIFIER);
        DataModelEnumeration e = getEnumeration();

        out.write('{');
        out.write('\n');

        while (e.hasMore()) {
            DataModel child = e.next();
            TextToken classname = new TextToken(child.getClass().getName());
            TextToken name = new TextToken(child.getFieldName());

            type.writeTo(out);
            out.write(' ');
            classname.writeTo(out);
            out.write(' ');
            name.writeTo(out);
            out.write(' ');

            if (child instanceof DataModelContainer) {
                ((DataModelContainer) child).writeInitDataTo(out);
            } else {
                child.writeDataTo(out);
            }

            out.write('\n');
        }

        out.write('}');
        out.write('\n');
    }

    /**
     * Get a tree hook instance which helps to display the hierarchie in a swing tree.
     *
     * @return a new (only on the first call) ContainerTreeHook instance (never null)
     */
    public DataModelTreeNode getTreeHook() {
        if (treeHook == null) {
            treeHook = new ContainerTreeHook(this);
        }

        return treeHook;
    }

    /**
     * A method which is not defined by the container but which is very useful for some user defined subclassed.
     * It is called every time a DataModel change event is fired by one of the containers children.
     */
    public void childValueChanged(DataModel child) {
        //FWLog.debug("CHILD CHANGED parent = " + getFieldName() + " child = " + (child == null ? "null" : child.getFieldName()));
    }

    /**
     * DataModel listener interface. (Do not redefine unless you call the parent method from the rdefined function)
     */
    public void dataModelChangeValue(DataModelEvent event) {
/*
    if (event.fromChild()) {
      propagateChildEvent(event);
      childValueChanged(event.getTransmitter());
    } else {
      fireChildValueChange(event.getDataModel());
      childValueChanged(event.getDataModel());
    }
*/
    }

    /**
     * DataModel listener interface. (Do not redefine unless you call the parent method from the rdefined function)
     */
    public void dataModelChangeStructure(DataModelEvent event) {
        propagateChildEvent(event);
    }

    /**
     * DataModel listener interface. (Do not redefine unless you call the parent method from the rdefined function)
     */
    public void dataModelEdit(DataModelEvent event) {
//    propagateChildEvent(event);
    }

    /**
     * DataModel listener interface. (Do not redefine unless you call the parent method from the rdefined function)
     */
    public void dataModelGrabFocus(DataModelEvent event) {
/*
    //System.out.println("dataModelGrabFocus on " + getFieldName() + " f child:" + event.fromChild());
    if (event.fromChild()) {
      propagateChildEvent(event);
    } else {
      fireGrabChildFocus(event.getDataModel());
    }
*/
    }

    protected void renumber(int from, int to) {
        // empty
    }

    /**
     * Accept a new DataModelVector as the new value.
     */
    public synchronized void setValue(Object newValue) {
        setFireEvents(false);

        DataModelEnumeration e = getEnumeration();

        int index = 0;

        while (e.hasMore()) {
            looseItem(e.next(), index++);
        }

        super.setValue(newValue);

        e = getEnumeration();

        index = 0;

        while (e.hasMore()) {
            gainItem(e.next(), index++);
        }

        setFireEvents(true);

        fireStructureChange();
    }

    /**
     * Accept a new child (define its new parent) and add myself to its listener list.
     */
    private void gainItem(DataModel item, int index) {
        if (item == null) {
            return;
        }

        item.setParentDataModelContainer(this);

        if (this instanceof ArrayModel) {
            fireValueChange(this, new DataModelEvent(this, DataModelEvent.VALUE_CHANGE, DataModelEvent.SUBTYPE_ITEM_ADDED, index, item));
        } else {
            fireStructureChange();
        }
    }

    /**
     * Release a child (undefine its parent) and remove myself fromits listener list.
     */
    private void looseItem(DataModel item, int index) {
        item.setParentDataModelContainer(null);

        if (this instanceof ArrayModel) {
            fireValueChange(this, new DataModelEvent(this, DataModelEvent.VALUE_CHANGE, DataModelEvent.SUBTYPE_ITEM_REMOVED, index, item));
        } else {
            fireStructureChange();
        }
    }

    /**
     * Append a new child (to the end of the list) and set its parent data model.
     *
     * @param item The new child which is added
     */
    public synchronized void append(DataModel item) {
        insertItemAt(item, getChildren().size());
    }

    /**
     * Insert a new child at a sorted position (Only works if the Sorter interface is defined properly)
     *
     * @param item The new child
     * @return the index of the new child
     */
    public synchronized int sortInsert(DataModel item) {
        return sortInsert(item, item);
    }

    public synchronized int sortInsert(DataModel item, Sorter sorter) {
        if (item == null) {
            return -1;
        }
        int index = getChildren().sortInsert(item, sorter);

        gainItem(item, index);
        renumber(index, getChildren().size() - 1);

        return index;
    }

    /**
     * Sort members (Only works if the Sorter interface is defined properly)
     */
    public synchronized void sort() {
        if (getChildren().size() < 2) {

            return;
        }

        DataModel item = getChildren().firstItem();

        if (getChildren().sort(item)) {

            renumber(0, getChildren().size() - 1);
            fireStructureChange();
        }
    }

    public synchronized void sort(Sorter sorter) {
        if (getChildren().size() < 2) {

            return;
        }

        getChildren().sort(sorter);

        renumber(0, getChildren().size() - 1);
        fireStructureChange();
    }

    /**
     * Inserts a child at a specified position
     *
     * @param item  The new child
     * @param index The index where it is inserted
     */
    public synchronized void insertItemAt(DataModel item, int index) {
        if (item == null) {
            return;
        }
        getChildren().insertElementAt(item, index);
        gainItem(item, index);
        renumber(index, getChildren().size() - 1);
    }

    /**
     * Remove a child from this container and reset its parent and listener list.
     *
     * @param child The child which looses its parent
     */
    public synchronized boolean removeItem(DataModel child) {
        if (hasChildren()) {
            int index = getChildren().indexOf(child);

            return null != removeItemAt(index);
        }
       
        return false;
    }

    /**
     * Remove a child at a specified position
     *
     * @param index The index where the child will be removed
     */
    public synchronized DataModel removeItemAt(int index) {
        if (index >= 0 && hasChildren()) {
            DataModel item = getChildren().getItem(index);

            getChildren().removeElementAt(index);
            looseItem(item, index);
            renumber(index, getChildren().size() - 1);

            return item;
        }

        return null;
    }

    /**
     * Get rid of all the children at once. This method does not reset the childrens parents and listener lists.
     * On ArrayModel this method throws a DataModelEvent.VALUE_CHANGE, DataModelEvent.SUBTYPE_CLEAR
     */
    public void removeChildren() {
        // FIX this function does not reset the childrens parent field and listener list
        getChildren().removeAllElements();

        if (this instanceof ArrayModel) {
            fireValueChange(this, new DataModelEvent(this, DataModelEvent.VALUE_CHANGE, DataModelEvent.SUBTYPE_CLEAR, -1, null));
            ((ArrayModel) this).setHighlightedItem(-1);
        } else {
            fireStructureChange();
        }
    }

    /**
     * Does this container have one or more children
     */
    public boolean hasChildren() {
        return getChildren().size() > 0;
    }

    /**
     * Fire a grab focus from a specified child (at index)
     *
     * @param index The index position of the child which will fire the grab focus event
     */
    public void grabFocus(int index) {
        DataModelVector v = getChildren();
        DataModel child = null;

        if (v != null && v.size() > index) {
            child = v.getItem(index);
        }

        if (child != null) {
            child.grabFocus();
        }
    }

    /**
     * Compares two containers recursively
     */
    public synchronized boolean equals(Object other) {
        if (other == this) {
            return true;
        }

        return false;
    }

    /**
     * Compare two field names
     *
     * @param m1 The first DataModel from which the fieldname is taken
     * @param m2 The second DataModel from which the fieldname is taken
     */
    public static boolean sameFieldName(DataModel m1, DataModel m2) {
        String s1 = m1.getFieldName();
        String s2 = m2.getFieldName();

        if (s1 == s2) {
            return true;
        }

        if (s1 != null) {
            return s1.equals(s2);
        }

        return false;
    }

    /**
     * Assign a new value to this container. If the structures of both containers are
     * the same (same children with same names) then the target structure is not changed
     * only the values which are not equal are overwritten. When the source container has additional
     * elements the these elements are duplicated and added to the target container. When the source
     * container has less elements than the target container then the children, which are not
     * in both containers are removed from the target container.
     *
     * @param newvalue The value of the source container (target.assignValue(source.getValue())
     */
    public synchronized void assignValue(Object newvalue) {
        Assert.assertion(newvalue != null, "newvalue may not be null");
        Assert.assertion(newvalue instanceof DataModelVector, "Can't assign " + newvalue.getClass().getName() + " to a DataModelContainer");

        if (value == null) {
            value = newvalue;

            return;
        }

        if (!value.equals(newvalue)) {
            DataModelVector nv = new DataModelVector((DataModelVector) newvalue);
            DataModelVector ov = new DataModelVector(getChildren());
            DataModel nm = nv.firstItem();
            DataModel om = ov.firstItem();
            boolean needFire = false;
            int index = 0;

            while (nm != null && om != null) {
                if (sameFieldName(om, nm)) {
                    if (!om.equals(nm)) {
                        om.assignValue(nm.getValue());
                        om.setStatus(nm.getStatus());
                    }
                } else {
                    needFire = true;
                    setFireEvents(false);
                    try {
                        setChild(index, nm);
                    } catch (Exception ex) {
                        // this exception should never be fired
                        ex.printStackTrace();
                    }
                    setFireEvents(true);
                }

                ++index;
                nm = nv.nextItem();
                om = ov.nextItem();
            }

            // if this is an array we have to add new intances to
            // the existing array if needed or delete instances if needed

            if (om != null || nm != null) {
                needFire = true;
                setFireEvents(false);

                while (om != null) {
                    getChildren().removeItem(om);
                    om = ov.nextItem();
                }

                while (nm != null) {
                    getChildren().append(nm);
                    nm = nv.nextItem();
                }

                setFireEvents(true);
            }

            if (needFire) {
                fireStructureChange();
            }

            fireValueChange();

        } else {
            // values are equal
        }
    }

    public synchronized void assignDuplicate(DataModel duplicate, CopyPairs copyPairs) {
        DataModelEnumeration en = getEnumeration();

        while (en.hasMore()) {
            DataModel child = en.next();
            DataModel other = (DataModel) copyPairs.getCopy(child);

            if (other == null) {
                System.out.println("copyPairs = " + copyPairs);
                throw new ErrorMessageException("No duplicate value of child " + child.getFieldName(), null, null);
            }

            child.assignDuplicate(other, copyPairs);
        }

        setStatus(duplicate.getStatus());
    }

    /**
     * Replace an existing child with a new instance
     *
     * @param fieldName The name of the existing child
     * @param newChild  The replacing new child
     * @throws Exception If the child was not found
     */
    public void setChild(String fieldName, DataModel newChild)
            throws Exception {
        setChild(getChild(fieldName), newChild);
    }

    /**
     * Override an existing child by a new instance
     *
     * @param oldChild The reference of the existing child
     * @param newChild The replacing new child
     * @throws Exception If the child was not found
     */
    public synchronized void setChild(DataModel oldChild, DataModel newChild)
            throws Exception {
        if (oldChild == null) {
            append(newChild);
        } else {
            if (oldChild != newChild) {
                setChild(getChildIndex(oldChild), newChild);
            }
        }
    }

    /**
     * Override an existing child by a new instance
     *
     * @param index    The position of the existing child
     * @param newChild The replacing new child
     * @throws Exception If there is no child at the specified index
     */
    public synchronized void setChild(int index, DataModel newChild)
            throws Exception {
        if (newChild == null) {
            return;
        }

        DataModel oldChild = (DataModel) getChildren().set(index, newChild);

        looseItem(oldChild, index);
        gainItem(newChild, index);
        renumber(index, index);
    }

    /**
     * Recursively creates a string representation (not portable) for debugging
     */
    public String toString() {
        StringBuffer b = new StringBuffer();
        StringBuffer offset = new StringBuffer();
        int level = getLevel();

        while (level-- > 0) {
            offset.append("\t");
        }

        b.append(offset + getTreeHook().getLabel());

        if (hasChildren()) {
            b.append(" {\n");

            for (DataModel i = getChildren().firstItem(); i != null; i = getChildren().nextItem()) {
                b.append(i);
                b.append("\n");
            }

            b.append(offset + "}");
        }

        b.append("\n");

        return b.toString();
    }

    /**
     * Get a list reference to the actual children (Do not change this list from outside)
     */
    public DataModelVector getChildren() {
        return (DataModelVector) getValue();
    }

    /**
     * Create a deep copy of the children list
     *
     * @param copyPairs A list which avoids creating two copies of the same instance (not null)
     */
    public Object cloneValue(CopyPairs copyPairs) {
        return getChildren().duplicate(copyPairs);
    }

    /**
     * Sets the parents of all children to this
     */
    public synchronized void reassignParent() {
        DataModelEnumeration e = getEnumeration();

        while (e.hasMore()) {
            DataModel m = e.next();

            m.setParentDataModelContainer(this);
//            m.addDataModelListener(getDataModelEditListener());
        }
    }

//    public DataModelEditListener getDataModelEditListener() {
//        return new MyDataModelEditListener();
//    }
//
//    protected DataModelEditListener getChildDataModelEditListener() {
//        if (myDataModelEditListener == null) {
//            myDataModelEditListener = new MyDataModelEditListener();
//        }
//
//        return myDataModelEditListener;
//    }
//
//    class MyDataModelEditListener implements DataModelEditListener {
//        public void dataModelChangeValue(DataModelEvent event) {
//            DataModelContainer.this.dataModelChangeValue(event);
//        }
//
//        public void dataModelChangeStructure(DataModelEvent event) {
//            DataModelContainer.this.dataModelChangeStructure(event);
//        }
//
//        public void dataModelGrabFocus(DataModelEvent event) {
//            DataModelContainer.this.dataModelGrabFocus(event);
//        }
//
//        public void dataModelEdit(DataModelEvent event) {
//            DataModelContainer.this.dataModelEdit(event);
//        }
//    }
}
TOP

Related Classes of com.nexirius.framework.datamodel.DataModelContainer

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.