blob: 73bbe851efb21d93926e25526def6acac033cba1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.properties;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataFolder;
import org.openide.loaders.DataObject;
import org.openide.loaders.FileEntry;
import org.openide.loaders.MultiDataObject;
import org.openide.nodes.CookieSet;
import org.openide.nodes.Node;
import org.openide.util.HelpCtx;
/**
* Object that represents one <code>FileEntry</code> and has support
* for presentation of this entry as a node. I.&thinsp;i. it contains methods
* required for a node, so that a node for the entry may just delegate to it.
*
* @author Jaroslav Tulach, Petr Jiricka
*/
public abstract class PresentableFileEntry extends FileEntry
implements Node.Cookie {
/** generated Serialized Version UID */
static final long serialVersionUID = 3328227388376142699L;
/** The node delegate for this data object. */
private transient Node nodeDelegate;
/** Modified flag */
private boolean modif = false;
/** property change listener support */
private transient PropertyChangeSupport changeSupport;
/** listener for changes in the cookie set */
private ChangeListener cookieL = new ChangeListener () {
public void stateChanged (ChangeEvent ev) {
firePropertyChange (Node.PROP_COOKIE, null, null);
}
};
/** array of cookies for this entry */
private transient CookieSet cookieSet;
// guard used in getNodeDelegate
private transient Object nodeDelegateMutex = new Object();
/**
* Creates a new presentable file entry initially attached
* to a given file object.
*
* @param obj the data object this entry belongs to
* @param fo the file object for the entry
*/
public PresentableFileEntry(MultiDataObject obj, FileObject fo) {
super (obj, fo);
}
/** Creates a node delegate for this entry. */
protected abstract Node createNodeDelegate();
/**
* Gets a node delegate for this data object entry. Either
* {@linkplain #createNodeDelegate creates it} (if it does not exist yet)
* or returns a previously created instance of it.
*
* @return the node delegate (without parent) for this data object
*/
public final Node getNodeDelegate () {
synchronized (nodeDelegateMutex) {
if (nodeDelegate == null) {
nodeDelegate = createNodeDelegate();
}
return nodeDelegate;
}
}
/**
* Sets value of attribute &quot;is template?&quot; for a given file.
* Used also from FileEntry.
*
* @param fo file to assign the attribute to
* @param newValue new value of the attribute
* @return <code>true</code> if the value was changed;
* <code>false</code> if the new value was the same
* as the old value
* @exception java.io.IOException if the operation failed
*/
private static boolean setTemplate(FileObject fo,
boolean newValue) throws IOException {
Object old = fo.getAttribute(DataObject.PROP_TEMPLATE);
boolean oldValue = Boolean.TRUE.equals(old);
if (newValue == oldValue) {
return false;
}
fo.setAttribute(DataObject.PROP_TEMPLATE,
newValue ? Boolean.TRUE : null);
return true;
}
/**
* Sets value of attribute &quot;is template?&quot; for this entry's file.
*
* @param newValue new value of the attribute
* @exception java.io.IOException if setting the template state fails
*/
public final void setTemplate(boolean newValue) throws IOException {
if (!setTemplate(getFile(), newValue)) {
// no change in state
return;
}
firePropertyChange(DataObject.PROP_TEMPLATE,
Boolean.valueOf(!newValue),
Boolean.valueOf(newValue));
}
/**
* Get the template status of this data object entry.
*
* @return <code>true</code> if it is a template;
* <code>false</code> otherwise
*/
public boolean isTemplate() {
Object o = getFile().getAttribute(DataObject.PROP_TEMPLATE);
return Boolean.TRUE.equals(o);
}
/**
* Renames underlying fileobject. This implementation returns the
* same file. Fires property change. Called when the DO is renamed, not the entry
*
* @param name new name
* @return file object with renamed file
* @see #renameEntry
*/
@Override
public FileObject rename (String name) throws IOException {
String oldName = getName();
FileObject fo = super.rename(name);
firePropertyChange(DataObject.PROP_NAME, oldName, name);
return fo;
}
/** Renames underlying fileobject. This implementation return the
* same file. Fires property change. Called when the file entry is renamed, not the DO
*
* @param name new name
* @return file object with renamed file
* @see #rename
*/
public FileObject renameEntry (String name) throws IOException {
return rename(name);
}
/** Deletes file object and fires property change. */
@Override
public void delete () throws IOException {
super.delete();
firePropertyChange(DataObject.PROP_VALID, Boolean.TRUE, Boolean.FALSE);
}
/** Test whether the object may be deleted.
* @return <code>true</code> if it may
*/
public abstract boolean isDeleteAllowed ();
/** Test whether the object may be copied.
* @return <code>true</code> if it may
*/
public abstract boolean isCopyAllowed ();
/** Test whether the object may be moved.
* @return <code>true</code> if it may
*/
public abstract boolean isMoveAllowed ();
/** Test whether the object may create shadows.
* <p>The default implementation returns <code>true</code>.
* @return <code>true</code> if it may
*/
public boolean isShadowAllowed () {
return true;
}
/** Test whether the object may be renamed.
* @return <code>true</code> if it may
*/
public abstract boolean isRenameAllowed ();
/** Test whether the object is modified.
* @return <code>true</code> if it is modified
*/
public boolean isModified() {
return modif;
}
/** Set whether the object is considered modified.
* Also fires a change event.
* If the new value is <code>true</code>, the data object is added into
* a {@link #getRegistry registry} of opened data objects.
* If the new value is <code>false</code>,
* the data object is removed from the registry.
*/
public void setModified(boolean modif) {
if (this.modif != modif) {
this.modif = modif;
firePropertyChange(DataObject.PROP_MODIFIED,
Boolean.valueOf(!modif),
Boolean.valueOf(modif));
}
}
/**
* Get help context for this object.
*
* @return the help context
*/
public abstract HelpCtx getHelpCtx ();
/**
* Returns name of the data object.
* <p>
* The default implementation returns name of the primary file.
*
* @return the name of the data object
*/
public String getName () {
return getFile ().getName ();
}
/**
* Returns a folder this data object is stored in.
*
* @return folder this data object is stored in;
* or <code>null</code> if the data object's primary file
* is the {@linkplain FileObject#isRoot root} of its filesystem
*/
public final DataFolder getFolder () {
FileObject fo = getFile ().getParent ();
// could throw IllegalArgumentException but only if fo is not folder
// => then there is a bug in filesystem implementation
return fo == null ? null : DataFolder.findFolder (fo);
}
//
// Property change support
//
/** @param l the listener
*/
public synchronized void addPropertyChangeListener (PropertyChangeListener l) {
getChangeSupport ().addPropertyChangeListener (l);
}
/** @param l the listener
*/
public void removePropertyChangeListener (PropertyChangeListener l) {
getChangeSupport ().removePropertyChangeListener (l);
}
/** Fires property change notification to all listeners registered via
* {@link #addPropertyChangeListener}.
*
* @param name of property
* @param oldValue old value
* @param newValue new value
*/
protected final void firePropertyChange (String name, Object oldValue, Object newValue) {
getChangeSupport ().firePropertyChange (name, oldValue, newValue);
}
/** Getter for standard property change support. This is used in
* this class and by this method provided to subclasses.
*
* @return support
*/
private synchronized final PropertyChangeSupport getChangeSupport () {
if (changeSupport == null) {
changeSupport = new PropertyChangeSupport (this);
}
return changeSupport;
}
/** 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
*/
protected final synchronized void setCookieSet (CookieSet s) {
if (cookieSet != null) {
cookieSet.removeChangeListener (cookieL);
}
s.addChangeListener (cookieL);
cookieSet = s;
firePropertyChange (Node.PROP_COOKIE, null, null);
}
/** 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 <code>null</code>)
*/
protected final CookieSet getCookieSet () {
CookieSet s = cookieSet;
if (s != null) {
return s;
}
synchronized (this) {
if (cookieSet != null) {
return cookieSet;
}
// sets an empty sheet and adds a listener to it
setCookieSet (CookieSet.createGeneric(null));
cookieSet.assign(FileObject.class, getFile());
return cookieSet;
}
}
/**
* Looks 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 <code>null</code> if this class of cookie
* is not supported
*/
@SuppressWarnings("unchecked")
public <T extends Node.Cookie> T getCookie(Class<T> type) {
CookieSet c = cookieSet;
if (c != null) {
T cookie = c.getCookie(type);
if (cookie != null) {
return cookie;
}
}
if (type.isInstance (this)) {
return (T) this;
}
return null;
}
}