Logo Search packages:      
Sourcecode: jabref version File versions  Download package

GroupTreeNode.java

/*
 All programs in this directory and subdirectories are published under the 
 GNU General Public License as described below.

 This program is free software; you can redistribute it and/or modify it 
 under the terms of the GNU General Public License as published by the Free 
 Software Foundation; either version 2 of the License, or (at your option) 
 any later version.

 This program is distributed in the hope that it will be useful, but WITHOUT 
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
 more details.

 You should have received a copy of the GNU General Public License along 
 with this program; if not, write to the Free Software Foundation, Inc., 59 
 Temple Place, Suite 330, Boston, MA 02111-1307 USA

 Further information about the GNU GPL is available at:
 http://www.gnu.org/copyleft/gpl.ja.html
 */

package net.sf.jabref.groups;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.undo.AbstractUndoableEdit;

import net.sf.jabref.BibtexDatabase;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.SearchRule;

/**
 * A node in the groups tree that holds exactly one AbstractGroup.
 * 
 * @author jzieren
 */
00045 public class GroupTreeNode extends DefaultMutableTreeNode implements
            Transferable {
      public static final DataFlavor flavor;
      public static final DataFlavor[] flavors;

      static {
            DataFlavor df = null;
            try {
                  df = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType
                              + ";class=net.sf.jabref.groups.GroupTreeNode");
            } catch (ClassNotFoundException e) {
                  // never happens
            }
            flavor = df;
            flavors = new DataFlavor[] { flavor };
      }

      /**
       * Creates this node and associates the specified group with it.
       */
00065       public GroupTreeNode(AbstractGroup group) {
            setGroup(group);
      }

      /**
       * @return The group associated with this node.
       */
00072       public AbstractGroup getGroup() {
            return (AbstractGroup) getUserObject();
      }

      /**
       * Associates the specified group with this node.
       */
00079       public void setGroup(AbstractGroup group) {
            setUserObject(group);
      }

      /**
       * Returns a textual representation of this node and its children. This
       * representation contains both the tree structure and the textual
       * representations of the group associated with each node. It thus allows a
       * complete reconstruction of this object and its children.
       */
00089       public String getTreeAsString() {
            StringBuffer sb = new StringBuffer();
            Enumeration<GroupTreeNode> e = preorderEnumeration();
            GroupTreeNode cursor;
            while (e.hasMoreElements()) {
                  cursor = e.nextElement();
            sb.append(cursor.getLevel()).append(" ").append(cursor.getGroup().toString()).append("\n");
            }
            return sb.toString();
      }

      /**
       * Creates a deep copy of this node and all of its children, including all
       * groups.
       * 
       * @return This object's deep copy.
       */
00106       public GroupTreeNode deepCopy() {
            GroupTreeNode copy = new GroupTreeNode(getGroup());
            for (int i = 0; i < getChildCount(); ++i)
                  copy.add(((GroupTreeNode) getChildAt(i)).deepCopy());
            return copy;
      }
        
        /**
         * Update all groups, if necessary, to handle the situation where the group
         * tree is applied to a different BibtexDatabase than it was created for. This
         * is for instance used when updating the group tree due to an external change.
         *
         * @param db The database to refresh for.
         */
00120         public void refreshGroupsForNewDatabase(BibtexDatabase db) {
            for (int i = 0; i < getChildCount(); ++i) {
                GroupTreeNode node = (GroupTreeNode)getChildAt(i);
                node.getGroup().refreshForNewDatabase(db);
                node.refreshGroupsForNewDatabase(db);
            }
        }
      
      /**
       * @return An indexed path from the root node to this node. The elements in
       *         the returned array represent the child index of each node in the
       *         path. If this node is the root node, the returned array has zero
       *         elements.
       */
00134       public int[] getIndexedPath() {
            TreeNode[] path = getPath();
            int[] indexedPath = new int[path.length - 1];
            for (int i = 1; i < path.length; ++i)
                  indexedPath[i - 1] = path[i - 1].getIndex(path[i]);
            return indexedPath;
      }

      /**
       * Returns the node indicated by the specified indexedPath, which contains
       * child indices obtained e.g. by getIndexedPath().
       */
00146       public GroupTreeNode getNode(int[] indexedPath) {
            GroupTreeNode cursor = this;
            for (int i = 0; i < indexedPath.length; ++i)
                  cursor = (GroupTreeNode) cursor.getChildAt(indexedPath[i]);
            return cursor;
      }

      /**
       * @param indexedPath
       *            A sequence of child indices that describe a path from this
       *            node to one of its desendants. Be aware that if <b>indexedPath
       *            </b> was obtained by getIndexedPath(), this node should
       *            usually be the root node.
       * @return The descendant found by evaluating <b>indexedPath </b>. If the
       *         path could not be traversed completely (i.e. one of the child
       *         indices did not exist), null will be returned.
       */
00163       public GroupTreeNode getDescendant(int[] indexedPath) {
            GroupTreeNode cursor = this;
            for (int i = 0; i < indexedPath.length && cursor != null; ++i)
                  cursor = (GroupTreeNode) cursor.getChildAt(indexedPath[i]);
            return cursor;
      }

      /**
       * A GroupTreeNode can create a SearchRule that finds elements contained in
       * its own group, or the union of those elements in its own group and its
       * children's groups (recursively), or the intersection of the elements in
       * its own group and its parent's group. This setting is configured in the
       * group contained in this node.
       * 
       * @return A SearchRule that finds the desired elements.
       */
00179       public SearchRule getSearchRule() {
            return getSearchRule(getGroup().getHierarchicalContext());
      }

      protected SearchRule getSearchRule(int originalContext) {
            final int context = getGroup().getHierarchicalContext();
            if (context == AbstractGroup.INDEPENDENT)
                  return getGroup().getSearchRule();
            AndOrSearchRuleSet searchRule = new AndOrSearchRuleSet(
                        context == AbstractGroup.REFINING, false);
            searchRule.addRule(getGroup().getSearchRule());
            if (context == AbstractGroup.INCLUDING
                        && originalContext != AbstractGroup.REFINING) {
                  for (int i = 0; i < getChildCount(); ++i)
                        searchRule.addRule(((GroupTreeNode) getChildAt(i))
                                    .getSearchRule(originalContext));
            } else if (context == AbstractGroup.REFINING && !isRoot()
                        && originalContext != AbstractGroup.INCLUDING) {
                  searchRule.addRule(((GroupTreeNode) getParent())
                              .getSearchRule(originalContext));
            }
            return searchRule;
      }

      @Override
      @SuppressWarnings("unchecked")
      public Enumeration<GroupTreeNode> preorderEnumeration(){
            return super.preorderEnumeration();
      }
      
      @Override
      @SuppressWarnings("unchecked")
      public Enumeration<GroupTreeNode> depthFirstEnumeration(){
            return super.depthFirstEnumeration();
      }
      
      @Override
      @SuppressWarnings("unchecked")
      public Enumeration<GroupTreeNode> breadthFirstEnumeration(){
            return super.breadthFirstEnumeration();
      }
      
      @Override
      @SuppressWarnings("unchecked")
      public Enumeration<GroupTreeNode> children(){
            return super.children();
      }
      
      /**
       * Scans the subtree rooted at this node.
       * 
       * @return All groups that contain the specified entry.
       */
00232       public AbstractGroup[] getMatchingGroups(BibtexEntry entry) {
            Vector<AbstractGroup> matchingGroups = new Vector<AbstractGroup>();
            Enumeration<GroupTreeNode> e = preorderEnumeration();
            AbstractGroup group;
            while (e.hasMoreElements()) {
                  group = (e.nextElement()).getGroup();
                  if (group.contains(null, entry)) // first argument is never used
                        matchingGroups.add(group);
            }
            AbstractGroup[] matchingGroupsArray = new AbstractGroup[matchingGroups
                        .size()];
            return matchingGroups.toArray(matchingGroupsArray);
      }

      public boolean canMoveUp() {
            return getPreviousSibling() != null
                        && !(getGroup() instanceof AllEntriesGroup);
      }

      public boolean canMoveDown() {
            return getNextSibling() != null
                        && !(getGroup() instanceof AllEntriesGroup);
      }

      public boolean canMoveLeft() {
            return !(getGroup() instanceof AllEntriesGroup)
                        && !(((GroupTreeNode) getParent()).getGroup() instanceof AllEntriesGroup);
      }

      public boolean canMoveRight() {
            return getPreviousSibling() != null
                        && !(getGroup() instanceof AllEntriesGroup);
      }

      public AbstractUndoableEdit moveUp(GroupSelector groupSelector) {
            final GroupTreeNode myParent = (GroupTreeNode) getParent();
            final int index = myParent.getIndex(this);
            if (index > 0) {
                  UndoableMoveGroup undo = new UndoableMoveGroup(groupSelector,
                              groupSelector.getGroupTreeRoot(), this, myParent, index - 1);
                  myParent.insert(this, index - 1);
                  return undo;
            }
            return null;
      }

      public AbstractUndoableEdit moveDown(GroupSelector groupSelector) {
            final GroupTreeNode myParent = (GroupTreeNode) getParent();
            final int index = myParent.getIndex(this);
            if (index < parent.getChildCount() - 1) {
                  UndoableMoveGroup undo = new UndoableMoveGroup(groupSelector,
                              groupSelector.getGroupTreeRoot(), this, myParent, index + 1);
                  myParent.insert(this, index + 1);
                  return undo;
            }
            return null;
      }

      public AbstractUndoableEdit moveLeft(GroupSelector groupSelector) {
            final GroupTreeNode myParent = (GroupTreeNode) getParent();
            final GroupTreeNode myGrandParent = (GroupTreeNode) myParent
                        .getParent();
            // paranoia
            if (myGrandParent == null)
                  return null;
            final int index = myGrandParent.getIndex(myParent);
            UndoableMoveGroup undo = new UndoableMoveGroup(groupSelector,
                        groupSelector.getGroupTreeRoot(), this, myGrandParent,
                        index + 1);
            myGrandParent.insert(this, index + 1);
            return undo;
      }

      public AbstractUndoableEdit moveRight(GroupSelector groupSelector) {
            final GroupTreeNode myPreviousSibling = (GroupTreeNode) getPreviousSibling();
            // paranoia
            if (myPreviousSibling == null)
                  return null;
            UndoableMoveGroup undo = new UndoableMoveGroup(groupSelector,
                        groupSelector.getGroupTreeRoot(), this, myPreviousSibling,
                        myPreviousSibling.getChildCount());
            myPreviousSibling.add(this);
            return undo;
      }

      /**
       * @param path
       *            A sequence of child indices that designate a node relative to
       *            this node.
       * @return The node designated by the specified path, or null if one or more
       *         indices in the path could not be resolved.
       */
00324       public GroupTreeNode getChildAt(int[] path) {
            GroupTreeNode cursor = this;
            for (int i = 0; i < path.length && cursor != null; ++i)
                  cursor = (GroupTreeNode) cursor.getChildAt(path[i]);
            return cursor;
      }

      /** Adds the selected entries to this node's group. */
00332       public AbstractUndoableEdit addToGroup(BibtexEntry[] entries) {
            if (getGroup() == null)
                  return null; // paranoia
            AbstractUndoableEdit undo = getGroup().add(entries);
            if (undo instanceof UndoableChangeAssignment)
                  ((UndoableChangeAssignment) undo).setEditedNode(this);
            return undo;
      }

      /** Removes the selected entries from this node's group. */
00342       public AbstractUndoableEdit removeFromGroup(BibtexEntry[] entries) {
            if (getGroup() == null)
                  return null; // paranoia
            AbstractUndoableEdit undo = getGroup().remove(entries);
            if (undo instanceof UndoableChangeAssignment)
                  ((UndoableChangeAssignment) undo).setEditedNode(this);
            return undo;
      }

      public DataFlavor[] getTransferDataFlavors() {
            return flavors;
      }

      public boolean isDataFlavorSupported(DataFlavor someFlavor) {
            return someFlavor.equals(GroupTreeNode.flavor);
      }

      public Object getTransferData(DataFlavor someFlavor)
                  throws UnsupportedFlavorException, IOException {
            if (!isDataFlavorSupported(someFlavor))
                  throw new UnsupportedFlavorException(someFlavor);
            return this;
      }

      /**
       * Recursively compares this node's group and all subgroups.
       */
00369       public boolean equals(Object other) {
            if (!(other instanceof GroupTreeNode))
                  return false;
            final GroupTreeNode otherNode = (GroupTreeNode) other;
            if (getChildCount() != otherNode.getChildCount())
                  return false;
            AbstractGroup g1 = getGroup();
            AbstractGroup g2 = otherNode.getGroup();
            if ((g1 == null && g2 != null) || (g1 != null && g2 == null))
                  return false;
            if (g1 != null && g2 != null && !g1.equals(g2))
                  return false;
            for (int i = 0; i < getChildCount(); ++i) {
                  if (!getChildAt(i).equals(otherNode.getChildAt(i)))
                        return false;
            }
            return true;
      }

    @Override
    public int hashCode() {
        return getGroup().getName().hashCode();
    }
}

Generated by  Doxygen 1.6.0   Back to index