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

org.openide.loaders.DataShadow Maven / Gradle / Ivy

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

package org.openide.loaders;

import java.awt.datatransfer.Transferable;
import java.awt.Image;
import java.beans.*;
import java.io.*;
import java.text.MessageFormat;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.net.URL;
import java.util.*;

import org.openide.filesystems.*;
import org.openide.filesystems.FileSystem;
import org.openide.util.NbBundle;
import org.openide.nodes.*;
import org.openide.util.HelpCtx;
import org.openide.ErrorManager;
import org.openide.util.datatransfer.ExTransferable;
import org.openide.util.Mutex;
import org.openide.util.MutexException;

/** Default implementation of a shortcut to another data object.
* Since 1.13 it extends MultiDataObject.
* @author Jan Jancura, Jaroslav Tulach
*/
public class DataShadow extends MultiDataObject implements DataObject.Container {
    /** generated Serialized Version UID */
    static final long serialVersionUID = 6305590675982925167L;

    /** original data object */
    private DataObject original;
    /** Listener attached to original DataObject. */
    private OrigL origL = null;
    /** List of nodes created for the DataShadow. */
    private LinkedList nodes = new LinkedList ();

    /** Extension name. */
    static final String SHADOW_EXTENSION = "shadow"; // NOI18N
    
    /** Map of all >. Where the file object
     Is the original file */
    private static Map allDataShadows;
    /** ReferenceQueue for collected DataShadows */
    private static ReferenceQueue rqueue;
    
    private static Mutex MUTEX = new Mutex ();

    /** Getter for the Set that contains all DataShadows. */
    private static synchronized Map getDataShadowsSet() {
        if (allDataShadows == null) {
            allDataShadows = new HashMap();
        }
        return allDataShadows;
    }
    
    /** Getter for the ReferenceQueue that contains WeakReferences
     * for discarded DataShadows
     */
    private static synchronized ReferenceQueue getRqueue() {
        if (rqueue == null) {
            rqueue = new ReferenceQueue();
        }
        return rqueue;
    }
    
    /** Removes WeakReference of collected DataShadows. */
    private static void checkQueue() {
        if (rqueue == null) {
            return;
        }
        
        Reference ref = rqueue.poll();
        while (ref != null) {
            synchronized (getDataShadowsSet ()) {
                getDataShadowsSet().remove(ref);
            }
            ref = rqueue.poll();
        }
    }
    
    /** Creates WeakReference for given DataShadow */
    static Reference createReference(DataObject ds, ReferenceQueue q) {
        return new DSWeakReference(ds, q);
    }
    
    private static synchronized void enqueueDataShadow(DataShadow ds) {
        checkQueue();
        Map m = getDataShadowsSet ();
        
        FileObject prim = ds.getOriginal ().getPrimaryFile ();
        Reference ref = createReference(ds, getRqueue());
        Set s = (Set)m.get (prim);
        if (s == null) {
            s = Collections.singleton (ref);
            getDataShadowsSet ().put (prim, s);
        } else {
            if (! (s instanceof HashSet)) {
                s = new HashSet (s);
                getDataShadowsSet ().put (prim, s);
            }
            s.add (ref);
        }
    }

    /** @return all active DataShadows or null */
    private static synchronized List getAllDataShadows() {
        if (allDataShadows == null || allDataShadows.isEmpty()) {
            return null;
        }
        
        List ret = new ArrayList(allDataShadows.size());
        Iterator it = allDataShadows.values ().iterator();
        while (it.hasNext()) {
            Set ref = (Set) it.next();
            Iterator refs = ref.iterator ();
            while (refs.hasNext ()) {
                Reference r = (Reference)refs.next ();
                DataShadow shadow = (DataShadow)r.get ();
                if (shadow != null) {
                    ret.add (shadow);
                }
            }
        }
        
        return ret;
    }
    
    /** Checks whether a change of the given dataObject
     * does not hurt validity of a DataShadow
     */
    static void checkValidity(EventObject ev) {
        DataObject src = null;
        if (ev instanceof OperationEvent) {
            src = ((OperationEvent)ev).getObject();
        }

        Set shadows = null;
        synchronized (DataShadow.class) {
            if (allDataShadows == null || allDataShadows.isEmpty ()) return;
            
            if (src != null) {
                shadows = (Set)allDataShadows.get (src.getPrimaryFile ());
                if (shadows == null) {
                    // we know the source of the event and there are no
                    // shadows with such original
                    return;
                }
            }
        }
        
        DataObject changed = null;
        OperationEvent.Copy c;
        if (
            ev instanceof OperationEvent.Rename
            ||
            ev instanceof OperationEvent.Move
        ) {
            changed = ((OperationEvent)ev).getObject();
        }
        
        if (shadows != null) {
            //
            // optimized for speed, we have found the shadow(s) that
            // belong to this FileObject
            //
            Iterator refs = shadows.iterator ();
            while (refs.hasNext ()) {
                Reference r = (Reference)refs.next ();
                DataShadow shadow = (DataShadow)r.get ();
                if (shadow != null) {
                    shadow.refresh (shadow.getOriginal () == changed);
                }
            }
            return;
        }
        
        List all = getAllDataShadows();
        if (all == null) {
            return;
        }
        
        
        int size = all.size();
        for (int i = 0; i < size; i++) {
            DataShadow obj = (DataShadow)all.get(i);
            // if original was renamed or moved update 
            // the file with the link
            obj.refresh (obj.getOriginal () == changed);
        }
    }
    
    /** Constructs new data shadow for given primary file and referenced original.
    * Method to allow subclasses of data shadow.
    *
    * @param fo the primary file
    * @param original original data object
    * @param loader the loader that created the object
    */
    protected DataShadow (
        FileObject fo, DataObject original, MultiFileLoader loader
    ) throws DataObjectExistsException {
        super (fo, loader);
        init(original);
    }

    /** Constructs new data shadow for given primary file and referenced original.
    * Method to allow subclasses of data shadow.
    *
    * @param fo the primary file
    * @param original original data object
    * @param loader the loader that created the object
    * @deprecated Since 1.13 do not use this constructor, it is for backward compatibility only
    */
    protected DataShadow (
        FileObject fo, DataObject original, DataLoader loader
    ) throws DataObjectExistsException {
        super (fo, loader);
        init(original);
    }
    
    /** Perform initialization after construction.
    * @param original original data object
    */
    private void init(DataObject original) {
        if (original == null)
            throw new IllegalArgumentException();
        setOriginal (original);
        enqueueDataShadow(this);
    }
    
    /** Constructs new data shadow for given primary file and referenced original.
    * @param fo the primary file
    * @param original original data object
    */
    private DataShadow (FileObject fo, DataObject original) throws DataObjectExistsException {
        this (fo, original, (MultiFileLoader)DataLoaderPool.getShadowLoader ());
    }

    /** Method that creates new data shadow in a folder. The name chosen is based
    * on the name of the original object.
    *
    * @param folder target folder to create data in
    * @param original orignal object that should be represented by the shadow
    */
    public static DataShadow create (DataFolder folder, DataObject original)
    throws IOException {
        return create (folder, null, original, SHADOW_EXTENSION);
    }

    /** Method that creates new data shadow in a folder. The default extension
    * is used.
    *
    * @param folder target folder to create data in
    * @param name name to give to the shadow
    * @param original object that should be represented by the shadow
    */
    public static DataShadow create (
        DataFolder folder,
        final String name,
        final DataObject original
    ) throws IOException {
        return create (folder, name, original, SHADOW_EXTENSION);
    }
    
    /** Method that creates new data shadow in a folder. All modifications are
    * done atomicly using {@link FileSystem#runAtomicAction}.
    *
    * @param folder target folder to create data in
    * @param name name to give to the shadow
    * @param original orignal object that should be represented by the shadow
    */
    public static DataShadow create (
        DataFolder folder,
        final String name,
        final DataObject original,
        final String ext
    ) throws IOException {
        final FileObject fo = folder.getPrimaryFile ();
        final DataShadow[] arr = new DataShadow[1];

        DataObjectPool.getPOOL().runAtomicAction (fo, new FileSystem.AtomicAction () {
                                                 public void run () throws IOException {
                                                     FileObject file = writeOriginal (name, ext, fo, original);
                                                     DataObject obj = DataObject.find (file);
                                                     if (obj instanceof DataShadow) {
                                                         arr[0] = (DataShadow)obj;
                                                     } else {
                                                         // wrong instance => shadow was not found
                                                         DataObjectNotFoundException dnfe = 
                                                             new DataObjectNotFoundException (obj.getPrimaryFile ());
                                                         ErrorManager errMan = ErrorManager.getDefault ();
                                                         errMan.annotate( dnfe, obj.getClass().toString());
                                                         errMan.annotate( dnfe, file == null ? null : file.getPath());
                                                         throw dnfe;
                                                     }
                                                 }
                                             });

        return arr[0];
    }
    
    /** Writes the original DataObject into file of given name and extension.
     * Both parameters {@link name} and {@link ext} are ignored when the data file
     * is passed in as a {@link trg} parameter, in that case name and link can be null.
     * @param name name of the file to write original DataObject in
     * @param ext extension of the file to write original DataObject in
     * @param trg folder where FileObject of given name and ext will be created or
     * file which content is replaced
     * @param obj DataObject which link is stored into
     * @return the file with link
     * @exception IOException on I/O error
     */
    private static FileObject writeOriginal (
        final String name, final String ext, final FileObject trg, final DataObject obj
    ) throws IOException {
        try {
            return (FileObject) MUTEX.writeAccess (new Mutex.ExceptionAction () {
                public Object run () throws IOException {
                    FileObject fo;
                    if (trg.isData ()) {
                        fo = trg;
                    } else {
                         String n;
                         if (name == null) {
                             // #45810 - if obj is disk root then fix the filename
                             String baseName = obj.getName().replace(':', '_').replace('/', '_'); // NOI18N
                             n = FileUtil.findFreeFileName (trg, baseName, ext);
                         } else {
                             n = name;
                         }
                         fo = trg.createData (n, ext);
                    }
                    writeShadowFile(fo, obj.getPrimaryFile().getURL());
                    return fo;
                }
            });
        } catch (MutexException e) {
            throw (IOException) e.getException ();
        }
    }
    
    /** Overwrites existing file object with new link and fs.
     * @exception IOException on I/O error
     */
    static void writeOriginal (final FileObject shadow, final URL url) throws IOException {
        try {
            MUTEX.writeAccess (new Mutex.ExceptionAction () {
                public Object run () throws IOException {
                    writeShadowFile(shadow, url);
                    return null;
                }
            });
        } catch (MutexException e) {
            throw (IOException) e.getException ();
        }
    }

    /**
     * Writes content of shadow file.
     * @param fo shadow file
     * @param url URL to original
     */
    private static void writeShadowFile(FileObject fo, URL url) throws IOException {
        FileLock lock = fo.lock();
        Writer os = new OutputStreamWriter(fo.getOutputStream(lock), "UTF-8");
        try {
            os.write(url.toExternalForm()); // NOI18N
        } finally {
            os.close();
            lock.releaseLock();
        }
    }
    
    /** Loads proper dataShadow from the file fileObject.
    *
    * @param fileObject The file to deserialize shadow from.
    * @return the original DataObject referenced by the shadow
    * @exception IOException error during load
    */
    protected static DataObject deserialize (FileObject fileObject) throws java.io.IOException {
        URL url = readURL(fileObject);
        FileObject fo = URLMapper.findFileObject(url);
        if (fo == null) {
            throw new java.io.FileNotFoundException (url.toExternalForm());
        }
        return DataObject.find (fo);
    }
    
    static URL readURL(final FileObject f) throws IOException {
        if ( f.getSize() == 0 ) {
            Object fileName = f.getAttribute ("originalFile"); // NOI18N
            if ( fileName instanceof String ) {
                return recreateURL((String)fileName, (String)f.getAttribute("originalFileSystem"), f.getFileSystem());
            } else if (fileName instanceof URL) {
                return (URL)fileName;
            } else {
                throw new java.io.FileNotFoundException (f.getPath());
            }
        }
        try {
            return (URL) MUTEX.readAccess (new Mutex.ExceptionAction () {
                public Object run () throws IOException {
                    BufferedReader ois = new BufferedReader (new InputStreamReader (f.getInputStream (), "UTF-8"));

                    try {
                        String s = ois.readLine ();
                        String fs = ois.readLine ();

                        if (s == null) {
                            // not found
                            throw new java.io.FileNotFoundException (f.getPath());
                        }
                        if (fs == null) {
                            return new URL(s);
                        }
                        return recreateURL(s, fs, f.getFileSystem());
                    } finally {
                        ois.close ();
                    }
                }
            });
        } catch (MutexException e) {
            throw (IOException) e.getException ();
        }
    }

    // BACKWARD COMPATIBILITY:
    private static URL recreateURL(String fileName, String fileSystem, FileSystem fs) throws IOException {
        if (fileSystem == null) {
            return createURL(fs, fileName);
        } else if (fileSystem.equals("SystemFileSystem")) {
            return createURL(Repository.getDefault().getDefaultFileSystem(), fileName);
        } else {
            // Does not work. No FS is mounted anymore.
            throw new java.io.FileNotFoundException (fileName+" "+fileSystem); //NOI18N
        }
    }
    private static URL createURL(FileSystem fs, String fileName) throws IOException {
        String root = fs.getRoot().getURL().toExternalForm();
        if (!root.endsWith("/")) {
            root += "/";
        }
        root += fileName.replace('\\', '/');
        return new URL(root);
    }
    
    private FileObject checkOriginal (DataObject orig) throws java.io.IOException {                
        if (orig == null)
            return null;
        return deserialize(getPrimaryFile()).getPrimaryFile();
    }

    /** Return the original shadowed object.
    * @return the data object
    */
    public DataObject getOriginal () {
        return original;
    }
    
    /** Implementation of Container interface.
     * @return array of one element, the original
     */
    public DataObject[] getChildren () {
        return new DataObject[] { getOriginal () };
    }

    /* Creates node delegate.
    */
    protected Node createNodeDelegate () {
        return new ShadowNode (this);
    }

    /* Getter for delete action.
    * @return true if the object can be deleted
    */
    public boolean isDeleteAllowed () {
        return !getPrimaryFile ().isReadOnly ();
    }

    /* 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 ();
    }

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

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

    /* Creates shadow for this object in specified folder. The current
    * implementation creates reference data shadow and pastes it into
    * specified folder.
    *
    * @param f the folder to create shortcut in
    * @return the shadow
    */
    protected DataShadow handleCreateShadow (DataFolder f) throws IOException {
        if (getOriginal() instanceof DataFolder) {
            DataFolder.testNesting(((DataFolder)getOriginal()), f);
        }
        return original.handleCreateShadow (f);
    }

    /* Scans the orginal bundle */
    public Node.Cookie getCookie (Class c) {
        if (c.isInstance (this)) {
            return this;
        }
        return original.getCookie (this, c);
    }

    /* Try to refresh link to original file */
    public void refresh() {
        refresh(false);
    }
    
    private void refresh(boolean moved) {        
        try {
            /* Link isn't broken */            
            if (moved)
                tryUpdate();
            if (checkOriginal(original) != null)            
                return;                
        } catch (IOException e) {            
        }
        try {            
            /* Link is broken */
            this.setValid(false);            
        } catch (java.beans.PropertyVetoException e) {                        
        }         
    }
    
    private void tryUpdate() throws IOException {
        URL url = readURL(getPrimaryFile ());
        if (url.equals(original.getPrimaryFile().getURL())) {
            return;
        }
        writeOriginal (null, null, getPrimaryFile (), original);
    }
    
    private void setOriginal (DataObject o) {
        if (origL == null) {
            origL = new OrigL (this);
        }

        // set new original
        if (original != null) {
            original.removePropertyChangeListener (origL);
        }

        DataObject oldOriginal = original;
        
        o.addPropertyChangeListener (origL);
        original = o;

        // update nodes
        ShadowNode n [] = null;
        synchronized (nodes) {
            n = (ShadowNode [])nodes.toArray (new ShadowNode [nodes.size ()]);
        }
        
        try {
            for (int i = 0; i < n.length; i++) {
                n[i].originalChanged ();
            }
        }
        catch (IllegalStateException e) {
            System.out.println("Please reopen the bug #18998 if you see this message."); // NOI18N
            System.out.println("Old:"+oldOriginal + // NOI18N
                ((oldOriginal == null) ? "" : (" / " + oldOriginal.isValid() + " / " + System.identityHashCode(oldOriginal)))); // NOI18N
            System.out.println("New:"+original + // NOI18N
                ((original == null) ? "" : (" / " + original.isValid() + " / " + System.identityHashCode(original)))); // NOI18N
            throw e;
        }
    }

    private static void updateShadowOriginal(final DataShadow shadow) {
        final FileObject primary = shadow.original.getPrimaryFile ();

        org.openide.util.RequestProcessor.postRequest (new Runnable () {
            public void run () {
                DataObject newOrig;

                try {
                    newOrig = DataObject.find (primary);
                } catch (DataObjectNotFoundException e) {
                    newOrig = null;
                }

                if (newOrig != null) {
                    shadow.setOriginal (newOrig);
                }
            }
        }, 100);
    }
    
    protected DataObject handleCopy (DataFolder f) throws IOException {
        if (getOriginal() instanceof DataFolder) {
            DataFolder.testNesting(((DataFolder)getOriginal()), f);
        }
        return super.handleCopy(f);
    }
    
    protected FileObject handleMove (DataFolder f) throws IOException {
        if (getOriginal() instanceof DataFolder) {
            DataFolder.testNesting(((DataFolder)getOriginal()), f);
        }
        return super.handleMove(f);
    }

    private static class OrigL implements PropertyChangeListener {
        WeakReference shadow = null;
        public OrigL (DataShadow shadow) {
            this.shadow = new WeakReference (shadow);
        }
        public void propertyChange (PropertyChangeEvent evt) {
            final DataShadow shadow = (DataShadow) this.shadow.get ();

            if (shadow != null && DataObject.PROP_VALID.equals (evt.getPropertyName ())) {
                updateShadowOriginal(shadow);
            }
        }
    }

    /** Node for a shadow object. */
    protected static class ShadowNode extends FilterNode {
        /** message to create name of node */
        private static MessageFormat format;
        /** message to create short description of node */
        private static MessageFormat descriptionFormat;
        /** if true, the DataShadow name is used instead of original's name, 
         * affects DataShadows of filesystem roots only
         */
        private static final String ATTR_USEOWNNAME = "UseOwnName"; //NOI18N

        /** shadow */
        private DataShadow obj;

        /** the sheet computed for this node or null */
        private Sheet sheet;

        /** filesystem name property of original */
        private String originalFS;
        
        /** Create a shadowing node.
         * @param shadow the shadow
         */
        public ShadowNode (DataShadow shadow) {
            this (shadow, shadow.getOriginal ().getNodeDelegate ());
        }

        /** Initializes it */
        private ShadowNode (DataShadow shadow, Node node) {
            super (node);
            this.obj = shadow;
            synchronized (this.obj.nodes) {
                this.obj.nodes.add (this);
            }
        }

        /* Clones the node
        */
        public Node cloneNode () {
            ShadowNode sn = new ShadowNode (obj);
            return sn;
        }

        /* Renames the shadow data object.
        * @param name new name for the object
        * @exception IllegalArgumentException if the rename failed
        */
        public void setName (String name) {
            try {
                if (!name.equals (obj.getName ())) {
                    obj.rename (name);
                    if (obj.original.getPrimaryFile ().isRoot ()) {
                        obj.getPrimaryFile ().setAttribute (ATTR_USEOWNNAME, Boolean.TRUE);
                    }
                    fireDisplayNameChange (null, null);
                    fireNameChange (null, null);
                }
            } catch (IOException ex) {
                throw new IllegalArgumentException (ex.getMessage ());
            }
        }

        /** The name of the shadow.
        * @return the name
        */
        public String getName () {
            return obj.getName ();
        }

        /* Creates name based on the original one.
        */
        public String getDisplayName () {
            if (format == null) {
                format = new MessageFormat (NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowName"));
            }
            String n = format.format (createArguments ());
            try {
                obj.getPrimaryFile().getFileSystem().getStatus().annotateName(n, obj.files());
            } catch (FileStateInvalidException fsie) {
                // ignore
            }
            return n;
        }

        /** Creates arguments for given shadow node */
        private Object[] createArguments () {
            String origDisp;
            String shadowName = obj.getName ();
            if (obj.original.isValid()) {
                origDisp = obj.original.getNodeDelegate().getDisplayName();
            } else {
                // We will soon be a broken data shadow, in the meantime...
                origDisp = ""; // NOI18N
            }
            Boolean useOwnName = (Boolean)obj.getPrimaryFile ().getAttribute (ATTR_USEOWNNAME);
            if (obj.original.getPrimaryFile ().isRoot () && 
                (useOwnName == null || !useOwnName.booleanValue ())) {
                try {
                    shadowName = obj.original.getPrimaryFile ().getFileSystem ().getDisplayName ();
                } catch (FileStateInvalidException e) {
                    // ignore
                }
            }
            return new Object[] {
                       shadowName, // name of the shadow
                       super.getDisplayName (), // name of original
                       systemNameOrFileName (obj.getPrimaryFile ()), // full name of file for shadow
                       systemNameOrFileName (obj.getOriginal ().getPrimaryFile ()), // full name of original file
                       origDisp, // display name of original
                   };
        }

        /** System name of file name
        */
        private static String systemNameOrFileName (FileObject fo) {
            return FileUtil.getFileDisplayName(fo);
        }

        /* Creates description based on the original one.
        */
        public String getShortDescription () {
            if (descriptionFormat == null) {
                descriptionFormat = new MessageFormat (
                                        NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowHint")
                                    );
            }
            return descriptionFormat.format (createArguments ());
        }
        
        /* Show filesystem icon if it is a root.
         */
        public Image getIcon(int type) {
            Image i = rootIcon(type);
            if (i != null) {
                return i;
            } else {
                return super.getIcon(type);
            }
        }
        public Image getOpenedIcon(int type) {
            Image i = rootIcon(type);
            if (i != null) {
                return i;
            } else {
                return super.getOpenedIcon(type);
            }
        }
        private Image rootIcon(int type) {
            FileObject orig = obj.getOriginal().getPrimaryFile();
            if (orig.isRoot()) {
                try {
                    FileSystem fs = orig.getFileSystem();
                    try {
                        Image i = Introspector.getBeanInfo(fs.getClass()).getIcon(type);
                        return fs.getStatus().annotateIcon(i, type, obj.files());
                    } catch (IntrospectionException ie) {
                        ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ie);
                        // ignore
                    }
                } catch (FileStateInvalidException fsie) {
                    // ignore
                }
            }
            return null;
        }

        /* @return obj.isDeleteAllowed () */
        public boolean canDestroy () {
            return obj.isDeleteAllowed ();
        }

        /* Destroyes the node
        */
        public void destroy () throws IOException {
            synchronized (obj.nodes) {
                obj.nodes.remove (this);
            }
            obj.delete ();
            //      super.destroy ();
        }

        /** @return true if shadow can be renamed
        */
        public final boolean canRename () {
            return obj.isRenameAllowed ();
        }

        /* Returns true if this object allows copying.
        * @returns true if so
        */
        public final boolean canCopy () {
            return obj.isCopyAllowed ();
        }

        /* Returns true if this object allows cutting.
        * @returns true if so
        */
        public final boolean canCut () {
            return obj.isMoveAllowed ();
        }

        /* First of all the DataObject.getCookie method is
        * called. If it produces non-null result, it is returned.
        * Otherwise the value returned from super.getCookie
        * method is returned.
        *
        * @return the cookie or null
        */
        public Node.Cookie getCookie (Class cl) {
            Node.Cookie c = obj.getCookie (cl);
            if (c != null) {
                return c;
            } else {
                return super.getCookie (cl);
            }
        }

        /** Returns modified properties of the original node.
        * @return property sets 
        */
        public PropertySet[] getPropertySets () {
            Sheet s = sheet;
            if (s == null) {
                s = sheet = cloneSheet ();
            }
            return s.toArray ();
        }

        /** Copy this node to the clipboard.
        *
        * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one copy flavor
        * @throws IOException if it could not copy
        * @see NodeTransfer
        */
        public Transferable clipboardCopy () throws IOException {
            ExTransferable t = ExTransferable.create (super.clipboardCopy ());
            t.put (LoaderTransfer.transferable (
                obj, 
                LoaderTransfer.CLIPBOARD_COPY)
            );
            return t;
        }

        /** Cut this node to the clipboard.
        *
        * @return {@link org.openide.util.datatransfer.ExTransferable.Single} with one cut flavor
        * @throws IOException if it could not cut
        * @see NodeTransfer
        */
        public Transferable clipboardCut () throws IOException {
            ExTransferable t = ExTransferable.create (super.clipboardCut ());
            t.put (LoaderTransfer.transferable (
                obj, 
                LoaderTransfer.CLIPBOARD_CUT)
            );
            return t;
        }
        /**
        * This implementation only calls clipboardCopy supposing that 
        * copy to clipboard and copy by d'n'd are similar.
        *
        * @return transferable to represent this node during a drag
        * @exception IOException when the
        *    cut cannot be performed
        */
        public Transferable drag () throws IOException {
            return clipboardCopy ();
        }

        /** Creates a node listener that allows listening on the
        * original node and propagating events to the proxy.
        * 

Intended for overriding by subclasses, as with {@link #createPropertyChangeListener}. * * @return a {@link org.openide.nodes.FilterNode.NodeAdapter} in the default implementation */ protected org.openide.nodes.NodeListener createNodeListener () { return new PropL (this); } /** Equal if the o is ShadowNode to the same shadow object. */ public boolean equals (Object o) { if (o instanceof ShadowNode) { ShadowNode sn = (ShadowNode)o; return sn.obj == obj; } return false; } /** Hashcode is computed by the represented shadow. */ public int hashCode () { return obj.hashCode (); } /** Clones the property sheet of original node. */ private Sheet cloneSheet () { PropertySet[] sets = this.getOriginal ().getPropertySets (); Sheet s = new Sheet (); for (int i = 0; i < sets.length; i++) { Sheet.Set ss = new Sheet.Set (); ss.put (sets[i].getProperties ()); ss.setName (sets[i].getName ()); ss.setDisplayName (sets[i].getDisplayName ()); ss.setShortDescription (sets[i].getShortDescription ()); // modifies the set if it contains name of object property modifySheetSet (ss); s.put (ss); } return s; } /** Modifies the sheet set to contain name of property and name of * original object. */ private void modifySheetSet (Sheet.Set ss) { Property p = ss.remove (DataObject.PROP_NAME); if (p != null) { p = new PropertySupport.Name (this); ss.put (p); p = new Name (); ss.put (p); } } private void originalChanged () { DataObject ori = obj.getOriginal(); if (ori.isValid()) { changeOriginal (ori.getNodeDelegate(), true); } else { updateShadowOriginal(obj); } } /** Class that renames the orginal object and also updates * the link */ private final class Name extends PropertySupport.ReadWrite { public Name () { super ( "OriginalName", // NOI18N String.class, DataObject.getString ("PROP_ShadowOriginalName"), DataObject.getString ("HINT_ShadowOriginalName") ); } public Object getValue () { return obj.getOriginal ().getName(); } public void setValue (Object val) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!canWrite()) throw new IllegalAccessException(); if (!(val instanceof String)) throw new IllegalArgumentException(); try { DataObject orig = obj.getOriginal (); orig.rename ((String)val); writeOriginal (null, null, obj.getPrimaryFile (), orig); } catch (IOException ex) { throw new InvocationTargetException (ex); } } public boolean canWrite () { return obj.getOriginal ().isRenameAllowed(); } } /** Property listener on data object that delegates all changes of * properties to this node. */ private static class PropL extends FilterNode.NodeAdapter { public PropL (ShadowNode sn) { super (sn); } protected void propertyChange (FilterNode fn, PropertyChangeEvent ev) { if (Node.PROP_PROPERTY_SETS.equals(ev.getPropertyName ())) { // clear the sheet ShadowNode sn = (ShadowNode)fn; sn.sheet = null; } super.propertyChange (fn, ev); } } } static final class DSWeakReference extends WeakReference { private int hash; private FileObject original; DSWeakReference(DataObject o, ReferenceQueue rqueue) { super(o, rqueue); this.hash = o.hashCode(); if (o instanceof DataShadow) { DataShadow s = (DataShadow)o; this.original = s.getOriginal ().getPrimaryFile (); } } public int hashCode() { return hash; } public boolean equals(Object o) { Object mine = get(); if (mine == null) { return false; } if (o instanceof DSWeakReference) { DSWeakReference him = (DSWeakReference) o; return mine.equals(him.get()); } return false; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy