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-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.explorer.propertysheet;

import java.awt.Component;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.beans.*;
import java.util.*;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;

import org.openide.ErrorManager;
import org.openide.nodes.*;
import org.openide.util.Utilities;
import org.openide.util.NbBundle;
import org.openide.util.actions.SystemAction;
import org.openide.util.datatransfer.NewType;


/**
* This class encapsulates working with indexed properties.
*/
class IndexedPropertyEditor extends Object implements ExPropertyEditor {

    //XXX this class should be rewritten 
    //and moved to the Nodes package where it belongs - TDB
    
    // -----------------------------------------------------------------------------
    // Private variables

    private Object[] array;

    private PropertyEnv env;

    private PropertyChangeSupport propertySupport = new PropertyChangeSupport (this);

    private Node.IndexedProperty indexedProperty = null;
    
    private IndexedEditorPanel currentEditorPanel;
    // -----------------------------------------------------------------------------
    // init

    public IndexedPropertyEditor() {
    }

    // -----------------------------------------------------------------------------
    // ExPropertyEditor implementation

    public void attachEnv (PropertyEnv env) {
        this.env = env;
        env.setChangeImmediate(false);
        FeatureDescriptor details = env.getFeatureDescriptor();
        if (details instanceof Node.IndexedProperty) {
            indexedProperty = (Node.IndexedProperty)details;
        } else {
            throw new IllegalStateException("This is not an array: " + details); // NOI18N
        }
    }


    // -----------------------------------------------------------------------------
    // PropertyEditor implementation

    public void setValue(Object value) {
        if (value == null) {
            array = null;
            firePropertyChange();
            return;
        }
        if (!value.getClass ().isArray ()) {
            throw new IllegalArgumentException(env != null ? "Property whose value is not an array " + env.getFeatureDescriptor().getName() : "Unknown property - not attached yet.");  //NOI18N
        }
        if (value.getClass().getComponentType().isPrimitive()) {
            array = Utilities.toObjectArray (value);
        } else {
            array = (Object[])Array.newInstance(
                value.getClass().getComponentType(), 
                ((Object[])value).length);
            System.arraycopy(value, 0, array, 0, array.length);
        }
            
        firePropertyChange();
    }

    public Object getValue() {
        if (array == null) {
            return null;
        }
        if (indexedProperty.getElementType().isPrimitive()) {
            return Utilities.toPrimitiveArray(array);
        }
        return array;
    }

    public boolean isPaintable() {
        return false;
    }

    public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
    }

    public String getJavaInitializationString(int index) {
        if (array[index] == null) return "null"; // NOI18N
        try {
            indexedProperty.getIndexedPropertyEditor().setValue(array [index]);
            return indexedProperty.getIndexedPropertyEditor().getJavaInitializationString();
        } catch (NullPointerException e) {
            return "null"; // NOI18N
        }
    }

    public String getJavaInitializationString() {
        if (array == null) return ""; // NOI18N
        StringBuffer buf = new StringBuffer ("new "); // NOI18N
        buf.append (indexedProperty.getElementType().getName ());

        // empty array
        if (array.length == 0) {
            buf.append ("[0]"); // NOI18N
        } else
            // non-empty array
        {
            buf.append ("[] {\n\t"); // NOI18N
            for (int i = 0; i < array.length; i++) {
                try {
                    indexedProperty.getIndexedPropertyEditor().setValue (array[i]);
                    buf.append (indexedProperty.getIndexedPropertyEditor().getJavaInitializationString());
                } catch (NullPointerException e) {
                    buf.append ("null"); // NOI18N
                }
                if (i != array.length - 1)
                    buf.append (",\n\t"); // NOI18N
                else
                    buf.append ("\n"); // NOI18N
            }
            buf.append ("}"); // NOI18N
        }
        return buf.toString ();
    }

    public String getAsText() {
        if (array == null) return "null"; // NOI18N
        StringBuffer buf = new StringBuffer ("["); // NOI18N
        PropertyEditor p = null;
            if (indexedProperty != null) {
                p = indexedProperty.getIndexedPropertyEditor();
            }
        for (int i = 0; i < array.length; i++) {
            if (p != null) {
                p.setValue (array[i]);
                // bugfix #27361, append property's value as text instead of java initialization string
                buf.append (p.getAsText());
            } else {
                buf.append ("null"); // NOI18N
            }
            if (i != array.length - 1)
                buf.append (", "); // NOI18N
        }
        buf.append ("]"); // NOI18N

        return buf.toString ();
    }

    public void setAsText(String text) throws java.lang.IllegalArgumentException {
        if (text.equals("null")) { // NOI18N
            setValue(null);
            return;
        }
        if (text.equals("[]")) { // NOI18N
            setValue(Array.newInstance(indexedProperty.getElementType(), 0));
            return;
        }
       
        int i1 = text.indexOf('[');
        if ((i1 <0) || (i1+1 >= text.length()) ) {
            i1 = 0;
        } else {
            i1++;
        }
        int i2 = text.lastIndexOf(']');
        if (i2 < 0) {
            i2 = text.length();
        }
        if ((i2 < i1) || (i2 > text.length()) ) {
            return;
        } else {
            
        }
        try {   
            PropertyEditor p = indexedProperty.getIndexedPropertyEditor();
            if (p == null) { //Test for no editor, it's not guaranteed there will be one
                throw new IllegalStateException ("Indexed type has no property " +
                    "editor");
            }
            text = text.substring(i1, i2);
            StringTokenizer tok = new StringTokenizer(text, ","); // NOI18N
            java.util.List list = new LinkedList();
            while (tok.hasMoreTokens()) {
                String s = tok.nextToken();
                p.setAsText(s.trim());
                list.add(p.getValue());
            }
            Object [] a = list.toArray((Object[])Array.newInstance(getConvertedType(), list.size()));
            setValue(a);
        } catch (Exception x) {
            IllegalArgumentException iae = new IllegalArgumentException();
            ErrorManager.getDefault().annotate(iae, ErrorManager.USER,
		getString("EXC_ErrorInIndexedSetter"),
                getString("EXC_ErrorInIndexedSetter"), x, new java.util.Date());
            throw iae;
        }
    }

    public String[] getTags() {
        return null;
    }

    public Component getCustomEditor() {
        if (array == null) {
            array = (Object[])Array.newInstance(getConvertedType(), 0);
            firePropertyChange();
        }
        Node dummy = new DisplayIndexedNode(0);

        // beware - this will function only if the DisplayIndexedNode has
        // one property on the first sheet and the property is of type
        // ValueProp
        Node.Property prop = dummy.getPropertySets()[0].getProperties()[0];

        Node.Property []np = new Node.Property[] { prop };
        currentEditorPanel = new IndexedEditorPanel(createRootNode(), np);
        return currentEditorPanel;
    }

    public boolean supportsCustomEditor() {
        return true;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.addPropertyChangeListener (listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.removePropertyChangeListener (listener);
    }

    // other methods ........................................................................

    private Node createRootNode() {
        DisplayIndexedNode[]n = new DisplayIndexedNode[array.length];
        for (int i = 0; i < n.length; i++) {
            n[i] = new DisplayIndexedNode(i);
        }
        MyIndexedRootNode idr = new MyIndexedRootNode(n);
        Index ind = (Index)idr.getCookie(Index.class);
        for (int i = 0; i < n.length; i++) {
            ind.addChangeListener(org.openide.util.WeakListeners.change (n[i], ind));
        }
        return idr;
    }
    
    
    
    /**
     * Converts indexedProperty.getElementType() class
     */
    private Class getConvertedType() {
        Class type = indexedProperty.getElementType();
        if (type.isPrimitive()) {
            type = Utilities.getObjectType(type);
        }
        return type;
    }

    void firePropertyChange () {
        propertySupport.firePropertyChange ("value", null, null); // NOI18N
    }

    private static String getString(String key) {
        return NbBundle.getBundle(IndexedPropertyEditor.class).getString(key);
    }  

    /**
     * Makes an attempt to create new value of the property. Usefull
     * especially when enlarging the array.
     */
    private Object defaultValue() {
        Object value = null;
        if (indexedProperty.getElementType().isPrimitive()) {
            if (getConvertedType().equals(Integer.class)) {
                value = new Integer(0);
            }
            if (getConvertedType().equals(Boolean.class)) {
                value = Boolean.FALSE;
            }
            if (getConvertedType().equals(Byte.class)) {
                value = new Byte((byte)0);
            }
            if (getConvertedType().equals(Character.class)) {
                value = new Character('\u0000');
            }
            if (getConvertedType().equals(Double.class)) {
                value = new Double(0d);
            }
            if (getConvertedType().equals(Float.class)) {
                value = new Float(0f);
            }
            if (getConvertedType().equals(Long.class)) {
                value = new Long(0l);
            }
            if (getConvertedType().equals(Short.class)) {
                value = new Short((short)0);
            }
        } else {
            try {
                value = getConvertedType().newInstance();
            } catch (Exception x) {
                // ignore any exception - if this fails just
                // leave null as the value
            }
        }
        return value;
    }
    
    /**
     * Node displayed in TableSheetView. Encapsulates a value of
     * one element in the array.
     */
    class DisplayIndexedNode extends AbstractNode implements ChangeListener {
        private int index;
        public DisplayIndexedNode(int index) {
            super (Children.LEAF);
            this.index = index;
            setName(Integer.toString(index));
            setDisplayName(Integer.toString(index));
        }

        protected SystemAction[] createActions() {
            try {
                return new SystemAction[] {
                    SystemAction.get(Class.forName("org.openide.actions.MoveUpAction")), // NOI18N
                    SystemAction.get(Class.forName("org.openide.actions.MoveDownAction")) // NOI18N
                };
            } catch (ClassNotFoundException cnfe) {
                // silently ignore in case we are in standalone library
                // without these actions
            }
            return null;
        }

        // Create a property sheet:
        protected Sheet createSheet () {
            Sheet sheet = super.createSheet ();
            Sheet.Set props = sheet.get (Sheet.PROPERTIES);
            if (props == null) {
                props = Sheet.createPropertiesSet ();
                sheet.put (props);
            }
            props.put (new ValueProp());
            return sheet;
        }

        /**
         * Destroy doesn't do regular destroy here
         * but changes the whole array and recreates
         * the node hierarchy. Does *not* even call super.destroy()!
         */
        public void destroy () throws java.io.IOException {
            Object []newArray = (Object[])Array.newInstance(getConvertedType(), array.length-1);
            System.arraycopy(array, 0, newArray, 0, index);
            System.arraycopy(array, index+1, newArray, index, array.length-index-1);
            // throw away the old array!
            array = newArray;
            IndexedPropertyEditor.this.firePropertyChange();
            if (currentEditorPanel != null) {
                currentEditorPanel.getExplorerManager().setRootContext(
                    createRootNode());
            }
        }
        
        /**
         * Listens on parent node. Assumes that parent node is
         * Indexed node !
         */
        public void stateChanged(ChangeEvent e) {
            Node parent = getParentNode();
            Index i = (Index)parent.getCookie(Index.class);
            if (i != null) {
                int currentIndex = i.indexOf(this);
                if (currentIndex != index) {
                    if (currentIndex > index) {
                        // first swap the values in array
                        // the condition is there in order react by swapping
                        // the values only on one of the two nodes
                        Object tmp = array[index];
                        array[index] = array[currentIndex];
                        array[currentIndex] = tmp;
                    }
                    // update the index variable and change the name
                    index = currentIndex;
                    DisplayIndexedNode.this.firePropertyChange(null, null, null);
                    setDisplayName(Integer.toString(index));
                    IndexedPropertyEditor.this.firePropertyChange();
                }
            }
        }

        private class ValueProp extends PropertySupport
        {
            public ValueProp () {
                super(
                    indexedProperty.getName(), 
                    indexedProperty.getElementType(),
                    indexedProperty.getDisplayName(),
                    indexedProperty.getShortDescription(),
                    indexedProperty.canRead(), 
                    indexedProperty.canWrite()); 
            }
            public Object getValue () {
                if (index < array.length) {
                    return array[index];
                } else {
                    return null;
                }
            }

            public void setValue (Object value) throws IllegalArgumentException,
            IllegalAccessException, InvocationTargetException
            {
                Object oldVal = array[index];
                array[index] = value;
                DisplayIndexedNode.this.firePropertyChange (this.getName(), oldVal, value);
                IndexedPropertyEditor.this.firePropertyChange();
            }

            public PropertyEditor getPropertyEditor ()
            {
                return indexedProperty.getIndexedPropertyEditor();
            }
        }
    }

    /** Root node in the TableSheetView. This node should
     * not be displayed.
     */
    private class MyIndexedRootNode extends IndexedNode {
        public MyIndexedRootNode(Node[] ch) {
            getChildren().add(ch);
            setName("IndexedRoot"); // NOI18N
            setDisplayName(NbBundle.getBundle(IndexedPropertyEditor.class).getString("CTL_Index"));
        }
        
        public NewType[] getNewTypes() {
            NewType nt = new NewType() {
                public void create() {
                    if (array != null) {
                        Object []newArray = (Object[])Array.newInstance(getConvertedType(), array.length+1);
                        System.arraycopy(array, 0, newArray, 0, array.length);
                        // throw away the old array!
                        array = newArray;
                        array[array.length-1] = defaultValue();
                    } else {
                        // throw away the old array!
                        array = (Object[])Array.newInstance(getConvertedType(), 1);
                        array[0] = defaultValue();
                    }
                    IndexedPropertyEditor.this.firePropertyChange();
                    DisplayIndexedNode din = new DisplayIndexedNode(array.length-1);
                    getChildren().add(new Node[] { din });
                    Index i = (Index)getCookie(Index.class);
                    i.addChangeListener(org.openide.util.WeakListeners.change(din, i));
                }
            };
            return new NewType[] { nt };
        }
    }
}
... 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.