Package com.nexirius.framework.datamodel

Source Code of com.nexirius.framework.datamodel.ArrayModel$AttributeSorter

//{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.datamodel.parser.DataModelClasses;
import com.nexirius.util.CopyPairs;
import com.nexirius.util.Sorter;
import com.nexirius.util.StringVector;
import com.nexirius.util.TextToken;
import com.nexirius.util.assertion.Assert;
import com.nexirius.util.textresource.TextResource;
import com.nexirius.util.textresource.TextResourceEvent;
import com.nexirius.util.textresource.TextResourceListener;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.util.Hashtable;

/**
* This class represents an array of DataModel items. The items which are appended
* to this container should all be from the same data type. The field name information
* is used to create and identify the columns which are displayed in a table.
*
* @author Marcel Baumann
*/
public class ArrayModel extends DataModelContainer {
    FieldName memberFieldNames;
    FieldNameVector fields;
    MyTableModel tableModel;
    DataModelClasses classes = null;
    String memberType = null;
    int highlightedItem = -1;
    DataModelVector deleted = null;
    private DataModel template;

    /**
     * Create a new ArrayModel which contains the specified member field names (used for table display).
     * Normally an array is created using the getFullFieldName() method on a DataModel which represents
     * the typical future member of the newly created array instance (member type).
     * <pre>
     * class AddressArrayModel extends ArrayModel
     * {
     *   public AddressArrayModel()
     *   {
     *     super(new AddressModel().getFullFieldName());
     *   }
     * }
     * </pre>
     *
     * @param memberFieldNames used to identify column titles in a table
     */
    public ArrayModel(FieldName memberFieldNames) {
        setMemberFieldNames(memberFieldNames);
    }

    /**
     * Create a new ArrayModel which contains the specified member field names (used for table display).
     * Normally an array is created using the getFullFieldName() method on a DataModel which represents
     * the typical future member of the newly created array instance (member type).
     * <pre>
     * class AddressArrayModel extends ArrayModel
     * {
     *   public AddressArrayModel()
     *   {
     *     super(new AddressModel().getFullFieldName(), "AddressArray");
     *   }
     * }
     * </pre>
     *
     * @param memberFieldNames The field names of the typical member class (used for column names of default ArrayViewer)
     * @param fieldName        the field name of the array
     */
    public ArrayModel(FieldName memberFieldNames, String fieldName) {
        setMemberFieldNames(memberFieldNames);
        setFieldName(fieldName);
    }

    /**
     * Create a new ArrayModel which contains the specified template field names (used for table display).
     * The array is created using the getFullFieldName() method on the DataModel which represents
     * the typical future template of the newly created array instance (member type).
     *
     * @param template  constructor calls getFullFieldName() on this instance
     * @param fieldName the field name of the array
     */
    public ArrayModel(DataModel template, String fieldName) {
        setMemberFieldNames(template.getFullFieldName());
        setFieldName(fieldName);
        this.template = template;
    }

    /**
     * creates a new instance of an empty array model.
     *
     * @param c the list of all available types
     * @param t the type of the array members as string
     */
    public ArrayModel(DataModelClasses c, String t) {
        classes = c;
        memberType = t;

        try {
            setMemberFieldNames(classes.newInstance(memberType, null).getFullFieldName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public DataModel getTemplate() {
        return template;
    }

    public synchronized void assignDuplicate(DataModel duplicate, CopyPairs copyPairs) {
        DataModelEnumeration enDuplicate = duplicate.getEnumeration();
        DataModelEnumeration enOrig = getEnumeration();
        CopyPairs cp = null;
        boolean valueChanged = false;
        CopyPairs origChildren = null;

        deleted = null;

        if (((ArrayModel) duplicate).getSize() != getSize()) {
            valueChanged = true;
        }

        DataModelVector newChildren = new DataModelVector();

        while (enDuplicate.hasMore()) {
            DataModel duplicateChild = enDuplicate.next();
            DataModel origChild = (DataModel) copyPairs.getOrig(duplicateChild);

            if (origChild == null) {
                valueChanged = true;
                if (cp == null) {
                    cp = new CopyPairs();
                }

                newChildren.append(duplicateChild.duplicate(null, cp));
            } else {
                if (origChildren == null) {
                    origChildren = new CopyPairs();
                }
                origChild.assignDuplicate(duplicateChild, copyPairs);
                origChildren.put(origChild, origChild);
                origChild.setParentDataModelContainer(null);
                newChildren.append(origChild);
            }

            if (!valueChanged) {
                // check, whether the order has changed
                DataModel origChildAtPos = enOrig.next();

                if (origChildAtPos != origChild) {
                    valueChanged = true;
                }
            }
        }

        if (valueChanged) {
            // store the deleted instances
            DataModelEnumeration en = getEnumeration();
            while (en.hasMore()) {
                DataModel model = en.next();

                if (origChildren == null || origChildren.getCopy(model) == null) {
                    if (deleted == null) {
                        deleted = new DataModelVector();
                    }
                    deleted.append(model);
                }
            }

            value = newChildren;
            reassignParent();
            fireValueChange();
//            clear();
//            DataModelEnumeration en2 = newChildren.getEnumeration();
//
//            while (en2.hasMore()) {
//                DataModel item = en2.next();
//                item.setParentDataModelContainer(null);
//                append(item);
//            }
        } else {
            reassignParent();
        }

        highlightedItem = ((ArrayModel) duplicate).highlightedItem;
    }

    /**
     * Access the instances which have been removed apon last call to assignDuplicate (e.g. when OK was pressed in a dialog window).
     *
     * @return all deleted instances
     */
    public DataModelVector getDeleted() {
        return deleted;
    }

    /*
     * removes all array elements
     */
    public void clear() {
        setHighlightedItem(-1);

        removeChildren();
    }

    public String getCaption() {
        return TextToken.quoteString(getFieldName());
    }

    public DataModel createDefaultChild() {
        if (memberType == null) {

            return null;
        }

        try {
            return classes.newInstance(memberType, null);
        } catch (Exception ex) {
            ex.printStackTrace();

            return null;
        }
    }

    public DataModel insertDefaultChild(int index) {
        DataModel ret = createDefaultChild();

        if (ret != null) {
            insertItemAt(ret, index);
        }

        return ret;
    }

    public void setHighlightedItem(int index) {
        if (index != highlightedItem) {
            highlightedItem = index;
            fireHighlightChange();
        }
    }

    public void setHighlightedItem(DataModel child) {
        int index = getChildren().indexOf(child);
        if (index != highlightedItem) {
            highlightedItem = index;
            fireHighlightChange();
        }
    }

    public void fireHighlightChange() {
        fireEditEvent(this, DataModelEvent.HIGHLIGHT_CHANGED, null);
    }

    public DataModel getHighlightedItem() {
        if (highlightedItem >= 0) {
            return getDataModel(highlightedItem);
        }

        return null;
    }

    public int getHighlightedItemIndex() {
        return highlightedItem;
    }

    public DataModel removeHighlightedItem() {
        if (highlightedItem >= 0) {
            int hi = highlightedItem;

            setHighlightedItem(-1);

            return removeItemAt(hi);
        }

        return null;
    }

    /**
     * Set the fieldnames to represent array index
     */
    protected synchronized void renumber(int from, int to) {
        if (from == to) {
            getChildren().getItem(from).setFieldName(from);
        } else {
            DataModelEnumeration e = getEnumeration();

            for (int index = 0; e.hasMore() && index <= to; ++index) {
                DataModel child = e.next();

                if (index >= from) {
                    child.setFieldName(index);
                }
            }
        }
    }

    /**
     * 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 {
        TextToken token = TextToken.nextToken(in);

        setHighlightedItem(-1);

        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);

                m.readDataFrom(in);

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

    /**
     * Introduce the fieldnames of a typical member
     *
     * @param memberFieldNames used to identify column titles in a table
     */
    public void setMemberFieldNames(FieldName memberFieldNames) {
        this.memberFieldNames = memberFieldNames;
        this.fields = new FieldNameVector();

        if (this.memberFieldNames != null) {
            this.memberFieldNames.getFieldNameVector(this.fields);
        }
    }

    /**
     * Get the registered member field names
     */
    public FieldName getMemberFieldNames() {
        return this.memberFieldNames;
    }

    /**
     * Create a persistent text representation of this instance
     *
     * @param out The stream to which the data is written
     * @throws Exception If the stream can not be accessed (write access)
     */
    public void writeDataTo(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(' ');

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

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

    /**
     * Get the current number of elements in the array
     *
     * @return 0 or bigger
     */
    public int getSize() {
        return getChildren().size();
    }

    /**
     * The fieldnames of array members are not used for identification. Each array member
     * is identified by its position index (0 or bigger) within the array.
     *
     * @param fieldName The index of a child as string (e.g. "4" for the 5th member)
     * @throws Exception If the specified field was not found
     */
    public synchronized DataModel getChild(String fieldName)
            throws Exception {
        if (fieldName.indexOf(FIELD_NAME_SEPARATOR_CHAR) >= 0) {

            return super.getChild(getFieldNameArray(fieldName));
        }

        int index = 0;

        try {
            index = Integer.parseInt(fieldName);
        } catch (Exception ex) {
            throw new Exception("ArrayModel fields are accessed with names which are integer numbers but have: '" + fieldName + "' in " + getFieldName());
        }

        return getDataModel(index);
    }

    /**
     * Get member by index
     *
     * @param i The index between 0 and size()-1
     * @return The Model at the specified position (or an array index exception is thrown)
     */
    public DataModel getDataModel(int i) {
        if (i < 0 || i >= getSize()) {
            return null;
        }
        return getChildren().getItem(i);
    }

    /**
     * The array is an exception to the rule that all fieldnames are recursively returned
     *
     * @return the direct field name of the array (not members are analysed)
     */
    public FieldName getFullFieldName() {
        Assert.pre(this.fieldName != null, "this.fieldName is not null");

        return this.fieldName;
    }

    /**
     * Get a swing table model which represents the data in this array
     *
     * @param text_source The text source is needed to translate the column names into the appropriate table column title string
     */
    public TableModel getTableModel(TextResource text_source) {
        if (this.tableModel == null) {
            this.tableModel = new MyTableModel(text_source);
//            text_source.addTextResourceListener(this.tableModel);
        }

        return this.tableModel;
    }

    /**
     * Child value change events are used to update the information in the table model
     *
     * @param child The child which has changed
     */
    public void childValueChanged(DataModel child) {
        if (this.tableModel != null) {
            try {
                this.tableModel.rowChanged(getChildIndex(child));
            } catch (Exception ex) {
// ignore
            }
        }
    }

    /**
     * Create a deep copy of this array instance.
     *
     * @param instance  null (a new instance is created) or the subclass copy instance
     * @param copyPairs null (copyPairs is generated internally) or the copy pairs instace of the subclass
     */
    public DataModel duplicate(DataModel instance, CopyPairs copyPairs) {
        if (instance == null) {
            try {
                instance = (DataModel) getClass().getConstructor(null).newInstance(null);
            } catch (Exception ex) {
                System.err.println("Need public default constructor (with no arguments) in " + getClass().getName());
                if (template != null) {
                    instance = new ArrayModel(memberFieldNames);
                } else {
                    instance = new ArrayModel(template, getFieldName());
                }
            }
        }

        if (copyPairs == null) {
            copyPairs = new CopyPairs();
        }

        ArrayModel ret = (ArrayModel) super.duplicate(instance, copyPairs);

        ret.highlightedItem = highlightedItem;

        ret.reassignParent();

        return ret;
    }

    /**
     * only used by duplicate
     *
     * @param orig
     * @param copyPairs
     */
    protected void assignModel(DataModel orig, CopyPairs copyPairs) {
        DataModelEnumeration en = orig.getEnumeration();

        if (getSize() > 0) {
            removeChildren();
        }

        while (en.hasMore()) {
            DataModel origChild = en.next();

            try {
                DataModel newChild = origChild.duplicate(null, copyPairs);
                append(newChild);
                copyPairs.put(origChild, newChild);
            } catch (Exception e) {
                e.printStackTrace()//TODO
            }
        }

        setStatus(orig.getStatus());
    }

    /**
     * Structure changed events are translated into value changed events (when the structure of an array
     * changes it usually has changed its number of members)
     */
    public void fireStructureChange() {
        fireValueChange();

        if (this.tableModel != null) {
            this.tableModel.reload();
        }
    }

    /**
     * Access a table containig all entries by instance name
     */
    public Hashtable getInstanceNameTable() {
        Hashtable ret = new Hashtable(getSize());
        DataModelEnumeration en = getEnumeration();

        while (en.hasMore()) {
            DataModel model = en.next();

            ret.put(model.getInstanceName(), model);
        }

        return ret;
    }

    /**
     * Access a child by instance name
     * @return null or the first model which holds the given instance name
     */
    public DataModel getChildByInstanceName(String instanceName) {
        DataModelEnumeration en = getEnumeration();

        while (en.hasMore()) {
            DataModel model = en.next();

            if (instanceName.equals(model.getInstanceName())) {
                return model;
            }
        }

        return null;
    }

    /**
     * Set the given flag on each data model depending on the return value of the given filter
     *
     * @param flag
     * @param filter
     */
    public void filter(ModelFlag flag, DataModelFilter filter) {
        DataModelEnumeration e = getEnumeration();

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

            model.setFlag(flag, filter.filter(model));
        }
    }

    public void sortByAttribute(String childName) {
        Sorter sorter = new AttributeSorter(childName);

        sort(sorter);
    }

    public void sortInsertByAttribute(DataModel model, String childName) {
        Sorter sorter = new AttributeSorter(childName);

        sortInsert(model, sorter);
    }

    public void sortByAttributeDesc(String childName) {
        Sorter sorter = new AttributeSorter(childName, true);

        sort(sorter);
    }

    public static class AttributeSorter implements Sorter {
        String childName;
        boolean desc = false;

        public AttributeSorter(String childName) {
            this.childName = childName;
        }

        public AttributeSorter(String childName, boolean desc) {
            this.childName = childName;
            this.desc = desc;
        }

        public int compare(Object model1, Object model2) {
            try {
                int ret = 0;
                DataModel m1 = ((DataModel) model1).getChild(childName);
                DataModel m2 = ((DataModel) model2).getChild(childName);

                if (m1 instanceof SimpleModel) {
                    if (((SimpleModel) m1).getSimpleType().wrap() instanceof Comparable) {
                        Comparable c1 = (Comparable) ((SimpleModel) m1).getSimpleType().wrap();
                        Comparable c2 = (Comparable) ((SimpleModel) m2).getSimpleType().wrap();

                        if (c1 instanceof String) {
                            ret = ((String) c2).compareToIgnoreCase((String) c1);
                        } else {
                            ret = c2.compareTo(c1);
                        }
                    } else {
                        String s1 = ((SimpleModel) m1).getSimpleType().wrap().toString();
                        String s2 = ((SimpleModel) m2).getSimpleType().wrap().toString();

                        ret = s2.compareToIgnoreCase(s1);
                    }
                } else {
                    ret = m1.compare(m1, m2);
                }

                if (desc) {
                    return -ret;
                } else {
                    return ret;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return 0;
        }
    }

    /**
     * internally used class to communicate with swing tables
     */
    class MyTableModel extends AbstractTableModel implements TextResourceListener {
        TextResource m_text_source;

        MyTableModel(TextResource text_source) {
            m_text_source = text_source;
        }

        public void textResourceChanged(TextResourceEvent event) {
            if (event.getType() == TextResourceEvent.LOCALE_CHANGED) {
                fireTableStructureChanged();
            }
        }

        public int getColumnCount() {
            return fields.size();
        }

        public int getRowCount() {
            return getChildren().size();
        }

        public Object getValueAt(int y, int x) {
            try {
                DataModel model = getChildren().getItem(y);

                if (x < 0) {
                    return model;
                }

                FieldName fieldName = fields.getFieldName(x);
                StringVector sv = new StringVector();

                while (fieldName != null) {
                    sv.insertElementAt(fieldName.toString(), 0);
                    fieldName = fieldName.getParent();
                }

                sv.removeElementAt(0);

                String names[] = sv.getArray();

                return model.getChild(names);
            } catch (Exception ex) {
                ex.printStackTrace();

                return "XXX";
            }
        }

        public String getColumnName(int x) {
            String ret = fields.getFieldName(x).toString();

            try {
                String s = m_text_source.getText(ret);

                if (s != null) {
                    ret = s;
                }
            } catch (Exception ex) {

            }

            if (ret == null) {
                return "xxx";
            }

            return ret;
        }

        public void rowChanged(int row) {
            fireTableRowsUpdated(row, row);
        }

        public void reload() {
            fireTableDataChanged();
        }

        /**
         * remove item and put it to the deleted list
         *
         * @param child
         * @return true if item was found and added to the deleted item list
         */
        public boolean deleteItem(DataModel child) {
            if (child == null) {
                return false;
            }

            if (highlightedItem >= 0 && getHighlightedItem() == child) {
                setHighlightedItem(-1);
            }

            if (removeItem(child)) {
                if (deleted == null) {
                    deleted = new DataModelVector();
                }
                deleted.append(child);

                return true;
            }

            return false;
        }

        class EventSender implements Runnable {
            TableModelEvent event;

            EventSender(TableModelEvent event) {
                event = this.event;
            }

            public void run() {
                immediatelyFireTableChanged(event);
            }
        }

        public void fireTableChanged(TableModelEvent e) {
            SwingUtilities.invokeLater(new EventSender(e));
        }

        public void immediatelyFireTableChanged(TableModelEvent e) {
            super.fireTableChanged(e);
        }
    }
}
TOP

Related Classes of com.nexirius.framework.datamodel.ArrayModel$AttributeSorter

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.