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

What this is

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

Other links

The source code

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

package org.openide.loaders;

import java.lang.ref.WeakReference;
import java.io.*;
import java.util.*;
import java.beans.PropertyVetoException;

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

import org.openide.*;
import org.openide.filesystems.*;
import org.openide.util.HelpCtx;
import org.openide.util.RequestProcessor;
import org.openide.nodes.Node;
import org.openide.nodes.CookieSet;

/** Provides support for handling of data objects with multiple files.
* One file is represented by one {@link Entry}. Each handler
* has one {@link #getPrimaryEntry primary} entry and zero or more secondary entries.
*
* @author Ales Novak, Jaroslav Tulach, Ian Formanek
*/
public class MultiDataObject extends DataObject {
    /** generated Serialized Version UID */
    static final long serialVersionUID = -7750146802134210308L;

    /** Synchronization object used in getCookieSet and setCookieSet methods.
     */
    private static final Object cookieSetLock = new Object();
    
    /** Lock used for lazy creation of secondary field (in method getSecondary()) */
    private static final Object secondaryCreationLock = new Object();
    
    /** A RequestProceccor used for firing property changes asynchronously */
    private static final RequestProcessor firingProcessor =
		new RequestProcessor( "MDO PropertyChange processor");
    
    /** A RequestProceccor used for waiting for finishing refresh */
    private static final RequestProcessor delayProcessor =
		new RequestProcessor( "MDO Firing delayer");
    /** a task waiting for the FolderList task to finish scanning of the folder */
    private RequestProcessor.Task delayedPropFilesTask;
    /** lock used in firePropFilesAfterFinishing */
    private static final Object delayedPropFilesLock = new Object();
    
    /** getPrimaryEntry() is intended to have all inetligence for copy/move/... */
    private Entry primary;

    /** Map of secondary entries and its files. (FileObject, Entry) */
    private HashMap secondary;

    /** array of cookies for this object */
    private CookieSet cookieSet;

    /** flag when to call checkFiles(this) */
    boolean checked = false;

    /** Create a MultiFileObject.
    * @see DataObject#DataObject(org.openide.filesystems.FileObject,org.openide.loaders.DataLoader)
    * @param fo the primary file object
    * @param loader loader of this data object
    */
    public MultiDataObject(FileObject fo, MultiFileLoader loader) throws DataObjectExistsException {
        super(fo, loader);        
        primary = createPrimaryEntry (this, getPrimaryFile ());
    }

    /** This constructor is added for backward compatibility, MultiDataObject should be
    * properly constructed using the MultiFileLoader.
    * @param fo the primary file object
    * @param loader loader of this data object
    * @deprecated do not use this constructor, it is for backward compatibility of 
    * {@link #DataShadow} and {@link #DataFolder} only
    * @since 1.13
    */
    MultiDataObject(FileObject fo, DataLoader loader) throws DataObjectExistsException {
        super(fo, loader);
        primary = createPrimaryEntry (this, getPrimaryFile ());
    }
    
    /** Getter for the multi file loader that created this
    * object.
    *
    * @return the multi loader for the object
    */
    public final MultiFileLoader getMultiFileLoader () {
        DataLoader loader = getLoader ();
        
        if (!(loader instanceof MultiFileLoader))
            return null;
        
        return (MultiFileLoader)loader;
    }

    /* Method to access all FileObjects used by this DataObject.
    * These file objects should have set the important flag to
    * allow the requester to distingush between important and
    * unimportant files.
    *
    * @return set of FileObjects
    */
    public Set files () {
        // move lazy initialization to FilesSet
        return new FilesSet (this);
    }

    /* Getter for delete action.
    * @return true if the object can be deleted
    */
    public boolean isDeleteAllowed() {
        return !getPrimaryFile ().isReadOnly () && !existReadOnlySecondary();
    }
    
    private boolean existReadOnlySecondary() {
        synchronized ( synchObjectSecondary() ) {
            Iterator it = getSecondary().keySet().iterator();
            while (it.hasNext())
                if ( ((FileObject)it.next()).isReadOnly() )
                    return true;                
        }
        return false;
    }

    /** Performs checks by calling checkFiles
     * @return getSecondary() method result
     */
    private HashMap checkSecondary () {
        // enumeration of all files
        if (! checked) {
            checkFiles (this);
            checked = true;
        }
        return getSecondary();
    }
        
    /** Lazy getter for secondary property
     * @return secondary object
     */
    /* package-private */ HashMap getSecondary() {
        synchronized (secondaryCreationLock) {
            if (secondary == null) {
                secondary = new HashMap (11);
            }
            return secondary;
        }
    }
    
    /* 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 !getPrimaryFile ().isReadOnly () && !existReadOnlySecondary();
    }

    /* Getter for rename action.
    * @return true if the object can be renamed
    */
    public boolean isRenameAllowed () {
        return !getPrimaryFile ().isReadOnly () && !existReadOnlySecondary();
    }

    /* Help context for this object.
    * @return help context
    */
    public HelpCtx getHelpCtx() {
        return HelpCtx.DEFAULT_HELP;
    }

    /** Provide object used for synchronization of methods working with 
     * Secondaries.
     * @return The private field secondary.
     */
    Object synchObjectSecondary() {
        Object lock = checkSecondary();
        if (lock == null) throw new IllegalStateException("checkSecondary was null from " + this); // NOI18N
        return checkSecondary();
    }
    
    /** Provides node that should represent this data object.
    *
    * @return the node representation
    * @see DataNode
    */
    protected Node createNodeDelegate () {
        DataNode dataNode = (DataNode) super.createNodeDelegate ();
        return dataNode;
    }

    /** Add a new secondary entry to the list.
    * @param fe the entry to add
    */
    protected final void addSecondaryEntry (Entry fe) {
        synchronized ( getSecondary() ) {
            getSecondary().put (fe.getFile (), fe);            
        }

        // Fire PROP_FILES only if we have actually finished making the folder.
        // It is dumb to fire this if we do not yet even know what all of our
        // initial secondary files are going to be.
        FolderList l = getFolderList();
        if (l == null) {
            firePropertyChangeLater (PROP_FILES, null, null);
        } else { // l != null
            if (l.isCreated()) {
                firePropertyChangeLater (PROP_FILES, null, null);
            } else {
                firePropFilesAfterFinishing();
            }
        }
    }

    /** Finds FolderList object for the primary file's parent folder
     * @return FolderList object or null
     */
    private FolderList getFolderList() {
        FileObject parent = primary.file.getParent();
        if (parent != null) {
            return FolderList.find(parent, false);
        }
        return null;
    }
    
    /** Remove a secondary entry from the list.
     * @param fe the entry to remove
    */
    protected final void removeSecondaryEntry (Entry fe) {
        synchronized (getSecondary()) {
            getSecondary().remove (fe.getFile ());
        }
        
        firePropertyChangeLater (PROP_FILES, null, null);

        if (fe.isImportant ()) {
            checkConsistency(this);
        }
    }

    /** All secondary entries are recognized. Called from multi file object.
    * @param recognized object to mark recognized file to
    */
    final void markSecondaryEntriesRecognized (DataLoader.RecognizedFiles recognized) {
        synchronized (getSecondary()) {
            Iterator it = getSecondary().keySet ().iterator ();
            while (it.hasNext ()) {
                FileObject fo=(FileObject)it.next ();
                recognized.markRecognized (fo);
            }
        }
    }


    /** Tests whether this file is between entries and if not,
    * creates a secondary entry for it and adds it into set of
    * secondary entries.
    * 

* This method should be used in constructor of MultiDataObject to * register all the important files, that could belong to this data object. * As example, our XMLDataObject, tries to locate its xmlinfo * file and then do register it * * @param fo the file to register (can be null, then the action is ignored) * @return the entry associated to this file object (returns primary entry if the fo is null) */ protected final Entry registerEntry (FileObject fo) { synchronized (getSecondary()) { if (fo == null) { // is it ok, to do this or somebody would like to see different behavour? return primary; } if (fo.equals (getPrimaryFile ())) { return primary; } Entry e = (Entry)getSecondary().get (fo); if (e != null) { return e; } // add it into set of entries e = createSecondaryEntry (this, fo); addSecondaryEntry (e); return e; } } /** Removes the entry from the set of secondary entries. * Called from the notifyFileDeleted */ final void removeFile (FileObject fo) { synchronized (getSecondary()) { Entry e = (Entry)getSecondary().get (fo); if (e != null) { removeSecondaryEntry (e); } } } /** Get the primary entry. * @return the entry */ public final Entry getPrimaryEntry () { return primary; } /** Get secondary entries. * @return immutable set of {@link Entry}s */ public final Set secondaryEntries () { synchronized ( synchObjectSecondary() ) { removeAllInvalid (); return new HashSet (getSecondary().values ()); } } /** For a given file, find the associated secondary entry. * @param fo file object * @return the entry associated with the file object, or null if there is no * such entry */ public final Entry findSecondaryEntry (FileObject fo) { Entry e; synchronized ( synchObjectSecondary() ) { e = (Entry)getSecondary().get (fo); } return e; } /** Removes all FileObjects that are not isValid from the * set of objects. */ /* package-private */ void removeAllInvalid () { Iterator it = checkSecondary ().entrySet ().iterator (); while (it.hasNext ()) { Map.Entry e = (Map.Entry)it.next (); FileObject fo = (FileObject)e.getKey (); if (!fo.isValid ()) { it.remove (); firePropertyChangeLater (PROP_FILES, null, null); } } } //methods overriding DataObjectHandler's abstract methods /* Obtains lock for primary file by asking getPrimaryEntry() entry. * * @return the lock for primary file * @exception IOException if it is not possible to set the template * state. */ protected FileLock takePrimaryFileLock () throws IOException { return getPrimaryEntry ().takeLock (); } // XXX does nothing of the sort --jglick /** Check if in specific folder exists fileobject with the same name. * If it exists user is asked for confirmation to rewrite, rename or cancel operation. * @param folder destination folder * @return the suffix which should be added to the name or null if operation is cancelled */ private String existInFolder(FileObject fo, FileObject folder) { // merge folders when neccessary if (fo.isFolder () && isMergingFolders ()) return ""; // NOI18N String orig = fo.getName (); String name = FileUtil.findFreeFileName( folder, orig, fo.getExt () ); if (name.length () <= orig.length ()) { return ""; // NOI18N } else { return name.substring (orig.length ()); } } /** Override to change default handling of name collisions detected during the * copy, move operations. Reasonable for MultiDataObjects having folder their * primary file (e.g. DataFolder, CompoundDataObject). * @return false means, that new folder name should be synthetized when * the same folder already exists in the target location of copy, move operation, otherwise * existing falder will be used. Default implementation returns false. */ boolean isMergingFolders() { return false; } /** Copies primary and secondary files to new folder. * May ask for user confirmation before overwriting. * @param df the new folder * @return data object for the new primary * @throws IOException if there was a problem copying * @throws UserCancelException if the user cancelled the copy */ protected DataObject handleCopy (DataFolder df) throws IOException { FileObject fo; String suffix = existInFolder( getPrimaryEntry().getFile(), df.getPrimaryFile () ); if (suffix == null) throw new org.openide.util.UserCancelException(); Iterator it = secondaryEntries().iterator(); while (it.hasNext ()) { ((Entry)it.next()).copy (df.getPrimaryFile (), suffix); } //#33244 - copy primary file after the secondary ones fo = getPrimaryEntry ().copy (df.getPrimaryFile (), suffix); try { return createMultiObject (fo); } catch (DataObjectExistsException ex) { return ex.getDataObject (); } } /* Deletes all secondary entries, removes them from the set of * secondary entries and then deletes the getPrimaryEntry() entry. */ protected void handleDelete() throws IOException { ArrayList toRemove = new ArrayList(); Iterator it; synchronized ( synchObjectSecondary() ) { it = new ArrayList(getSecondary().entrySet ()).iterator(); } while (it.hasNext ()) { Map.Entry e = (Map.Entry)it.next (); ((Entry)e.getValue ()).delete (); toRemove.add(e.getKey()); } synchronized ( synchObjectSecondary() ) { Object[] objects = toRemove.toArray(); for (int i = 0; i < objects.length; i++) getSecondary().remove(objects[i]); } getPrimaryEntry().delete(); } /* Renames all entries and changes their files to new ones. */ protected FileObject handleRename (String name) throws IOException { getPrimaryEntry ().changeFile (getPrimaryEntry().rename (name)); HashMap add = null; ArrayList toRemove = new ArrayList(); Iterator it; synchronized ( synchObjectSecondary() ) { it = new ArrayList(getSecondary().entrySet ()).iterator(); } while (it.hasNext ()) { Map.Entry e = (Map.Entry)it.next (); FileObject fo = ((Entry)e.getValue ()).rename (name); if (fo == null) { // remove the entry toRemove.add (e.getKey()); } else { if (!fo.equals (e.getKey ())) { // put the new one into change table if (add == null) add = new HashMap (); Entry entry = (Entry)e.getValue (); entry.changeFile (fo); // using getFile to let the entry correctly annotate // the file by isImportant flag add.put (entry.getFile (), entry); // changed the file => remove the file toRemove.add(e.getKey()); } } } // if there has been a change in files, apply it if ((add != null) || (!toRemove.isEmpty())) { synchronized ( synchObjectSecondary() ) { // remove entries if (!toRemove.isEmpty()) { Object[] objects = toRemove.toArray(); for (int i = 0; i < objects.length; i++) getSecondary().remove(objects[i]); } // add entries if (add != null) { getSecondary().putAll (add); } } firePropertyChangeLater (PROP_FILES, null, null); } return getPrimaryEntry ().getFile (); } /** Moves primary and secondary files to a new folder. * May ask for user confirmation before overwriting. * @param df the new folder * @return the moved primary file object * @throws IOException if there was a problem moving * @throws UserCancelException if the user cancelled the move */ protected FileObject handleMove (DataFolder df) throws IOException { String suffix = existInFolder(getPrimaryEntry().getFile(), df.getPrimaryFile ()); if (suffix == null) throw new org.openide.util.UserCancelException(); List backup = saveEntries(); try { getPrimaryEntry ().changeFile (getPrimaryEntry ().move (df.getPrimaryFile (), suffix)); HashMap add = null; ArrayList toRemove = new ArrayList(); Iterator it; synchronized ( synchObjectSecondary() ) { it = new ArrayList(getSecondary().entrySet ()).iterator(); } while (it.hasNext ()) { Map.Entry e = (Map.Entry)it.next (); FileObject fo = ((Entry)e.getValue ()).move (df.getPrimaryFile (), suffix); if (fo == null) { // remove the entry toRemove.add(e.getKey()); } else { if (!fo.equals (e.getKey ())) { // put the new one into change table if (add == null) add = new HashMap (); Entry entry = (Entry)e.getValue (); entry.changeFile (fo); // using entry.getFile, so the file has correctly // associated its isImportant flag add.put (entry.getFile (), entry); // changed the file => remove the file toRemove.add(e.getKey()); } } } // if there has been a change in files, apply it if ((add != null) || (!toRemove.isEmpty())) { synchronized ( synchObjectSecondary() ) { // remove entries if (!toRemove.isEmpty()) { Object[] objects = toRemove.toArray(); for (int i = 0; i < objects.length; i++) getSecondary().remove(objects[i]); } // add entries if (add != null) { getSecondary().putAll (add); } } firePropertyChangeLater (PROP_FILES, null, null); } return getPrimaryEntry ().getFile (); } catch (IOException e) { restoreEntries(backup); throw e; } } /* Creates new object from template. * @exception IOException */ protected DataObject handleCreateFromTemplate ( DataFolder df, String name ) throws IOException { FileObject fo; if (name == null) { name = FileUtil.findFreeFileName( df.getPrimaryFile (), getPrimaryFile ().getName (), getPrimaryFile ().getExt () ); } fo = getPrimaryEntry().createFromTemplate (df.getPrimaryFile (), name); Iterator it = secondaryEntries().iterator(); while (it.hasNext ()) { ((Entry)it.next()).createFromTemplate (df.getPrimaryFile (), name); } try { return createMultiObject (fo); } catch (DataObjectExistsException ex) { return ex.getDataObject (); } } /** Set the set of cookies. * To the provided cookie set a listener is attached, * and any change to the set is propagated by * firing a change on {@link #PROP_COOKIE}. * * @param s the cookie set to use * @deprecated just use getCookieSet().add(...) instead */ protected final void setCookieSet (CookieSet s) { setCookieSet(s, true); } /** Set the set of cookies. * * @param s the cookie set to use * @param fireChange used when called from getter. In this case event shouldn't * be fired. */ private void setCookieSet (CookieSet s, boolean fireChange) { synchronized (cookieSetLock) { ChangeListener ch = getChangeListener(); if (cookieSet != null) { cookieSet.removeChangeListener (ch); } s.addChangeListener (ch); cookieSet = s; } if (fireChange) { fireCookieChange (); } } /** Get the set of cookies. * If the set had been * previously set by {@link #setCookieSet}, that set * is returned. Otherwise an empty set is * returned. * * @return the cookie set (never null) */ protected final CookieSet getCookieSet () { CookieSet s = cookieSet; if (s != null) return s; synchronized (cookieSetLock) { if (cookieSet != null) return cookieSet; // sets empty sheet and adds a listener to it setCookieSet (new CookieSet (), false); return cookieSet; } } /** Look for a cookie in the current cookie set matching the requested class. * * @param type the class to look for * @return an instance of that class, or null if this class of cookie * is not supported */ public Node.Cookie getCookie (Class type) { CookieSet c = cookieSet; if (c != null) { Node.Cookie cookie = c.getCookie (type); if (cookie != null) return cookie; } return super.getCookie (type); } /** Fires cookie change. */ final void fireCookieChange () { firePropertyChange (PROP_COOKIE, null, null); } /** Fires property change but in event thread. */ private void firePropertyChangeLater ( final String name, final Object oldV, final Object newV ) { firingProcessor.post(new Runnable () { public void run () { firePropertyChange (name, oldV, newV); } }); } /** * Posts a task to delayProcessor such that task * 1. waits for the FolderList to finish * 2. calls firePropertyChangeLater with PROP_FILES * Second time this method is called (delayedPropFilesTask is not null) * the new task is not created - the old one is rescheduled to run again. * * NOTE: this method should be improved not to fire twice in some cases. */ private void firePropFilesAfterFinishing() { synchronized (delayedPropFilesLock) { if (delayedPropFilesTask == null) { delayedPropFilesTask = delayProcessor.post(new Runnable() { public void run() { FolderList l = getFolderList(); if (l != null) { l.waitProcessingFinished(); } firePropertyChangeLater(PROP_FILES, null, null); } }); } else { delayedPropFilesTask.schedule(0); } } } /** sets checked to true */ final void recognizedByFolder() { checked = true; } private ChangeListener chLis; final ChangeListener getChangeListener() { if (chLis == null) { chLis = new ChangeListener() { /** State changed */ public void stateChanged (ChangeEvent ev) { fireCookieChange (); } }; } return chLis; } // -- Following methods were added in order to wrap calls to MultiFileLoader // and check if the loader is really of this type. This hack was added to // keep backward compatibility of DataFolder and DataShadow classes, which // were originally subclassing DataObject, but was changed to subclass // MultiDataObject. Methods can be removed as the deprecated constructor // MultiDataObject(FileObject, DataLoader) disappears. private final MultiDataObject.Entry createPrimaryEntry(MultiDataObject obj, FileObject fo) { MultiFileLoader loader = getMultiFileLoader (); if (loader != null) return loader.createPrimaryEntry (obj, fo); Entry e; if (fo.isFolder ()) e = new FileEntry.Folder(obj, fo); else e = new FileEntry (obj, fo); return e; } private final MultiDataObject.Entry createSecondaryEntry(MultiDataObject obj, FileObject fo) { MultiFileLoader loader = getMultiFileLoader (); if (loader != null) return loader.createSecondaryEntryImpl (obj, fo); Entry e; if (fo.isFolder ()) e = new FileEntry.Folder(obj, fo); else e = new FileEntry (obj, fo); return e; } private final MultiDataObject createMultiObject(FileObject fo) throws DataObjectExistsException, IOException { MultiFileLoader loader = getMultiFileLoader (); MultiDataObject obj; if (loader != null) { obj = DataObjectPool.createMultiObject(loader, fo); } else { obj = (MultiDataObject)getLoader ().findDataObject (fo, RECOGNIZER); } return obj; } private final void checkConsistency (MultiDataObject obj) { MultiFileLoader loader = getMultiFileLoader (); if (loader != null) loader.checkConsistency (obj); } private final void checkFiles (MultiDataObject obj) { MultiFileLoader loader = getMultiFileLoader (); if (loader != null) loader.checkFiles (obj); } private static EmptyRecognizer RECOGNIZER = new EmptyRecognizer(); private static class EmptyRecognizer implements DataLoader.RecognizedFiles { EmptyRecognizer() {} public void markRecognized (FileObject fo) { } } // End of compatibility hack. --^ /** Save pairs Entry <-> Entry.getFile () in the list * @return list of saved pairs */ final List saveEntries() { synchronized ( synchObjectSecondary() ) { LinkedList ll = new LinkedList(); Iterator it = secondaryEntries ().iterator (); ll.add (new Pair(getPrimaryEntry ())); while (it.hasNext ()) { MultiDataObject.Entry en = (MultiDataObject.Entry)it.next (); ll.add (new Pair(en)); } return ll; } } /** Restore entries from the list. If Entry.getFile () has changed from * time when backup list was created, original file is restored and * Entry is re-assigned to it. * @param backup list obtained from {@link #saveEntries ()} function */ final void restoreEntries(List backup) { Iterator it = backup.iterator (); while (it.hasNext ()) { Pair p = (Pair)it.next (); if (p.entry.getFile ().equals (p.file)) continue; if (p.file.isValid()) { p.entry.changeFile (p.file); } else { // copy back try { if (p.entry.getFile ().isData ()) p.entry.changeFile (p.entry.getFile ().copy (p.file.getParent (), p.file.getName (), p.file.getExt ())); else { FileObject fo = p.file.getParent ().createFolder (p.file.getName ()); FileUtil.copyAttributes (p.entry.getFile (), fo); p.entry.changeFile (fo); } } catch (IOException e) { // should not occure } } } } private final static class Pair { MultiDataObject.Entry entry; FileObject file; Pair(MultiDataObject.Entry e) { entry = e; file = e.getFile (); } } /** Represents one file in a {@link MultiDataObject group data object}. */ public abstract class Entry implements java.io.Serializable { /** generated Serialized Version UID */ static final long serialVersionUID = 6024795908818133571L; /** modified from MultiDataObject operations, that is why it is package * private. Do not assign anything to this object, use changeFile method */ private FileObject file; /** This factory is used for creating new clones of the holding lock for internal * use of this DataObject. It factory is null it means that the file entry is not */ private transient WeakReference lock; protected Entry (FileObject file) { this.file = file; if (!isImportant()) { file.setImportant(false); } } /** A method to change the entry file to some else. * @param newFile */ final void changeFile (FileObject newFile) { if (newFile.equals (file)) { return; } newFile.setImportant (isImportant ()); this.file = newFile; // release lock for old file FileLock l = lock == null ? null : (FileLock)lock.get (); if (l != null && l.isValid ()) { l.releaseLock (); } lock = null; } /** Get the file this entry works with. */ public final FileObject getFile () { return file; } /** Get the multi data object this entry is assigned to. * @return the data object */ public final MultiDataObject getDataObject () { return MultiDataObject.this; } /** Method that allows to check whether an entry is important or is not. * Should be overriden by subclasses, the current implementation returns * true. * * @return true if this entry is important or false if not */ public boolean isImportant () { return true; } /** Called when the entry is to be copied. * Depending on the entry type, it should either copy the underlying FileObject, * or do nothing (if it cannot be copied). * @param f the folder to create this entry in * @param suffix the suffix to add to the name of original file * @return the copied FileObject or null if it cannot be copied * @exception IOException when the operation fails */ public abstract FileObject copy (FileObject f, String suffix) throws IOException; /** Called when the entry is to be renamed. * Depending on the entry type, it should either rename the underlying FileObject, * or delete it (if it cannot be renamed). * @param name the new name * @return the renamed FileObject or null if it has been deleted * @exception IOException when the operation fails */ public abstract FileObject rename (String name) throws IOException; /** Called when the entry is to be moved. * Depending on the entry type, it should either move the underlying FileObject, * or delete it (if it cannot be moved). * @param f the folder to move this entry to * @param suffix the suffix to use * @return the moved FileObject or null if it has been deleted * @exception IOException when the operation fails */ public abstract FileObject move (FileObject f, String suffix) throws IOException; /** Called when the entry is to be deleted. * @exception IOException when the operation fails */ public abstract void delete () throws IOException; /** Called when the entry is to be created from a template. * Depending on the entry type, it should either copy the underlying FileObject, * or do nothing (if it cannot be copied). * @param f the folder to create this entry in * @param name the new name to use * @return the copied FileObject or null if it cannot be copied * @exception IOException when the operation fails */ public abstract FileObject createFromTemplate (FileObject f, String name) throws IOException; /** Try to lock this file entry. * @return the lock if the operation was successful; otherwise null * @throws IOException if the lock could not be taken */ public FileLock takeLock() throws IOException { FileLock l = lock == null ? null : (FileLock)lock.get (); if (l == null || !l.isValid ()){ l = getFile ().lock (); lock = new WeakReference (l); } return l; } /** Tests whether the entry is locked. * @return true if so */ public boolean isLocked() { FileLock l = lock == null ? null : (FileLock)lock.get (); return l != null && l.isValid (); } public boolean equals(Object o) { if (! (o instanceof Entry)) return false; return getFile ().equals(((Entry) o).getFile ()); } public int hashCode() { return getFile ().hashCode(); } /** Make a Serialization replacement. * The entry is identified by the * file object is holds. When serialized, it stores the * file object and the data object. On deserialization * it finds the data object and creates the right entry * for it. */ protected Object writeReplace () { return new EntryReplace (getFile ()); } } void notifyFileDeleted (FileEvent fe) { removeFile (fe.getFile ()); if (fe.getFile ().equals (getPrimaryFile ())) { try { MultiDataObject.this.markInvalid0 (); } catch (PropertyVetoException ex) { // silently ignore? ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); } } } /** Fired when a file has been added to the same folder * @param fe the event describing context where action has taken place */ void notifyFileDataCreated(FileEvent fe) { checked = false; } /** Entry replace. */ private static final class EntryReplace extends Object implements java.io.Serializable { /** generated Serialized Version UID */ static final long serialVersionUID = -1498798537289529182L; /** file object of the entry */ private FileObject file; /** entry to be used during read */ private transient Entry entry; public EntryReplace (FileObject fo) { file = fo; } private void readObject (ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject (); try { DataObject obj = DataObject.find (file); if (obj instanceof MultiDataObject) { MultiDataObject m = (MultiDataObject)obj; if (file.equals (m.getPrimaryFile ())) { // primary entry entry = m.getPrimaryEntry (); } else { // secondary entry Entry e = (Entry)m.findSecondaryEntry (file); if (e == null) { throw new InvalidObjectException (obj.toString ()); } // remember the entry entry = e; } } } catch (DataObjectNotFoundException ex) { throw new InvalidObjectException (ex.getMessage ()); } } public Object readResolve () { return entry; } } }

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