alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.netbeans.modules.properties;


import java.io.Serializable;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.JTable;

import org.openide.util.NbBundle;


/** 
 * Model for the properties edit table. First column represents keys containing all .properties
 * files belonging to bundle represnted by this model. Each next columns represents values
 * for on .properties file from that bundle.
 *
 * @author Petr Jiricka
 * @see BundleEditPanel
 * @see javax.swing.table.AbstractTableModel
 */
public class PropertiesTableModel extends AbstractTableModel {

    /** Generated serialized version UID. */
    static final long serialVersionUID = -7882925922830244768L;

    /** PropertiesDataObject this table presents. */
    private BundleStructure structure;

    /** Listens to changes on the bundle structure. */
    private PropertyBundleListener bundleListener;

    
    /** Create a data node for a given data object.
     * The provided children object will be used to hold all child nodes.
     * @param structure model to work with
     */
    public PropertiesTableModel(BundleStructure structure) {
        super();
        this.structure = structure;

        // listener for the BundleStructure
        bundleListener = new TablePropertyBundleListener();
        structure.addPropertyBundleListener(new WeakListenerPropertyBundle(bundleListener));
    }
    

    /** Gets the class for a model. Overrides column class. */
    public Class getColumnClass(int columnIndex) {
        return StringPair.class;
    }

    /** Gets the number of rows in the model. Implements superclass abstract method. */
    public int getRowCount() {
        return structure.getKeyCount();
    }

    /** Gets the number of columns in the model. Implements superclass abstract method. */
    public int getColumnCount() {
        return structure.getEntryCount() + 1;
    }

    /** Gets the value for the given row and column. Implements superclass abstract method. */
    public Object getValueAt(int row, int column) {
        BundleStructure bs = structure;
        
        if(column == 0)
            // Get StringPair for key.
            return stringPairForKey(row);//bs.keyAt(row);
        else {
            // Get StringPair for value.
            Element.ItemElem item;
            try {
                item = bs.getItem(column - 1, bs.keyAt(row));
            } catch (ArrayIndexOutOfBoundsException aie) {
                item = null;
            }
            return stringPairForValue(item);
        }
    }

    /** Gets string pair for a key in an item (may be null). */
    private StringPair stringPairForKey(int row) {
        BundleStructure bs = structure;
        Element.ItemElem item = bs.getItem(0, bs.keyAt(row));
        StringPair sp;
        if (item == null)
            sp = new StringPair("", bs.keyAt(row), true); // NOI18N
        else
            sp = new StringPair(item.getComment(), bs.keyAt(row), true);
        
        if (structure.getEntryCount() > 1)
            sp.setCommentEditable(false);
        
        return sp;
    }

    /** Gets string pair for a value in an item (may be null). */
    private StringPair stringPairForValue(Element.ItemElem item) {
        if (item == null)
            // item doesnt't exist -> value is null
            return new StringPair(null, null);
        else
            return new StringPair(item.getComment(), item.getValue());
    }

    /** Gets name for column. Overrides superclass method.
     * @param column model index of column
     * @return name for column */
    public String getColumnName(int column) {
        String leading;
        
        // Construct label.
        if(column == structure.getSortIndex())
            // Place for drawing ascending/descending mark in renderer.
            leading = "     "; // NOI18N
        else
            leading = " "; // NOI18N
        
        if(column == 0)
            return leading+NbBundle.getBundle(PropertiesTableModel.class).getString("LAB_KeyColumnLabel");
        else {
            if(structure.getEntryCount() == 1)
                return leading+NbBundle.getBundle(PropertiesTableModel.class).getString("LBL_ColumnValue");
            else {
                PropertiesFileEntry entry = structure.getNthEntry(column - 1);
                return entry == null ? "" : leading+Util.getLocaleLabel(entry); // NOI18N
            }
        }
    }

    /** Sets the value at rowIndex and columnIndex. Overrides superclass method. */
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        // If values equals -> no change was made -> return immediatelly.
        if(aValue.equals(getValueAt(rowIndex, columnIndex)))
            return;
        
        // PENDING - set comment for all files
        // Is key.
        if (columnIndex == 0) {
            BundleStructure bs = structure;
            String oldValue = (String)bs.keyAt(rowIndex);
            if (oldValue == null)
                return;
            String newValue = ((StringPair)aValue).getValue();
            if (newValue == null) { // Key can be an empty string
                // Remove from all files.
                return;
            } else {
                // Set in all files
                for (int i=0; i < structure.getEntryCount(); i++) {
                    PropertiesFileEntry entry = structure.getNthEntry(i);
                    if (entry != null) {
                        PropertiesStructure ps = entry.getHandler().getStructure();
                        if (ps != null) {
                            // set the key
                            if (!oldValue.equals(newValue)) {
                                ps.renameItem(oldValue, newValue);
                                // this resorting is necessary only if this column index is same as
                                // column according the sort is performed, REFINE
                                structure.sort(-1);
                            }
                            // set the comment
                            if (i == 0) {
                                Element.ItemElem item = ps.getItem(newValue);
                                if (item != null && ((StringPair)aValue).isCommentEditable()) {
                                    // only set if they differ
                                    if (!item.getComment().equals(((StringPair)aValue).getComment()))
                                        item.setComment(((StringPair)aValue).getComment());
                                }
                            }
                        }
                    }
                }
            }
        } else {
            // Property value.
            PropertiesFileEntry entry = structure.getNthEntry(columnIndex - 1);
            String key = structure.keyAt(rowIndex);
            if (entry != null && key != null) {
                PropertiesStructure ps = entry.getHandler().getStructure();
                if (ps != null) {
                    Element.ItemElem item = ps.getItem(key);
                    if (item != null) {
                        item.setValue(((StringPair)aValue).getValue());
                        item.setComment(((StringPair)aValue).getComment());
                        // this resorting is necessary only if this column index is same as
                        // column according the sort is performed, REFINE
                        structure.sort(-1);
                    } else {
                        if ((((StringPair)aValue).getValue().length() > 0) || (((StringPair)aValue).getComment().length() > 0))  {
                            ps.addItem(key, ((StringPair)aValue).getValue(), ((StringPair)aValue).getComment());
                            // this resorting is necessary only if this column index is same as
                            // column according the sort is performed, REFINE
                            structure.sort(-1);
                        }
                    }
                }
            }
        }
    }

    /** Overrides superclass method. Overrides superclass method.
     * @return true for all cells */
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
    }

    /** Fires a TableModelEvent - change of one column */
    public void fireTableColumnChanged(int index) {
        int columnModelIndex = index;
        
        // reset the header value as well
        Object list[] = listenerList.getListenerList();
        for (int i = 0; i < list.length; i++) {
            if (list[i] instanceof JTable) {
                JTable jt = (JTable)list[i];
                try {
                    TableColumn column = jt.getColumnModel().getColumn(index);
                    columnModelIndex = column.getModelIndex();
                    column.setHeaderValue(jt.getModel().getColumnName(columnModelIndex));
                } catch (ArrayIndexOutOfBoundsException abe) {
                    // only catch exception
                }
                jt.getTableHeader().repaint();
            }
        }
        fireTableChanged(new TableModelEvent(this, 0, getRowCount() - 1, columnModelIndex));
    }

    /** Overrides superclass method. */
    public String toString() {
        StringBuffer result = new StringBuffer();
        result.append("------------------------------ TABLE MODEL DUMP -----------------------\n"); // NOI18N
        for (int row = 0; row < getRowCount(); row ++) {
            for (int column = 0; column < getColumnCount(); column ++) {
                StringPair sp = (StringPair)getValueAt(row, column);
                result.append("[" /*+ sp.getComment() + "," */+ sp.getValue() + "]"); // NOI18N
                if (column == 0)
                    result.append(" : "); // NOI18N
                else
                    if (column == getColumnCount() - 1)
                        result.append("\n"); // NOI18N
                    else
                        result.append(","); // NOI18N
            }
        }
        result.append("---------------------------- END TABLE MODEL DUMP ---------------------\n"); // NOI18N
        return result.toString();
    }

    /** Cancels editing in all listening JTables if appropriate. */
    private void cancelEditingInTables(CancelSelector can) {
        
        Object list[] = listenerList.getListenerList();
        for(int i = 0; i < list.length; i++) {
            if(list[i] instanceof BundleEditPanel.BundleTable) {
                BundleEditPanel.BundleTable jt = (BundleEditPanel.BundleTable)list[i];
                if (can.doCancelEditing(jt.getEditingRow(), jt.getEditingColumn())) {                    
                    jt.removeEditorSilent();
                }
            }
        }
    }

    /** Gets CancelSelector for this table. */
    private CancelSelector getDefaultCancelSelector() {
        return new CancelSelector() {
                   /** Returns whether editing should be canceled for given row and column. */
                   public boolean doCancelEditing(int row, int column) {
                       return (row >= 0 && row < getRowCount() && column >= 0 && column < getColumnCount());
                   }
               };
    }
    

    /** Interface which finds out whether editing should be canceled if given cell is edited. */
    private static interface CancelSelector {
        /** Returns whether editing should be canceled for given row and column. */
        public boolean doCancelEditing(int row, int column);
    } // End of interface CancelSelector.

    
    /** Inner class. Listener for changes on bundle structure. */
    private class TablePropertyBundleListener implements PropertyBundleListener {
        public void bundleChanged(final PropertyBundleEvent evt) {
            // quick patch for bug #13026
            // (ensure visual updates are performed in AWT thread)
            if (java.awt.EventQueue.isDispatchThread()) {
                doBundleChanged(evt);
            }
            else {
                java.awt.EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        doBundleChanged(evt);
                    }
                });
            }
        }

        private void doBundleChanged(PropertyBundleEvent evt) {
            int changeType = evt.getChangeType();
            
            if(changeType == PropertyBundleEvent.CHANGE_STRUCT) {
                // Structure changed.
                
                // Note: Normal way would be use the next commented out rows, which should do in effect
                // the same thing like reseting the model, but it doesn't, therefore we reset the model directly.
                
                //cancelEditingInTables(getDefaultCancelSelector());
                //fireTableStructureChanged(); 

                Object[] list = PropertiesTableModel.super.listenerList.getListenerList();
                for(int i = 0; i < list.length; i++) {
                    if(list[i] instanceof JTable) {
                        //!!! strange comment this model should be only stateless proxy
                        // Its necessary to create new instance of model otherwise the 'old' model values would remain.
                        ((JTable)list[i]).setModel(new PropertiesTableModel(PropertiesTableModel.this.structure));
                    }
                }
            } else if(changeType == PropertyBundleEvent.CHANGE_ALL) {
                // All items changed (keyset).                
                cancelEditingInTables(getDefaultCancelSelector());
                
                // reset all header values as well
                Object[] list = PropertiesTableModel.super.listenerList.getListenerList();
                for (int i = 0; i < list.length; i++) {
                    if (list[i] instanceof JTable) {
                        JTable jt = (JTable)list[i];

                        for (int j=0 ; j < jt.getColumnModel().getColumnCount(); j++) {
                            TableColumn column = jt.getColumnModel().getColumn(j);
                            column.setHeaderValue(jt.getModel().getColumnName(column.getModelIndex()));
                        }
                    }
                }
                
                fireTableDataChanged();
            } else if(changeType == PropertyBundleEvent.CHANGE_FILE) {
                // File changed.
                final int index = structure.getEntryIndexByFileName(evt.getEntryName());
                if (index == -1) {
                    if (Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
                        (new Exception("Changed file not found")).printStackTrace(); // NOI18N
                    return;
                }
                
                cancelEditingInTables(new CancelSelector() {
                    public boolean doCancelEditing(int row, int column) {
                        if (!(row >= 0 && row < getRowCount() && column >= 0 && column < getColumnCount()))
                            return false;
                        return (column == index + 1);
                    }
                });
                
                fireTableColumnChanged(index + 1);
            } else if(changeType == PropertyBundleEvent.CHANGE_ITEM) {
                // one item changed
                final int index2 = structure.getEntryIndexByFileName(evt.getEntryName());
                final int keyIndex = structure.getKeyIndexByName(evt.getItemName());
                
                if(index2 == -1 || keyIndex == -1) {
                    if(Boolean.getBoolean("netbeans.debug.exceptions")) // NOI18N
                        (new Exception("Changed file not found")).printStackTrace(); // NOI18N
                    
                    return;
                }
                
                cancelEditingInTables(new CancelSelector() {
                    public boolean doCancelEditing(int row, int column) {
                        if (!(row >= 0 && row < getRowCount() && column >= 0 && column < getColumnCount()))
                            return false;
                        return (column == index2 + 1 && row == keyIndex);
                    }
                });
                
                fireTableCellUpdated(keyIndex, index2 + 1);
            }
        }
    }  // End of inner class TablePropertyBundleListener.
    
    
    /** 
     * Object for the value for one cell in a table view.
     * It is used to represent either (comment, value) pair of an item, or a key for an item.
     */
    static class StringPair implements Serializable {

        /** Holds comment for this instance. */
        private String comment;
        
        /** Key or value string depending on the keyType. */
        private String value;
        
        /** Type of instance. */
        private boolean keyType;
        
        /** Flag if comment is editable for this instance. */
        private boolean commentEditable;

        /** Generated serial version UID. */
        static final long serialVersionUID =-463968846283787181L;
        
        
        /** Constructs with empty comment and value. */
        public StringPair() {
            this (null, "", false); // NOI18N
        }

        /** Constructs with the given value and no comment. */
        public StringPair(String v) {
            this (null, v, true);
        }

        /** Constructs with the given comment and value. */
        public StringPair(String c, String v) {
            this (c, v, false);
        }

        /** Constructs with the given comment and value. */
        public StringPair(String c, String v, boolean kt) {
            comment = c;
            value   = v;
            keyType = kt;
            commentEditable = true;
        }

        
        /** @return comment associated with this element. */
        public String getComment() {
            return comment;
        }

        /** @return the value associated with this element. */
        public String getValue() {
            return value;
        }

        /** Overrides superclass method. */
        public boolean equals(Object obj) {
            if(obj == null || !(obj instanceof StringPair))
                return false;
            
            StringPair compared = (StringPair)obj;

            // PENDING compare keyTypes as well?
            
            // Compare commnents first.
            if(comment == null && compared.getComment() != null)
                return false;
 
            String str1 = comment;
            String str2 = compared.getComment();
            
            if(!str1.equals(str2))
                return false;
            
            // Compare values.
            if(value == null && compared.getValue() != null)
                return false;
            
            str1 = value;
            str2 = compared.getValue();
            
            return str1.equals(str2);
        }
        
        /** Overrides superclass method. */
        public String toString() {
            return value;
        }

        /** Returns the type key/value of the pair. */
        public boolean isKeyType () {
            return keyType;
        }

        /** @return true if comment should be allowed for editing. */
        public boolean isCommentEditable() {
            return commentEditable;
        }

        /** Sets whether the comment should be allowed to be edited. */
        public void setCommentEditable(boolean newEditable) {
            commentEditable = newEditable;
        }
    } // End of nested class StringPair.
        
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.