![JAR search and dependency download from the Maven repository](/logo.png)
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;
}
}
}