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

package org.openide.loaders;

import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.beans.*;
import java.io.IOException;
import java.io.Serializable;
import java.io.File;
import java.lang.ref.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import org.openide.DialogDisplayer;

import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.util.datatransfer.*;
import org.openide.cookies.*;
import org.openide.filesystems.*;
import org.openide.util.HelpCtx;
import org.openide.nodes.*;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;

/** A folder containing data objects.
* Is actually itself a data object, whose primary (and only) file object
* is a file folder.
* 

Has special support for determining the sorting of the folder, * or even explicit ordering of the children. * * @author Jaroslav Tulach, Petr Hamernik */ public class DataFolder extends MultiDataObject implements Serializable, DataObject.Container { /** generated Serialized Version UID */ static final long serialVersionUID = -8244904281845488751L; /** Name of property that holds children of this node. */ public static final String PROP_CHILDREN = Container.PROP_CHILDREN; /** Name of property which decides sorting mode. */ public static final String PROP_SORT_MODE = "sortMode"; // NOI18N /** name of extended attribute for order of children */ static final String EA_SORT_MODE = "OpenIDE-Folder-SortMode"; // NOI18N /** name of extended attribute for order of children */ static final String EA_ORDER = "OpenIDE-Folder-Order"; // NOI18N /** Name of property for order of children. */ public static final String PROP_ORDER = "order"; // NOI18N /** Name of set with sorting options. */ public static final String SET_SORTING = "sorting"; // NOI18N /** Icon resource string for folder node */ static final String FOLDER_ICON_BASE = "org/openide/loaders/defaultFolder"; // NOI18N /** name of a shadow file for a root */ private static final String ROOT_SHADOW_NAME = "Root"; // NOI18N /** listener that contains array of children * Also represents the folder as the node delegate. */ private FolderList list; /** Listener for changes in FolderList */ private PropertyChangeListener pcl; private DataTransferSupport dataTransferSupport = new Paste (); /** Create a data folder from a folder file object. * @deprecated This method should not be used in client code. * If you are searching for a DataFolder for * a FileObject use {@link DataFolder#findFolder} factory method. * * @param fo file folder to work on * @exception DataObjectExistsException if there is one already * @exception IllegalArgumentException if fo is not folder */ public DataFolder (FileObject fo) throws DataObjectExistsException, IllegalArgumentException { this(fo, DataLoaderPool.getFolderLoader ()); } /** Create a data folder from a folder file object. * * @param fo file folder to work on * @param loader data loader for this data object * @exception DataObjectExistsException if there is one already * @exception IllegalArgumentException if fo is not folder */ protected DataFolder (FileObject fo, MultiFileLoader loader) throws DataObjectExistsException, IllegalArgumentException { this (fo, loader, true); } /** Create a data folder from a folder file object. * @param fo file folder to work on * @param loader data loader for this data object * @exception DataObjectExistsException if there is one already * @exception IllegalArgumentException if fo is not folder * @deprecated Since 1.13 do not use this constructor, it is for backward compatibility only. */ protected DataFolder (FileObject fo, DataLoader loader) throws DataObjectExistsException, IllegalArgumentException { super (fo, loader); init(fo, true); } /** Create a data folder from a folder file object. * @param fo file folder to work on * @param loader data loader for this data object * @param attach listen to changes? * @exception DataObjectExistsException if there is one already * @exception IllegalArgumentException if fo is not folder */ private DataFolder (FileObject fo, MultiFileLoader loader, boolean attach) throws DataObjectExistsException, IllegalArgumentException { super (fo, loader); init(fo, attach); } /** Perform initialization after construction. * @param fo file folder to work on * @param attach listen to changes? */ private void init(FileObject fo, boolean attach) throws IllegalArgumentException { if (!fo.isFolder ()) { // not folder => throw an exception throw new IllegalArgumentException ("Not folder: " + fo); // NOI18N } list = reassignList (fo, attach); } /** Attaches a listener to the folder list, removes any previous one if registered. * @param fo the new primary file we should listen on * @param attach really attache listener */ private FolderList reassignList (FileObject fo, boolean attach) { // creates object that handles all elements in array and // assignes it to the FolderList list = FolderList.find (fo, true); if (attach) { pcl = new ListPCL (); list.addPropertyChangeListener (org.openide.util.WeakListeners.propertyChange (pcl, list)); } return list; } /** Helper method to find or create a folder of a given path. * Tries to find such a subfolder, or creates it if it needs to. * * @param folder the folder to start in * @param name a subfolder path (e.g. com/mycom/testfolder) * @return a folder with the given name * @exception IOException if the I/O fails */ public static DataFolder create (DataFolder folder, String name) throws IOException { StringTokenizer tok = new StringTokenizer (name, "/"); // NOI18N while (tok.hasMoreTokens ()) { String piece = tok.nextToken (); if (! confirmName (piece)) { throw new IOException (NbBundle.getMessage (DataFolder.class, "EXC_WrongName", piece)); } } return DataFolder.findFolder (FileUtil.createFolder (folder.getPrimaryFile (), name)); } /** Set the sort mode for the folder. * @param mode an constant from {@link DataFolder.SortMode} * @exception IOException if the mode cannot be set */ public synchronized final void setSortMode (SortMode mode) throws IOException { SortMode old = getOrder ().getSortMode (); getOrder ().setSortMode (mode); firePropertyChange (PROP_SORT_MODE, old, getOrder ().getSortMode ()); } /** Get the sort mode of the folder. * @return the sort mode */ public final SortMode getSortMode () { return getOrder ().getSortMode (); } /** Set the order of the children. * The provided array defines * the order of some children for the folder. Such children * will be returned at the beginning of the array returned from * {@link #getChildren}. If there are any other children, they * will be appended to the array. * * @param arr array of data objects (children of this * folder) to define the order; or null if any particular ordering should * be cancelled * * @exception IOException if the order cannot be set * */ public synchronized final void setOrder (DataObject[] arr) throws IOException { getOrder ().setOrder (arr); firePropertyChange (PROP_ORDER, null, null); } /** Getter for order object. * @return order of children */ private FolderOrder getOrder () { return FolderOrder.findFor (getPrimaryFile ()); } /** Get the name of the data folder. *

This implementation uses the name and extension of the primary file. * @return the name */ public String getName () { return getPrimaryFile ().getNameExt (); } /** Get the children of this folder. * @return array of children */ public DataObject[] getChildren () { return list.getChildren (); } /** Getter for list of children. * @param filter filter to notify about addition of new objects */ final List getChildrenList () { return list.getChildrenList (); } /** Computes list of children asynchronously * @param l listener to notify about the progress * @return task that will handle the computation */ final RequestProcessor.Task computeChildrenList (FolderListListener l) { return list.computeChildrenList (l); } /** Get enumeration of children of this folder. * @return enumeration of {@link DataObject}s */ public Enumeration children () { return Collections.enumeration (getChildrenList ()); } /** Enumerate all children of this folder. If the children should be enumerated * recursively, first all direct children are listed; then children of direct subfolders; and so on. * * @param rec whether to enumerate recursively * @return enumeration of type DataObject */ public Enumeration children (final boolean rec) { if (!rec) { return children(); } class Processor implements org.openide.util.Enumerations.Processor { /** @param o processes object by adding its children to the queue */ public Object process (Object o, Collection toAdd) { DataObject dataObj = (DataObject)o; if (rec && dataObj instanceof DataFolder) { toAdd.addAll (Arrays.asList (((DataFolder)dataObj).getChildren())); } return o; } } Enumeration en = org.openide.util.Enumerations.queue ( org.openide.util.Enumerations.array (getChildren ()), new Processor () ); return en; } /** Create node representative for this folder. */ protected synchronized Node createNodeDelegate () { FolderNode fn = new FolderNode(); // netbeans.core.nodes.description fn.setShortDescription(NbBundle.getMessage (DataFolder.class, "HINT_Folder")); // NOI18N return fn; } private final class ClonedFilter extends FilterNode { private DataFilter filter; private int hashCode = -1; // We need to remember the hash code in // order to keep it constant fix for public ClonedFilter (Node n, DataFilter filter) { super (n, DataFolder.this.createNodeChildren (filter)); this.filter = filter; } public ClonedFilter (DataFilter filter) { this (DataFolder.this.getNodeDelegate (), filter); } public Node cloneNode () { if (isValid()) { return new ClonedFilter (filter); } else { return super.cloneNode(); } } public Node.Handle getHandle () { return new ClonedFilterHandle (DataFolder.this, filter); } public boolean equals (Object o) { if (o == null) { return false; } else if (o == this) { return true; } else if (o instanceof FolderNode) { FolderNode fn = (FolderNode) o; if (fn.getCookie (DataFolder.class) != DataFolder.this) return false; org.openide.nodes.Children ch = fn.getChildren (); return (ch instanceof FolderChildren) && ((FolderChildren) ch).getFilter ().equals (filter); } else if (o instanceof ClonedFilter) { ClonedFilter cf = (ClonedFilter) o; return cf.getCookie (DataFolder.class) == DataFolder.this && cf.filter.equals (filter); } else { return false; } } public int hashCode () { if ( hashCode == -1 ) { if ( isValid() ) { hashCode = getNodeDelegate().hashCode(); } else { hashCode = super.hashCode(); } if ( hashCode == -1 ) { hashCode = -2; } } return hashCode; } } private final static class ClonedFilterHandle implements Node.Handle { private final static long serialVersionUID = 24234097765186L; private DataObject folder; private DataFilter filter; public ClonedFilterHandle (DataFolder folder, DataFilter filter) { this.folder = folder; this.filter = filter; } public Node getNode () throws IOException { if (folder instanceof DataFolder) { return ((DataFolder)folder).new ClonedFilter (filter); } else { throw new java.io.InvalidObjectException( folder == null ? "" : folder.toString() // NOI18N ); } } } /** This method allows DataFolder to filter its nodes. * * @param filter filter for subdata objects * @return the node delegate (without parent) for this data object */ Node getClonedNodeDelegate (DataFilter filter) { Node n = getNodeDelegate (); Children c = n.getChildren (); // #7362: relying on subclassers to override createNodeChildren is ugly... if (c.getClass () == FolderChildren.class) { DataFilter f = ((FolderChildren) c).getFilter (); if (f == DataFilter.ALL) { // Either createNodeDelegate was not overridden; or // it provided some node with the same children as // DataFolder would have anyway. Filter the children. return new ClonedFilter (n, filter); } else if (filter != DataFilter.ALL && filter != f) { // Tricky. createNodeDelegate was overridden, and it is // producing FolderChildren with some special filter. // Apply both the subclass's filter and this additional one. return new ClonedFilter (n, filterCompose (f, filter)); } else { // Subclass provided FolderChildren with some special filter, // and we are not trying to filter specially. Let the subclass // display as usual. return n.cloneNode (); } } else { // We have some DataFolder subclass with idiosyncratic children. // Play it safe and let it display what it wants. return n.cloneNode (); } } /** Logically compose two filters: accept the intersection. */ private static DataFilter filterCompose (final DataFilter f1, final DataFilter f2) { if (f1.equals (f2)) { return f1; } else { return new DataFilter () { public boolean acceptDataObject (DataObject obj) { return f1.acceptDataObject (obj) && f2.acceptDataObject (obj); } }; } } /** Support method to obtain a children object that * can be added to any {@link Node}. The provided filter can be * used to exclude some objects from the list. *

Overriding this method is deprecated! * @param filter filter of data objects * @return children object representing content of this folder */ public /* XXX final */ Children createNodeChildren (DataFilter filter) { return new FolderChildren (this, filter); } /* Getter for delete action. * @return true if the object can be deleted */ public boolean isDeleteAllowed () { return isRenameAllowed (); } /* Getter for copy action. * @return true if the object can be copied */ public boolean isCopyAllowed () { return true; } /* Getter for move action. * @return true if the object can be moved */ public boolean isMoveAllowed () { return isRenameAllowed (); } /* Getter for rename action. * @return true if the object can be renamed */ public boolean isRenameAllowed () { FileObject fo = getPrimaryFile (); return !fo.isRoot() && !fo.isReadOnly (); } /* Help context for this object. * @return help context */ public HelpCtx getHelpCtx () { return null; } /** Create a folder for a specified file object. * @param fo file object * @return folder for the file object * @exception IllegalArgumentException if the file object is not folder */ public static DataFolder findFolder (FileObject fo) { DataObject d; try { d = DataObject.find(fo); } catch (DataObjectNotFoundException e) { throw (IllegalArgumentException)new IllegalArgumentException(e.toString()).initCause(e); } if (!(d instanceof DataFolder)) { throw new IllegalArgumentException("Not a DataFolder: " + fo + " (was a " + d.getClass().getName() + ") (file is folder? " + fo.isFolder() + ")"); // NOI18N } return (DataFolder)d; } /** Finds a DataObject.Container representing given folder. * @param fo file object (must be folder) * @return the container for the file object * @exception IllegalArgumentException if the file object is not folder * * @since 1.11 */ public static DataObject.Container findContainer (FileObject fo) { if (fo.isFolder ()) { return FolderList.find (fo, true); } else { throw new IllegalArgumentException ("Not a folder: " + fo); // NOI18N } } /* Copy this object to a folder. * The copy of the object is required to * be deletable and movable. * * @param f the folder to copy object to * @exception IOException if something went wrong * @return the new object */ protected DataObject handleCopy (DataFolder f) throws IOException { testNesting(this, f); Enumeration en = children (); DataFolder newFolder = (DataFolder)super.handleCopy (f); while (en.hasMoreElements ()) { try { DataObject obj = (DataObject)en.nextElement (); if (obj.isCopyAllowed()) { obj.copy (newFolder); } else { // data object can not be copied, inform user ErrorManager.getDefault().log(ErrorManager.USER, NbBundle.getMessage(DataFolder.class, "FMT_CannotCopyDo", obj.getName() ) ); } } catch (IOException ex) { ErrorManager.getDefault().notify(ex); } } return newFolder; } /** * Ensure that given folder is not parent of targetFolder. Also * ensure that they are not equal. */ static void testNesting(DataFolder folder, DataFolder targetFolder) throws IOException { if (targetFolder.equals(folder)) { throw (IOException) ErrorManager.getDefault().annotate( new IOException("Error Copying File or Folder"), //NOI18N ErrorManager.WARNING, null, NbBundle.getMessage(DataFolder.class, "EXC_CannotCopyTheSame", folder.getName()) //NOI18N , null, null); } else { DataFolder testFolder = targetFolder.getFolder(); while (testFolder != null) { if (testFolder.equals(folder)) { throw (IOException) ErrorManager.getDefault().annotate( new IOException("Error copying file or folder: " + folder.getPrimaryFile() + " cannot be copied to its subfolder " + targetFolder.getPrimaryFile()), //NOI18N ErrorManager.WARNING, null, NbBundle.getMessage(DataFolder.class, "EXC_CannotCopySubfolder", folder.getName()) //NOI18N , null, null); } testFolder = testFolder.getFolder(); } } } /* Deals with deleting of the object. Must be overriden in children. * @exception IOException if an error occures */ protected void handleDelete () throws IOException { Enumeration en = children (); try { while (en.hasMoreElements ()) { DataObject obj = (DataObject)en.nextElement (); if (obj.isValid ()) { obj.delete (); } } } catch (IOException iex) { /** Annotates exception and throws again*/ FileObject fo = getPrimaryFile(); String message = NbBundle.getMessage(DataFolder.class, "EXC_CannotDelete2", FileUtil.getFileDisplayName(fo)); ErrorManager.getDefault().annotate(iex, message); throw iex; } super.handleDelete (); } /* Handles renaming of the object. * Must be overriden in children. * * @param name name to rename the object to * @return new primary file of the object * @exception IOException if an error occures */ protected FileObject handleRename (final String name) throws IOException { if (! confirmName (name)) { throw new IOException ("bad name: " + name) { // NOI18N public String getLocalizedMessage () { return NbBundle.getMessage (DataFolder.class, "EXC_WrongName", name); } }; } return super.handleRename (name); } /* Handles move of the object. Must be overriden in children. Since 1.13 move operation * behaves similar like copy, it merges folders whith existing folders in target location. * @param df target data folder * @return new primary file of the object * @exception IOException if an error occures */ protected FileObject handleMove (DataFolder df) throws IOException { FileObject originalFolder = getPrimaryFile (); FileLock lock = originalFolder.lock(); List backup = saveEntries(); try { // move entries (FolderEntry creates new folder when moved) FileObject newFile = super.handleMove (df); DataFolder newFolder = null; boolean dispose = false; boolean keepAlive = false; /* * The following code is a partial bugfix of the issue #8705. * Please note that this problem is hardly reproducible by users, * but only by unit test. * * The root of the problem is that it is not possible to disable * recognizing of DataObjects for some time. Couple of lines above * the file object (destination folder) is created using * super.handleMove(df) and couple of lines below DataFolder if created * for this file object using createMultiObject. * The problems are: * 1) Temporary DataFolder created as destination folder is used only * during copying the original (this) DataFolder content. * Then is is marked as not valid using setValid(false). The original * datafolder switches its primary file to the destination file object. * The problem occurs, when some other thread takes the node representing * the temporary folder. * Solution: Special DataFolder that delegates nodeDelegate and * clonedNodeDelegate to the original folder. * * 2) There is still some sort time between creating of fileobject * and its datafolder. Another thread can ask for parent folder's * dataobjects and it forces creation of "normal" datafolder, * not the special one (with delegating nodes). Then it is necessary * to dispose the normal DataFolder and try to create our one. * To prevent infinite look there is a count down initialy set * to 20 repeats. Acording to results of DataFolderMoveTest it should * help. When this solution fails it only means that in some rare * cases some DataNode might represent invalid DataFolder. It is * not possible to delete such a node in explorer for instance. * * This is really strange hack (especially the 2nd part), and it is * necessary to think about better solution for NetBeans 4.0 * data system architecture changes. * */ final int COUNT_DOWN_INIT = 20; int countDown = COUNT_DOWN_INIT; while (countDown >= 0) { countDown--; try { // resolve temporary object for moving into DataLoaderPool.FolderLoader folderLoader = (DataLoaderPool.FolderLoader) getMultiFileLoader (); newFolder = (DataFolder) DataObjectPool.createMultiObject (folderLoader, newFile, this); dispose = false; break; } catch (DataObjectExistsException e) { // object already exists, get it and remember we should be discarded newFolder = (DataFolder)e.getDataObject (); newFolder.dispose(); dispose = true; } } // move all children Enumeration en = children (); while (en.hasMoreElements ()) { try { DataObject obj = (DataObject)en.nextElement (); if (obj.isMoveAllowed ()) { obj.move (newFolder); } else { keepAlive = true; // data object can not be moved, inform user ErrorManager.getDefault().log(ErrorManager.USER, NbBundle.getMessage (DataFolder.class, "FMT_CannotMoveDo", obj.getName ()) ); } } catch (IOException ex) { keepAlive = true; ErrorManager.getDefault().notify(ex); } } if (keepAlive) { // some children couldn't be moved -> folder shouldn't be moved restoreEntries (backup); list.refresh (); return originalFolder; } // remove original folder try { originalFolder.delete (lock); } catch (IOException e) { Throwable t = ErrorManager.getDefault ().annotate (e, DataObject.getString ("EXC_folder_delete_failed")); // NOI18N ErrorManager.getDefault ().notify (t); } if (dispose) { // current object will be discarded, target already existed try { setValid (false); newFile = originalFolder; } catch (PropertyVetoException e) { // ignore, just repair entries restoreEntries (backup); newFile = getPrimaryEntry ().getFile (); } } else { // dispose temporary folder and place itself instead of it // call of changePrimaryFile and dispose must be in this order // to silently change DataFolders in the DataObjectPool item.changePrimaryFile (newFile); newFolder.dispose (); list = reassignList (newFile, true); } return newFile; } finally { lock.releaseLock(); } } /* Creates new object from template. * @param f folder to create object in * @return new data object * @exception IOException if an error occured */ protected DataObject handleCreateFromTemplate ( DataFolder f, String name ) throws IOException { DataFolder newFolder = (DataFolder)super.handleCreateFromTemplate (f, name); Enumeration en = children (); while (en.hasMoreElements ()) { try { DataObject obj = (DataObject)en.nextElement (); obj.createFromTemplate (newFolder); } catch (IOException ex) { ErrorManager.getDefault().notify(ex); } } return newFolder; } /** Creates shadow for this object in specified folder (overridable in subclasses). *

The default * implementation creates a reference data shadow and pastes it into * the specified folder. * * @param f the folder to create a shortcut in * @return the shadow */ protected DataShadow handleCreateShadow (DataFolder f) throws IOException { // #33871 - prevent creation of recursive folder structure testNesting(this, f); String name; if (getPrimaryFile ().isRoot ()) { name = FileUtil.findFreeFileName ( f.getPrimaryFile (), ROOT_SHADOW_NAME, DataShadow.SHADOW_EXTENSION ); } else { name = null; } return DataShadow.create (f, name, this); } /** Merge folder on move or copy when it exists in target location. * @returns true * @since 1.13 */ boolean isMergingFolders() { return true; } /** Support for index cookie for folder nodes. */ public static class Index extends org.openide.nodes.Index.Support { /** Asociated data folder */ private DataFolder df; /** node to be associated with */ private Node node; /** change listener */ private Listener listener; /** Create an index cookie associated with a data folder. * @param df the data folder * @deprecated Please explicitly specify a node to be safe. */ public Index(final DataFolder df) { this (df, df.getNodeDelegate ()); } /** Create an index cookie associated with a data folder. * @param df the data folder * @param node node to be associated with. subnodes of this node will be returned, etc. */ public Index(final DataFolder df, Node node) { this.df = df; this.node = node; listener = new Listener (); node.addNodeListener (org.openide.nodes.NodeOp.weakNodeListener (listener, node)); } /* Returns count of the nodes. */ public int getNodesCount () { return node.getChildren().getNodesCount(); } /* Returns array of subnodes * @return array of subnodes */ public Node[] getNodes () { return node.getChildren().getNodes(); } /* 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 (int[] perm) { // #11809: the children of the node may not directly match the data folder // children. Specifically, it is legal to reorder a set of nodes where // each node has a distinct data object cookie, each object being a child of // this folder, but there are some objects missing. In such a case, the // specified objects are permuted according to the node permutation, while // other objects in the folder are left in their original positions and order. DataObject[] curObjs = df.getChildren(); DataObject[] newObjs = new DataObject[curObjs.length]; Node[] nodes = getNodes (); if (nodes.length != perm.length) { throw new IllegalArgumentException ("permutation of incorrect length: " + perm.length + " rather than " + nodes.length); // NOI18N } // hashtable from names of nodes to their data objects for // nodes that do not express their data object as their cookie HashMap names = new HashMap (2 * curObjs.length); for (int i = 0; i < curObjs.length; i++) { Node del = curObjs[i].getNodeDelegate (); if (del.getCookie (DataObject.class) == null) { names.put (del.getName (), curObjs[i]); } } DataObject[] dperm = new DataObject[perm.length]; for (int i = 0; i < perm.length; i++) { DataObject d = (DataObject) nodes[i].getCookie (DataObject.class); if (d == null) { // try to scan the names table too d = (DataObject)names.get (nodes[i].getName ()); } if (d == null) { throw new IllegalArgumentException ("cannot reorder node with no DataObject: " + nodes[i]); // NOI18N } if (d.getFolder () != df) { throw new IllegalArgumentException ("wrong folder for: " + d.getPrimaryFile () + " rather than " + df.getPrimaryFile ()); // NOI18N } dperm[perm[i]] = d; } Set dpermSet = new HashSet (Arrays.asList (dperm)); // Set if (dpermSet.size () != dperm.length) { throw new IllegalArgumentException ("duplicate DataObject's among reordered childen"); // NOI18N } int dindex = 0; for (int i = 0; i < curObjs.length; i++) { if (dpermSet.remove (curObjs[i])) { newObjs[i] = dperm[dindex++]; } else { // Not reordered, leave where it was. newObjs[i] = curObjs[i]; } } try { df.setOrder(newObjs); } catch (IOException ex) { ErrorManager.getDefault ().annotate (ex, DataObject.getString ("EXC_ReorderFailed")); // NOI18N ErrorManager.getDefault ().notify (ex); } } /* Invokes a dialog for reordering subnodes. */ public void reorder () { Index.Support.showIndexedCustomizer(this); } /** Fires notification about reordering to all * registered listeners. */ void fireChangeEventAccess () { fireChangeEvent (new ChangeEvent (this)); } /** Listener to change of children of the folder. */ private final class Listener extends Object implements NodeListener { Listener() {} /** Change of children? */ public void propertyChange (PropertyChangeEvent ev) { } /** Fired when the node is deleted. * @param ev event describing the node */ public void nodeDestroyed(NodeEvent ev) { } /** Fired when the order of children is changed. * @param ev event describing the change */ public void childrenReordered(NodeReorderEvent ev) { fireChangeEventAccess (); } /** Fired when a set of children is removed. * @param ev event describing the action */ public void childrenRemoved(NodeMemberEvent ev) { fireChangeEventAccess (); } /** Fired when a set of new children is added. * @param ev event describing the action */ public void childrenAdded(NodeMemberEvent ev) { fireChangeEventAccess (); } } // end of Listener } // end of Index inner class /** Type-safe enumeration of sort modes for data folders. */ public abstract static class SortMode extends Object implements Comparator { /** Objects are unsorted. */ public static final SortMode NONE = new FolderComparator (FolderComparator.NONE); /** Objects are sorted by their names. */ public static final SortMode NAMES = new FolderComparator (FolderComparator.NAMES); /** Objects are sorted by their types and then by names. */ public static final SortMode CLASS = new FolderComparator (FolderComparator.CLASS); /** Folders go first (sorted by name) followed by non-folder * objects sorted by name. */ public static final SortMode FOLDER_NAMES = new FolderComparator (FolderComparator.FOLDER_NAMES); /** * Folders go first (sorted by name) followed by files sorted by decreasing * last modification time. * @since org.openide.loaders 4.10 */ public static final SortMode LAST_MODIFIED = new FolderComparator(FolderComparator.LAST_MODIFIED); /** * Folders go first (sorted by name) followed by files sorted by decreasing size. * @since org.openide.loaders 4.10 */ public static final SortMode SIZE = new FolderComparator(FolderComparator.SIZE); /** Method to write the sort mode to a folder's attributes. * @param folder folder write this mode to */ void write (FileObject f) throws IOException { // Let it throw the IOException: //if (f.getPrimaryFile ().getFileSystem ().isReadOnly ()) return; // cannot write to read-only FS String x; if (this == FOLDER_NAMES) x = "F"; // NOI18N else if (this == NAMES) x = "N"; // NOI18N else if (this == CLASS) x = "C"; // NOI18N else if (this == LAST_MODIFIED) x = "M"; // NOI18N else if (this == SIZE) x = "S"; // NOI18N else x = "O"; // NOI18N f.setAttribute (EA_SORT_MODE, x); } /** Reads sort mode for given folder. */ static SortMode read (FileObject f) { String x = (String)f.getAttribute (EA_SORT_MODE); if (x == null || x.length () != 1) return FOLDER_NAMES; char c = x.charAt (0); switch (c) { case 'N': return NAMES; case 'C': return CLASS; case 'O': return NONE; case 'M': return LAST_MODIFIED; case 'S': return SIZE; case 'F': default: return FOLDER_NAMES; } } } /** true if the new folder name is acceptable */ private static boolean confirmName (String folderName) { return folderName.indexOf ('/') == -1 && folderName.indexOf ('\\') == -1; } /** Node for a folder. */ public class FolderNode extends DataNode { /** Create a folder node with some children. * @param ch children to use for the node */ public FolderNode (Children ch) { super (DataFolder.this, ch); setIconBase(FOLDER_ICON_BASE); } /** Create a folder node with default folder children. */ protected FolderNode () { super (DataFolder.this, new FolderChildren (DataFolder.this)); setIconBase(FOLDER_ICON_BASE); } /** Overrides folder icon to search for icon in UIManager table for * BeanInfo.ICON_COLOR_16x16 type, to allow for different icons * across Look and Feels. * Keeps possibility of icon annotations. */ public Image getIcon (int type) { Image img = null; if (type == BeanInfo.ICON_COLOR_16x16) { // search for proper folder icon installed by core/windows module img = (Image)UIManager.get("Nb.Explorer.Folder.icon"); } if (img == null) { img = super.getIcon(type); } else { // give chance to annotate icon returned from UIManeger // copied from DataNode to keep the contract try { DataObject obj = getDataObject(); img = obj.getPrimaryFile().getFileSystem(). getStatus().annotateIcon(img, type, obj.files()); } catch (FileStateInvalidException e) { // no fs, do nothing } } return img; } /** Overrides folder icon to search for icon in UIManager table for * BeanInfo.ICON_COLOR_16x16 type, to allow for different icons * across Look and Feels. * Keeps possibility of icon annotations. */ public Image getOpenedIcon (int type) { Image img = null; if (type == BeanInfo.ICON_COLOR_16x16) { // search for proper folder icon installed by core/windows module img = (Image)UIManager.get("Nb.Explorer.Folder.openedIcon"); } if (img == null) { img = super.getOpenedIcon(type); } else { // give chance to annotate icon returned from UIManeger // copied from DataNode to keep the contract try { DataObject obj = getDataObject(); img = obj.getPrimaryFile().getFileSystem(). getStatus().annotateIcon(img, type, obj.files()); } catch (FileStateInvalidException e) { // no fs, do nothing } } return img; } public Node.Cookie getCookie (Class clazz) { if (clazz == org.openide.nodes.Index.class || clazz == Index.class) { //#33130 - enable IndexCookie only on SystemFileSystem try { if (DataFolder.this.getPrimaryFile().getFileSystem() == Repository.getDefault().getDefaultFileSystem()) { return new Index (DataFolder.this, this); } } catch (FileStateInvalidException ex) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); } } return super.getCookie (clazz); } /* Adds properties for sorting. * @return the augmented property sheet */ protected Sheet createSheet () { Sheet s = super.createSheet (); Sheet.Set ss = new Sheet.Set (); ss.setName (SET_SORTING); ss.setDisplayName (DataObject.getString ("PROP_sorting")); ss.setShortDescription (DataObject.getString ("HINT_sorting")); Node.Property p; p = new PropertySupport.ReadWrite ( PROP_SORT_MODE, SortMode.class, DataObject.getString("PROP_sort"), DataObject.getString("HINT_sort") ) { public Object getValue () { return DataFolder.this.getSortMode (); } public void setValue (Object o) throws InvocationTargetException { try { DataFolder.this.setSortMode ((SortMode)o); } catch (IOException ex) { throw new InvocationTargetException (ex); } } public java.beans.PropertyEditor getPropertyEditor () { return new SortModeEditor (); } }; ss.put (p); s.put (ss); return s; } /* No default action on data folder node. * @return null */ public Action getPreferredAction() { return null; } /* New type for creating new subfolder. * @return array with one element */ public NewType[] getNewTypes () { return new NewType[0]; /* Commented out. Folder is now created via template. if (getPrimaryFile ().isReadOnly ()) { // no new types return new NewType[0]; } else { return new NewType[] { new NewFolder () }; } */ } /* May add some paste types for objects being added to folders. * May move data objects; copy them; create links for them; instantiate * them as templates; serialize instances; or create instance data objects * from instances, according to the abilities of the transferable. * * @param t transferable to use * @param s list of {@link PasteType}s */ protected void createPasteTypes (Transferable t, java.util.List s) { super.createPasteTypes (t, s); if (!getPrimaryFile ().isReadOnly ()) { dataTransferSupport.createPasteTypes (t, s); } } } // end of FolderNode /** New type for creation of new folder. */ private final class NewFolder extends NewType { NewFolder() {} /** Display name for the creation action. This should be * presented as an item in a menu. * * @return the name of the action */ public String getName() { return DataObject.getString ("CTL_NewFolder"); } /** Help context for the creation action. * @return the help context */ public HelpCtx getHelpCtx() { return new HelpCtx (NewFolder.class); } /** Create the object. * @exception IOException if something fails */ public void create () throws IOException { NotifyDescriptor.InputLine input = new NotifyDescriptor.InputLine ( DataObject.getString ("CTL_NewFolderName"), DataObject.getString ("CTL_NewFolderTitle") ); input.setInputText (DataObject.getString ("CTL_NewFolderValue")); if (DialogDisplayer.getDefault ().notify (input) == NotifyDescriptor.OK_OPTION) { String folderName = input.getInputText (); if ("".equals (folderName)) return; // empty name = cancel // NOI18N FileObject folder = getPrimaryFile (); int dotPos = -1; while ((dotPos = folderName.indexOf (".")) != -1) { // NOI18N String subFolder = folderName.substring (0, dotPos); folderName = folderName.substring (dotPos + 1); FileObject existingFile = folder.getFileObject (subFolder); if (existingFile != null) { if (!existingFile.isFolder ()) { DialogDisplayer.getDefault ().notify ( new NotifyDescriptor.Message ( NbBundle.getMessage (DataObject.class, "MSG_FMT_FileExists", subFolder, folder.getName ()), NotifyDescriptor.WARNING_MESSAGE ) ); return; } folder = existingFile; } else { if (! confirmName (subFolder)) { throw new IOException( NbBundle.getMessage(DataObject.class, "EXC_WrongName", subFolder) ); } folder = folder.createFolder (subFolder); } } if (!"".equals (folderName)) { // NOI18N FileObject existingFile = folder.getFileObject (folderName); if (existingFile != null) { if (existingFile.isFolder ()) { DialogDisplayer.getDefault ().notify ( new NotifyDescriptor.Message ( NbBundle.getMessage (DataObject.class, "MSG_FMT_FolderExists", folderName, folder.getName ()), NotifyDescriptor.INFORMATION_MESSAGE ) ); } else { DialogDisplayer.getDefault ().notify ( new NotifyDescriptor.Message ( NbBundle.getMessage (DataObject.class, "MSG_FMT_FileExists", folderName, folder.getName ()), NotifyDescriptor.WARNING_MESSAGE ) ); } return; } if (! confirmName (folderName)) { throw new IOException( NbBundle.getMessage(DataObject.class, "EXC_WrongName", folderName ) ); } DataObject created = DataObject.find(folder.createFolder (folderName)); if (created != null) { DataLoaderPool.getDefault().fireOperationEvent( new OperationEvent.Copy (created, DataFolder.this), OperationEvent.TEMPL ); } } } } } private class Paste extends DataTransferSupport { Paste() {} /** Defines array of classes implementing paste for specified clipboard operation. * @param op clopboard operation to specify paste types for * @return array of classes extending PasteTypeExt class */ protected PasteTypeExt[] definePasteTypes (int op) { switch (op) { case LoaderTransfer.CLIPBOARD_CUT: return new PasteTypeExt [] { new PasteTypeExt () { public String getName () { return DataObject.getString ("PT_move"); // NOI18N } public HelpCtx getHelpCtx () { return new HelpCtx (Paste.class.getName () + ".move"); // NOI18N } protected boolean handleCanPaste (DataObject obj) { return obj.isMoveAllowed () && !isParent (getPrimaryFile (), obj.getPrimaryFile ()); } protected void handlePaste (DataObject obj) throws IOException { obj.move (DataFolder.this); } /** Cleans clipboard after paste. Overrides superclass method. */ protected boolean cleanClipboard() { return true; } /** Check if one file object has another as a parent. * @param fo the file object to check * @param parent * @return true if parent is fo's (indirect) parent */ /*not private called from FolderNode*/ private boolean isParent (FileObject fo, FileObject parent) { File parentFile = FileUtil.toFile(parent); File foFile = FileUtil.toFile(fo); if (foFile != null && parentFile != null) { return isParentFile(foFile, parentFile); } try { if (fo.getFileSystem () != parent.getFileSystem ()) { return false; } } catch (IOException ex) { } while (fo != null) { if (fo.equals (parent)) { return true; } fo = fo.getParent (); } return false; } } }; case LoaderTransfer.CLIPBOARD_COPY: return new PasteTypeExt [] { new PasteTypeExt () { public String getName () { return DataObject.getString ("PT_copy"); // NOI18N } public HelpCtx getHelpCtx () { return new HelpCtx (Paste.class.getName () + ".copy"); // NOI18N } protected boolean handleCanPaste (DataObject obj) { return obj.isCopyAllowed (); } protected void handlePaste (DataObject obj) throws IOException { saveIfModified(obj); obj.copy (DataFolder.this); } private void saveIfModified(DataObject obj) throws IOException { if (obj.isModified()) { SaveCookie sc = (SaveCookie)obj.getCookie(SaveCookie.class); if (sc != null) { sc.save(); } } } }, new PasteTypeExt () { public String getName () { return DataObject.getString ("PT_instantiate"); // NOI18N } public HelpCtx getHelpCtx () { return new HelpCtx (Paste.class.getName () + ".instantiate"); // NOI18N } protected boolean handleCanPaste (DataObject obj) { return obj.isTemplate (); } protected void handlePaste (DataObject obj) throws IOException { obj.createFromTemplate (DataFolder.this); } }, new PasteTypeExt () { public String getName () { return DataObject.getString ("PT_shadow"); // NOI18N } public HelpCtx getHelpCtx () { return new HelpCtx (Paste.class.getName () + ".shadow"); // NOI18N } protected boolean handleCanPaste (DataObject obj) { // #42888 - disable "Create as Link" action on non-SystemFileSystem try { if (!DataFolder.this.getPrimaryFile().getFileSystem().equals( Repository.getDefault().getDefaultFileSystem())) { return false; } } catch (FileStateInvalidException ex) { // something wrong. disable. return false; } return obj.isShadowAllowed (); } protected void handlePaste (DataObject obj) throws IOException { obj.createShadow (DataFolder.this); } } }; } return new PasteTypeExt [] {}; } private boolean isParentFile(File foFile, File parentFile) { boolean retVal = false; while (foFile != null) { if (foFile.equals (parentFile)) { retVal = true; break; } foFile = foFile.getParentFile (); } return retVal; } /** Defines array of data clipboard operations recognized by this paste support. * @return array of DataFlavors */ protected int [] defineOperations () { return new int [] { LoaderTransfer.CLIPBOARD_CUT, LoaderTransfer.CLIPBOARD_COPY }; } protected void handleCreatePasteTypes (Transferable t, java.util.List s) { // These should only accept single-node transfers, since they require dialogs. Node node = NodeTransfer.node (t, NodeTransfer.CLIPBOARD_COPY); // lastly try special cookies if (node != null) { try { InstanceCookie cookie = (InstanceCookie)node.getCookie (InstanceCookie.class); if (cookie != null && java.io.Serializable.class.isAssignableFrom (cookie.instanceClass ())) { s.add (new DataTransferSupport.SerializePaste (DataFolder.this, cookie)); s.add (new DataTransferSupport.InstantiatePaste (DataFolder.this, cookie)); } } catch (IOException e) { } catch (ClassNotFoundException e) { } } } } /** Listener on changes in FolderList that delegates to our PCL. */ private final class ListPCL extends Object implements PropertyChangeListener { ListPCL() {} public void propertyChange(java.beans.PropertyChangeEvent ev) { if (this == DataFolder.this.pcl) { // if I am still folder's correct listener DataFolder.this.firePropertyChange (PROP_CHILDREN, null, null); } } } }

... 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.