| /* |
| * 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. 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 "is template?" 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 "is template?" 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; |
| } |
| } |