All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openide.nodes.Index Maven / Gradle / Ivy

There is a newer version: RELEASE240
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.openide.nodes;

import java.util.*;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


/** Index cookie providing operations useful for reordering
* child nodes. {@link IndexedNode} is the common implementation.
*
* @author Jaroslav Tulach, Dafe Simonek
*/
public interface Index extends Node.Cookie {
    /** Get the number of nodes.
    * @return the count
    */
    public int getNodesCount();

    /** Get the child nodes.
    * @return array of nodes that can be sorted by this index
    */
    public Node[] getNodes();

    /** Get the index of a given node.
    * @param node node to find index of
    * @return index of the node, or -1 if no such node was found
    */
    public int indexOf(final Node node);

    /** Invoke a dialog for reordering the children.
    */
    public void reorder();

    /** Reorder all children with a given permutation.
    * @param perm permutation with the length of current nodes. The permutation
    * lists the new positions of the original nodes, that is, for nodes
    * [A,B,C,D] and permutation [0,3,1,2], the final
    * order would be [A,C,D,B].
    * @exception IllegalArgumentException if the permutation is not valid
    */
    public void reorder(int[] perm);

    /** Move the element at the x-th position to the y-th position. All
    * elements after the y-th position are moved down.
    *
    * @param x the position to remove the element from
    * @param y the position to insert the element to
    * @exception IndexOutOfBoundsException if an index is out of bounds
    */
    public void move(int x, int y);

    /** Exchange two elements.
    * @param x position of the first element
    * @param y position of the second element
    * @exception IndexOutOfBoundsException if an index is out of bounds
    */
    public void exchange(int x, int y);

    /** Move an element up.
    * @param x index of element to move up
    * @exception IndexOutOfBoundsException if an index is out of bounds
    */
    public void moveUp(int x);

    /** Move an element down.
    * @param x index of element to move down
    * @exception IndexOutOfBoundsException if an index is out of bounds
    */
    public void moveDown(int x);

    /** Add a new listener to the listener list. The listener will be notified of
    * any change in the order of the nodes.
    *
    * @param chl new listener
    */
    public void addChangeListener(final ChangeListener chl);

    /** Remove a listener from the listener list.
    *
    * @param chl listener to remove
    */
    public void removeChangeListener(final ChangeListener chl);

    /*********************** Inner classes ***********************/
    /** A support class implementing some methods of the Index
    * cookie.
    */
    public abstract static class Support implements Index {
        /** Registered listeners */
        private HashSet listeners;

        /** Default constructor. */
        public Support() {
        }

        /* Moves element at x-th position to y-th position. All
        * elements after the y-th position are moved down.
        *
        * @param x the position to remove the element from
        * @param y the position to insert the element to
        * @exception IndexOutOfBoundsException if an index is out of bounds
        */
        public void move(final int x, final int y) {
            int[] perm = new int[getNodesCount()];

            // if the positions are the same then no move
            if (x == y) {
                return;
            }

            for (int i = 0; i < perm.length; i++) {
                if (((i < x) && (i < y)) || ((i > x) && (i > y))) {
                    // this area w/o change
                    perm[i] = i;
                } else {
                    if ((i > x) && (i < y)) {
                        // i-th element moves backward
                        perm[i] = i - 1;
                    } else {
                        // i-th element moves forward
                        perm[i] = i + 1;
                    }
                }
            }

            // set new positions for the elemets on x-th and y-th position
            perm[x] = y;

            if (x < y) {
                perm[y] = y - 1;
            } else {
                perm[y] = y + 1;
            }

            reorder(perm);
        }

        /* Exchanges two elements.
        * @param x position of the first element
        * @param y position of the second element
        * @exception IndexOutOfBoundsException if an index is out of bounds
        */
        public void exchange(final int x, final int y) {
            int[] perm = new int[getNodesCount()];

            for (int i = 0; i < perm.length; i++) {
                perm[i] = i;
            }

            perm[x] = y;
            perm[y] = x;

            reorder(perm);
        }

        /* Moves element up.
        * @param x index of element to move up
        * @exception IndexOutOfBoundsException if an index is out of bounds
        */
        public void moveUp(final int x) {
            exchange(x, x - 1);
        }

        /* Moves element down.
        * @param x index of element to move down
        * @exception IndexOutOfBoundsException if an index is out of bounds
        */
        public void moveDown(final int x) {
            exchange(x, x + 1);
        }

        /* Adds new listener to the listener list. Listener is notified of
        * any change in ordering of nodes.
        *
        * @param chl new listener
        */
        public void addChangeListener(final ChangeListener chl) {
            if (listeners == null) {
                listeners = new HashSet();
            }

            listeners.add(chl);
        }

        /* Removes listener from the listener list.
        * Removed listener isn't notified no more.
        *
        * @param chl listener to remove
        */
        public void removeChangeListener(final ChangeListener chl) {
            if (listeners == null) {
                return;
            }

            listeners.remove(chl);
        }

        /** Fires notification about reordering to all
        * registered listeners.
        *
        * @param che change event to fire off
        */
        protected void fireChangeEvent(ChangeEvent che) {
            if (listeners == null) {
                return;
            }

            HashSet cloned;

            // clone listener list
            synchronized (this) {
                cloned = (HashSet) listeners.clone();
            }

            // fire on cloned list to prevent from modifications when firing
            for (Iterator iter = cloned.iterator(); iter.hasNext();) {
                ((ChangeListener) iter.next()).stateChanged(che);
            }
        }

        /** Get the nodes; should be overridden if needed.
        * @return the nodes
        */
        public abstract Node[] getNodes();

        /** Get the index of a node. Simply scans through the array returned by {@link #getNodes}.
        * @param node the node
        * @return the index, or -1 if the node was not found
        */
        public int indexOf(final Node node) {
            Node[] arr = getNodes();

            for (int i = 0; i < arr.length; i++) {
                if (node.equals(arr[i])) {
                    return i;
                }
            }

            return -1;
        }

        /** Reorder the nodes with dialog; should be overridden if needed.
        */
        public void reorder() {
            showIndexedCustomizer(this);
        }

        /** Utility method to create and show an indexed customizer.
         * Displays some sort of dialog permitting the index cookie to be reordered.
         * Blocks until the reordering is performed (or cancelled).
         * @param idx the index cookie to reorder based on
         * @since 1.37
         */
        public static void showIndexedCustomizer(Index idx) {
            TMUtil.showIndexedCustomizer(idx);
        }

        /** Get the node count. Subclasses must provide this.
        * @return the count
        */
        public abstract int getNodesCount();

        /** Reorder by permutation. Subclasses must provide this.
        * @param perm the permutation
        */
        public abstract void reorder(int[] perm);
    }
     // end of Support inner class

    /** Reorderable children list stored in an array.
    */
    public static class ArrayChildren extends Children.Array implements Index {
        /** Support instance for delegation of some Index methods. */
        protected Index support;

        /** Constructor for the support.
        */
        public ArrayChildren() {
            this(null);
        }

        /** Constructor.
        * @param ar the array
        */
        private ArrayChildren(List ar) {
            super(ar);

            // create support instance for delegation of common tasks
            support = new Support() {
                        public Node[] getNodes() {
                            return ArrayChildren.this.getNodes();
                        }

                        public int getNodesCount() {
                            return ArrayChildren.this.getNodesCount();
                        }

                        public void reorder(int[] perm) {
                            ArrayChildren.this.reorder(perm);
                            fireChangeEvent(new ChangeEvent(ArrayChildren.this));
                        }
                    };
        }

        /** If default constructor is used, then this method is called to lazily create
        * the collection. Even it claims that it returns Collection only subclasses
        * of List are valid values.
        * 

* This implementation returns ArrayList. * * @return any List collection. */ @Override protected java.util.List initCollection() { return new ArrayList(); } /* Reorders all children with given permutation. * @param perm permutation with the length of current nodes * @exception IllegalArgumentException if the perm is not valid permutation */ public void reorder(final int[] perm) { MUTEX.postWriteRequest(new Runnable() { public void run() { Node[] n = nodes.toArray(new Node[nodes.size()]); List l = (List) nodes; for (int i = 0; i < n.length; i++) { l.set(perm[i], n[i]); } refresh(); } }); } /** Invokes a dialog for reordering children using {@link IndexedCustomizer}. */ public void reorder() { try { PR.enterReadAccess(); Support.showIndexedCustomizer(this); } finally { PR.exitReadAccess(); } } /* Moves element at x-th position to y-th position. All * elements after the y-th position are moved down. * Delegates functionality to Index.Support. * * @param x the position to remove the element from * @param y the position to insert the element to * @exception IndexOutOfBoundsException if an index is out of bounds */ public void move(final int x, final int y) { support.move(x, y); } /* Exchanges two elements. * Delegates functionality to Index.Support. * @param x position of the first element * @param y position of the second element * @exception IndexOutOfBoundsException if an index is out of bounds */ public void exchange(final int x, final int y) { support.exchange(x, y); } /* Moves element up. * Delegates functionality to Index.Support. * @param x index of element to move up * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveUp(final int x) { support.exchange(x, x - 1); } /* Moves element down. * Delegates functionality to Index.Support. * @param x index of element to move down * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveDown(final int x) { support.exchange(x, x + 1); } /* Returns the index of given node. * @param node Node to find index of. * @return Index of the node, -1 if no such node was found. */ public int indexOf(final Node node) { try { PR.enterReadAccess(); return ((List) nodes).indexOf(node); } finally { PR.exitReadAccess(); } } /* Adds new listener to the listener list. Listener is notified of * any change in ordering of nodes. * * @param chl new listener */ public void addChangeListener(final ChangeListener chl) { support.addChangeListener(chl); } /* Removes listener from the listener list. * Removed listener isn't notified no more. * * @param chl listener to remove */ public void removeChangeListener(final ChangeListener chl) { support.removeChangeListener(chl); } } // End of ArrayChildren inner class /** Implementation of index interface that operates on an list of * objects that are presented by given nodes. */ public abstract class KeysChildren extends Children.Keys { /** Support instance for delegation of some Index methods. */ private Index support; // JST: Maybe made protected /** list of objects that should be manipulated with this keys */ protected final List list; /** Constructor. * @param ar the array of any objects */ public KeysChildren(List ar) { list = ar; update(); } /** Getter for the index that works with this children. * @return the index */ public Index getIndex() { synchronized (this) { if (support == null) { support = createIndex(); } return support; } } /** The method that creates the supporting index for this * children object. */ protected Index createIndex() { // create support instance for delegation of common tasks return new Support() { /** Returns only nodes that are indexable. Not any fixed ones. */ public Node[] getNodes() { List l = Arrays.asList(KeysChildren.this.getNodes()); if (KeysChildren.this.nodes != null) { l.removeAll(KeysChildren.this.nodes); } return l.toArray(new Node[l.size()]); } public int getNodesCount() { return list.size(); } public void reorder(int[] perm) { KeysChildren.this.reorder(perm); update(); fireChangeEvent(new ChangeEvent(this)); } }; } /* Reorders the list with given permutation. * @param perm permutation with the length of current nodes * @exception IllegalArgumentException if the perm is not valid permutation */ protected void reorder(final int[] perm) { synchronized (lock()) { List n = new ArrayList(list); for (int i = 0; i < n.size(); i++) { list.set(perm[i], n.get(i)); } } } /** The lock to use when accessing the list. * By default this implementation returns the list itself, but can * be changed to lock more properly. * * @return lock to use for accessing the list */ protected Object lock() { return list; } /** Update the status of the list if it has changed. */ public final void update() { Collection keys; synchronized (lock()) { keys = new ArrayList(list); } super.setKeys(keys); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy