Index: core/src/org/netbeans/core/projects/SettingChildren.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/projects/SettingChildren.java,v retrieving revision 1.18 diff -c -r1.18 SettingChildren.java *** core/src/org/netbeans/core/projects/SettingChildren.java 12 Nov 2002 16:19:47 -0000 1.18 --- core/src/org/netbeans/core/projects/SettingChildren.java 24 Feb 2003 12:18:14 -0000 *************** *** 19,24 **** --- 19,25 ---- import org.openide.nodes.*; import org.openide.loaders.DataObject; import org.openide.loaders.DataFolder; + import org.openide.loaders.DataShadow; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileStateInvalidException; *************** *** 93,98 **** --- 94,110 ---- public int hashCode() { return getOriginal().hashCode(); } + public String getDisplayName() { + //find the original to discard the (->) text + DataObject dob = (DataObject) getCookie (DataObject.class); + if (dob == null) { + //we have a node on its own + return super.getDisplayName(); + } else { + while (dob instanceof DataShadow) dob = ((DataShadow) dob).getOriginal(); + return dob.getNodeDelegate().getDisplayName(); + } + } } /** Property allowing display/manipulation of setting status for one specific layer. */ *************** *** 311,317 **** public SettingFilterNode (Node original) { super (original); ! FileObject pf = ((DataObject) getCookie (DataObject.class)).getPrimaryFile (); weakL = new FSL (this); FileStateManager.getDefault ().addFileStatusListener (weakL, pf); --- 323,329 ---- public SettingFilterNode (Node original) { super (original); ! FileObject pf = ((DataObject) getCookie (DataObject.class)).getPrimaryFile (); weakL = new FSL (this); FileStateManager.getDefault ().addFileStatusListener (weakL, pf); *************** *** 320,325 **** --- 332,344 ---- specialProp (new FileStateProperty (pf, FileStateManager.LAYER_PROJECT, PROP_LAYER_PROJECT, false)); specialProp (new FileStateProperty (pf, FileStateManager.LAYER_SESSION, PROP_LAYER_SESSION, false)); specialProp (new FileStateProperty (pf, FileStateManager.LAYER_MODULES, PROP_LAYER_MODULES, false)); + } + + public String getDisplayName() { + //find the original to discard the (->) text + DataObject dob = (DataObject) getCookie (DataObject.class); + while (dob instanceof DataShadow) dob = ((DataShadow) dob).getOriginal(); + return dob.getNodeDelegate().getDisplayName(); } /** Registers special property. Index: openide/src/org/openide/actions/NewTemplateAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/actions/NewTemplateAction.java,v retrieving revision 1.76 diff -c -r1.76 NewTemplateAction.java *** openide/src/org/openide/actions/NewTemplateAction.java 13 Feb 2003 13:14:03 -0000 1.76 --- openide/src/org/openide/actions/NewTemplateAction.java 24 Feb 2003 12:18:14 -0000 *************** *** 235,242 **** if (privileged.size() > 0) popup.add(new JSeparator()); // separator for (Iterator it = privileged.iterator(); it.hasNext(); ) { DataObject dobj = (DataObject)it.next(); ! if (dobj instanceof DataShadow) ! dobj = ((DataShadow)dobj).getOriginal(); popup.add(new Item(dobj, true)); } --- 235,242 ---- if (privileged.size() > 0) popup.add(new JSeparator()); // separator for (Iterator it = privileged.iterator(); it.hasNext(); ) { DataObject dobj = (DataObject)it.next(); ! // if (dobj instanceof DataShadow) ! // dobj = ((DataShadow)dobj).getOriginal(); popup.add(new Item(dobj, true)); } *************** *** 283,289 **** /** Invoked when an action occurs. */ public void actionPerformed(ActionEvent e) { ! doShowWizard(template); } } } --- 283,314 ---- /** Invoked when an action occurs. */ public void actionPerformed(ActionEvent e) { ! // final Container w = ((JFrame) ! // WindowManager.getDefault().getMainWindow()).getContentPane(); ! //hide the menu immediately ! this.getParent().setVisible(false); ! //set the wait cursor until the dialog is displayed ! // w.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); ! // final AWTEventListener listener = new AWTEventListener () { ! // public void eventDispatched (AWTEvent ae) { ! // Toolkit.getDefaultToolkit().removeAWTEventListener (this); ! // w.setCursor ! // (Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); ! // } ! // }; ! ! // try { ! //Attach the listener - it will detach itself on the first ! //component event ! // Toolkit.getDefaultToolkit().addAWTEventListener(listener, ! // AWTEvent.COMPONENT_EVENT_MASK); ! //show the dialog ! doShowWizard(template); ! // } finally { ! //ensure the listener is really removed ! // Toolkit.getDefaultToolkit().removeAWTEventListener (listener); ! // w.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); ! // } } } } *************** *** 302,314 **** List l2 = new ArrayList(data.length); for (int i=0; i)" at the end ! DataNode dn = new DataNode(shadow, Children.LEAF); ! nodeName = dn.getDisplayName(); ! obj = shadow.getOriginal(); ! n = obj.getNodeDelegate(); ! } ! ! if (obj == null) ! obj = (DataObject)n.getCookie (DataObject.class); if (obj != null) { if (obj.isTemplate ()) { // on normal nodes stop recursion --- 477,483 ---- protected Node[] createNodes (Object key) { Node n = (Node)key; String nodeName = n.getDisplayName(); ! DataObject obj = (DataObject) n.getCookie(DataObject.class); if (obj != null) { if (obj.isTemplate ()) { // on normal nodes stop recursion *************** *** 604,619 **** Node n = (Node)key; String nodeName = n.getDisplayName(); ! DataObject obj = null; ! DataShadow shadow = (DataShadow)n.getCookie (DataShadow.class); ! if (shadow != null) { ! // I need DataNode here to get localized name of the ! // shadow, but without the ugly "(->)" at the end ! DataNode dn = new DataNode(shadow, Children.LEAF); ! nodeName = dn.getDisplayName(); ! obj = shadow.getOriginal(); ! n = obj.getNodeDelegate(); ! } if (obj == null) obj = (DataObject)n.getCookie (DataObject.class); --- 616,622 ---- Node n = (Node)key; String nodeName = n.getDisplayName(); ! DataObject obj = (DataObject) n.getCookie(DataObject.class); if (obj == null) obj = (DataObject)n.getCookie (DataObject.class); Index: openide/src/org/openide/loaders/DataObject.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataObject.java,v retrieving revision 1.86 diff -c -r1.86 DataObject.java *** openide/src/org/openide/loaders/DataObject.java 10 Feb 2003 19:45:41 -0000 1.86 --- openide/src/org/openide/loaders/DataObject.java 24 Feb 2003 12:18:14 -0000 *************** *** 340,351 **** * @return true if it is a template */ public final boolean isTemplate () { Object o = getPrimaryFile().getAttribute(PROP_TEMPLATE); boolean ret = false; if (o instanceof Boolean) ret = ((Boolean) o).booleanValue(); return ret; ! } /** Test whether the object may be deleted. --- 340,358 ---- * @return true if it is a template */ public final boolean isTemplate () { + return isTemplateImpl(); + } + + /** Implementation of isTemplate test. Overridden by + * DataShadow to return the template value for the + * file pointed to. */ + boolean isTemplateImpl () { Object o = getPrimaryFile().getAttribute(PROP_TEMPLATE); boolean ret = false; if (o instanceof Boolean) ret = ((Boolean) o).booleanValue(); return ret; ! } /** Test whether the object may be deleted. Index: openide/src/org/openide/loaders/DataLoaderPool.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataLoaderPool.java,v retrieving revision 1.99 diff -c -r1.99 DataLoaderPool.java *** openide/src/org/openide/loaders/DataLoaderPool.java 29 Jan 2003 01:26:54 -0000 1.99 --- openide/src/org/openide/loaders/DataLoaderPool.java 24 Feb 2003 12:18:14 -0000 *************** *** 838,845 **** */ protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException { try { ! DataObject d = DataShadow.deserialize (primaryFile); ! if (d != null) return new DataShadow (primaryFile, d, this); } catch (IOException ex) { // broken link or damaged shadow file } --- 838,858 ---- */ protected MultiDataObject createMultiObject(FileObject primaryFile) throws DataObjectExistsException, IOException { try { ! //first, try to instantiate the shadow without creating ! //the underlying DataObject ! if (DataShadow.canInstantiateLazily (primaryFile)) { ! //Make sure the shadow points to a valid file ! if (primaryFile == null) { ! return new BrokenDataShadow (primaryFile, this); ! } else { ! FileObject fo = DataShadow.validate (primaryFile); ! return new DataShadow (primaryFile, this); ! } ! } else { ! //use the old behavior, instantiating the DataObject ! DataObject d = DataShadow.deserialize (primaryFile); ! if (d != null) return new DataShadow (primaryFile, d, this); ! } } catch (IOException ex) { // broken link or damaged shadow file } Index: openide/src/org/openide/loaders/DataShadow.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataShadow.java,v retrieving revision 1.49 diff -c -r1.49 DataShadow.java *** openide/src/org/openide/loaders/DataShadow.java 21 Dec 2002 08:40:25 -0000 1.49 --- openide/src/org/openide/loaders/DataShadow.java 24 Feb 2003 12:18:15 -0000 *************** *** 13,19 **** package org.openide.loaders; ! import java.awt.datatransfer.Transferable; import java.awt.Image; import java.beans.*; import java.io.*; --- 13,20 ---- package org.openide.loaders; ! import java.awt.Component; ! import java.awt.datatransfer.*; import java.awt.Image; import java.beans.*; import java.io.*; *************** *** 21,26 **** --- 22,28 ---- import java.lang.reflect.*; import java.lang.ref.*; import java.util.*; + import javax.swing.Action; import org.openide.filesystems.*; import org.openide.filesystems.FileSystem; *************** *** 28,34 **** 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; --- 30,36 ---- import org.openide.nodes.*; import org.openide.util.HelpCtx; import org.openide.ErrorManager; ! import org.openide.util.datatransfer.*; import org.openide.util.Mutex; import org.openide.util.MutexException; *************** *** 39,45 **** 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. */ --- 41,50 ---- public class DataShadow extends MultiDataObject implements DataObject.Container { /** generated Serialized Version UID */ static final long serialVersionUID = 6305590675982925167L; ! //XXX code below for debugging as module owners convert their modules ! //to use lazy shadows for templates. Should be removed before 3.5 release. ! private static final boolean DEBUG = ! Boolean.getBoolean("netbeans.debug.lazyshadows"); /** original data object */ private DataObject original; /** Listener attached to original DataObject. */ *************** *** 59,64 **** --- 64,72 ---- private static final int IDX_FS = 0; private static final int IDX_PATH = 1; + //tracking variable to enforce display name consistency + //across ProxyShadowNode and ShadowNode + boolean wasLazy = false; /** Getter for the Set that contains all DataShadows. */ private static Set getDataShadowsSet() { *************** *** 181,191 **** 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); --- 189,221 ---- init(original); } + /** Constructs a new data shadow which will lazily dereference + * its original DataObject only if information (generally cookies + * or description) are asked for that cannot be found on the primary + * file.

+ * Used to avoid creation of the original if the display name and + * icon are available on the original node. Note that this constructor + * should not be used for shadows where the original DataObject might + * change out from under them, because it will only start listening + * to the original DataObject if something causes it to call + * DataObject.find() and dereference the original. It is primarily + * useful for shadows where the common case is that they will remain + * untouched and creating the real DataObject to represent the + * underlying file would cause additional classloading. See + * issue 28683. + */ + DataShadow (FileObject fo, DataLoader loader) throws DataObjectExistsException { + super (fo, loader); + if (DEBUG) + System.out.println("Lazy shadow created for file: " + fo); + wasLazy=true; + } + /** Perform initialization after construction. * @param original original data object */ private void init(DataObject original) { ! if ((original == null) && (!canInstantiateLazily (getPrimaryFile()))) throw new IllegalArgumentException(); setOriginal (original); enqueueDataShadow(this); *************** *** 311,316 **** --- 341,363 ---- throw (IOException) e.getException (); } } + + /** Returns the primary file of the DataObject this + * shadow links to. If this DataShadow was instantiated + * without the DataObject it points to instantiated (i.e., + * in an XML layer with the SystemFileSystem.icon + * and SystemFileSystem.localizingBundle attributes + * present), calling this method will not force instantiation + * of the DataObject this shadow links to. + */ + final FileObject getOriginalPrimaryFile() { + try { + return validate (getPrimaryFile()); + } catch (java.io.IOException ioe) { + ErrorManager.getDefault().notify (ErrorManager.INFORMATIONAL, ioe); + return getOriginal().getPrimaryFile(); + } + } /** Loads proper dataShadow from the file fileObject. * *************** *** 319,327 **** * @exception IOException error during load */ protected static DataObject deserialize (FileObject fileObject) throws java.io.IOException { String result [] = read (fileObject); FileObject fo = checkOriginal (result [IDX_PATH], result [IDX_FS], fileObject.getFileSystem()); ! return DataObject.find (fo); } private static String [] read (final FileObject f) throws IOException { --- 366,410 ---- * @exception IOException error during load */ protected static DataObject deserialize (FileObject fileObject) throws java.io.IOException { + FileObject fo = validate (fileObject); + return DataObject.find (fo); + } + + /** Can the shadow be instantiated lazily? This could be done for + * all shadows, but as this is a micro-optimization, it will hurt + * in the case that a lazy node would be created and immediately + * asked for some cookie that would force the node's replacement. */ + private static final boolean isLazy(FileObject fo) { + if (fo == null) return false; //broken link + return (Boolean.TRUE.equals (fo.getAttribute("template")) && //NOI18N + fo.getAttribute("SystemFileSystem.icon") != null && //NOI18N + fo.getAttribute ("SystemFileSystem.localizingBundle") != null); //NOI18N + } + + /** Checks for icon and display name properties that will allow the + * Shadow to be instantiated without first creating the DataObject. */ + static final boolean canInstantiateLazily (FileObject fo) { + //See if it specifies an icon and display name + boolean result = isLazy (fo); + //If no, see if the file pointed to does + try { + if (!result) result = isLazy(validate(fo)); + } catch (java.io.IOException ioe) { + //we have a broken link, fail silently + if (DEBUG) ioe.printStackTrace(); + } + if (DEBUG) System.out.println(fo + (result ? " is " : " is not ") //NOI18N + + "lazy"); //NOI18N + return result; + } + + /** Returns the primary file pointed to by the passed shadow file + * without instantiating a DataObject. */ + static FileObject validate (FileObject fileObject) throws java.io.IOException { String result [] = read (fileObject); + if (DEBUG) System.out.println("Original primary file for " + fileObject + " is " + result[IDX_PATH] + " on " + result[IDX_FS]); FileObject fo = checkOriginal (result [IDX_PATH], result [IDX_FS], fileObject.getFileSystem()); ! return fo; } private static String [] read (final FileObject f) throws IOException { *************** *** 420,428 **** --- 503,533 ---- * @return the data object */ public DataObject getOriginal () { + if (original == null) { + resolveOriginal(); + } return original; } + /** For the case of lazy initialization, find the + * DataObject. + */ + private synchronized void resolveOriginal () { + try { + FileObject fo = this.getPrimaryFile(); + if (DEBUG) { + System.out.println("Resolving original " + fo); + Thread.dumpStack(); + } + init (deserialize (fo)); + } catch (java.io.IOException ioe) { + IllegalStateException e = new IllegalStateException ("Failure resolving file " //NOI18N + + getPrimaryFile()); + ErrorManager.getDefault().annotate (ioe, e); + throw e; + } + } + /** Implementation of Container interface. * @return array of one element, the original */ *************** *** 430,439 **** return new DataObject[] { getOriginal () }; } /* Creates node delegate. */ protected Node createNodeDelegate () { ! return new ShadowNode (this); } /* Getter for delete action. --- 535,571 ---- return new DataObject[] { getOriginal () }; } + /**Overrides DataObject.isTemplateImpl() to check if the primary + * file the shadow points to has its template attribute set. + * This avoids DataObject creation if this shadow is was + * created from an XML layer and instantiates its backing + * DataObject lazily. */ + protected boolean isTemplateImpl() { + if (original == null) { + try { + FileObject DOPrimary = validate(getPrimaryFile()); + if (DOPrimary != null) { + Object o = (Boolean) DOPrimary.getAttribute(PROP_TEMPLATE); + return Boolean.TRUE.equals (o); + } + } catch (java.io.IOException ioe) { + ErrorManager.getDefault().notify (ErrorManager.INFORMATIONAL, ioe); + } + } + return getOriginal().isTemplate(); + } + /* Creates node delegate. */ protected Node createNodeDelegate () { ! Node realNode; ! if ((original == null) && wasLazy) { ! realNode = new ProxyShadowNode(new ProxyDOChildren()); ! } else { ! realNode = new ShadowNode(this); ! } ! MutableFilterNode result = new MutableFilterNode (realNode); ! return result; } /* Getter for delete action. *************** *** 479,485 **** * @return the shadow */ protected DataShadow handleCreateShadow (DataFolder f) throws IOException { ! return original.handleCreateShadow (f); } /* Scans the orginal bundle */ --- 611,617 ---- * @return the shadow */ protected DataShadow handleCreateShadow (DataFolder f) throws IOException { ! return getOriginal().handleCreateShadow (f); } /* Scans the orginal bundle */ *************** *** 487,493 **** if (c.isInstance (this)) { return this; } ! return original.getCookie (this, c); } /* Try to refresh link to original file */ --- 619,625 ---- if (c.isInstance (this)) { return this; } ! return getOriginal().getCookie (this, c); } /* Try to refresh link to original file */ *************** *** 500,506 **** /* Link isn't broken */ if (moved) tryUpdate(); ! if (checkOriginal(original) != null) return; } catch (IOException e) { } --- 632,638 ---- /* Link isn't broken */ if (moved) tryUpdate(); ! if (checkOriginal(getOriginal()) != null) return; } catch (IOException e) { } *************** *** 513,519 **** private void tryUpdate() throws IOException { String result [] = read (getPrimaryFile ()); ! FileObject pf = original.getPrimaryFile (); if (result [IDX_PATH].equals (pf.getPath())) { if (result[IDX_FS] == null) { if (getPrimaryFile().getFileSystem() == pf.getFileSystem ()) --- 645,651 ---- private void tryUpdate() throws IOException { String result [] = read (getPrimaryFile ()); ! FileObject pf = getOriginal().getPrimaryFile (); if (result [IDX_PATH].equals (pf.getPath())) { if (result[IDX_FS] == null) { if (getPrimaryFile().getFileSystem() == pf.getFileSystem ()) *************** *** 563,569 **** } private static void updateShadowOriginal(final DataShadow shadow) { ! final FileObject primary = shadow.original.getPrimaryFile (); org.openide.util.RequestProcessor.postRequest (new Runnable () { public void run () { --- 695,701 ---- } private static void updateShadowOriginal(final DataShadow shadow) { ! final FileObject primary = shadow.getOriginal().getPrimaryFile (); org.openide.util.RequestProcessor.postRequest (new Runnable () { public void run () { *************** *** 595,601 **** --- 727,1142 ---- } } } + + + /** Utility method for getting the declared resource bundle for the + * shadow and looking the display name up in it. Looks for the attribute + * SystemFileSystem.localizingBundle first on the + * shadow's file, then on the original file it represents, then + * tries to find a key for whatever file it got the bundle attribute + * from. + */ + private static final String displayNameForShadow (DataShadow ds) { + FileObject fo = ds.getPrimaryFile(); + String bundlename = (String) fo.getAttribute ("SystemFileSystem.localizingBundle"); //NOI18N + if (bundlename == null) { + fo = ds.getOriginalPrimaryFile(); + bundlename = (String) fo.getAttribute("SystemFileSystem.localizingBundle"); //NOI18N + } + if (bundlename != null) { + try { + ResourceBundle rb = NbBundle.getBundle(bundlename); + return rb.getString (fo.getPath()); + } catch (MissingResourceException mre) { + ErrorManager.getDefault().log + (ErrorManager.INFORMATIONAL, + "Missing display name key for " + fo + " in resource bundle " + + bundlename); //NOI18N + ErrorManager.getDefault().notify(ErrorManager.WARNING, mre); + } + } + return ds.getName(); + } + + /** Utility method for fetching an attribute from the shadow's source file + * or if not present there, on the file pointed to by it, while not + * forcing DataObject instantiation. If checkParent is true, will + * also check for the attribute on the containing folder for the + * shadow (useful, e.g., for specifying HelpCtx for a group of templates)*/ + private static final Object attributeForShadow (DataShadow ds, String attr, boolean checkParent) { + FileObject fo = ds.getPrimaryFile(); + Object result = fo.getAttribute (attr); + if (result == null) { + fo = ds.getOriginalPrimaryFile(); + result = fo.getAttribute (attr); + } + if ((result == null) && checkParent) { + fo = ds.getPrimaryFile().getParent(); + result = fo.getAttribute (attr); + if (result == null) { + fo = ds.getOriginalPrimaryFile(); + result = fo.getAttribute (attr); + } + } + return result; + } + + + + /**Attempt to get the icon from the file's attributes without + * recourse to the underlying DataObject. */ + private static final Image getDeclaredIcon (DataShadow ds, int type) { + //XXX sfs not annotating icon; need to investigate further + //Try to get the icon from file attributes. + try { + if (!ds.getOriginalPrimaryFile().getFileSystem().equals ( + Repository.getDefault().getDefaultFileSystem())) return null; + Object iconRes=null; + switch (type) { + case BeanInfo.ICON_COLOR_32x32 : + iconRes = attributeForShadow (ds, "SystemFileSystem.icon32",false); //NOI18N + break; + default : + iconRes = attributeForShadow (ds, "SystemFileSystem.icon",false); //NOI18N + break; + } + if (iconRes instanceof java.net.URL) { + Image i = java.awt.Toolkit.getDefaultToolkit ().getImage ((java.net.URL) iconRes); + return i; + } + } catch (FileStateInvalidException fsie) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, fsie); + } + return null; + } + + /** Create a new data object from template. + * In DataShadow, this delegates to + * getOriginal().createFromTemplate() + * + * @param df data folder to create object in + * @param name name to give to the new object (or null + * if the name should be chosen according to the template) + * @return the new data object + * @exception IOException if an error occured + * + */ + protected DataObject handleCreateFromTemplate(DataFolder df, String name) throws IOException { + DataObject retValue; + retValue = getOriginal().createFromTemplate(df, name); + return retValue; + } + + + + /** A trivial class to make FilterNode.changeOriginal public + * for use by ProxyShadowNode. The base DataShadow root + * node is a MutableFilterNode that delegates either + * to a ProxyShadowNode or a ShadowNode, depending on + * whether the DataObject has been instantiated. */ + private static class MutableFilterNode extends FilterNode { + public MutableFilterNode (Node n) { + super (n); + } + + public void setOriginal (Node n) { + changeOriginal (n, true); + } + } + + /**A node that stands in for ShadowNode and tries to proxy + * display name and icon to the filesystem. When asked for + * data it can't provide (since it does not represent the + * node delegate for the underlying DataObject, which ideally + * is not yet instantiated), it will force instantiation of + * the DataObject (causing it to be replaced by a bona-fide + * ShadowNode) and return the result of the same call to its + * replacement. */ + private class ProxyShadowNode extends AbstractNode { + //This is an inner class of MutableFilterNode in order + //to have access to MutableFilterNode.changeOriginal(). + public ProxyShadowNode (ProxyDOChildren children) { + super (children); //XXX + } + + private boolean dying=false; + private ShadowNode replacement=null; + /* The method below should *probably* be synchronized, + * as it could conceivably be entered before + * dying is set to true. This causes a deadlock + * in the options dialog, when FormEditorSettings + * is selected. TODO: See if the ShadowNode for + * a single shadow is ever created twice. -TDB + */ + /** Create a ShadowNode to replace this instance and + * call changeOriginal on the MutableFilterNode that + * was proxying this instance. */ + private Node die() { + if (dying) return replacement; + //Get the filter node that proxies this node + MutableFilterNode mfn = (MutableFilterNode)DataShadow.this.getNodeDelegate(); + //Create a new ShadowNode to represent the DataObject + dying=true; + replacement = new ShadowNode (DataShadow.this); + //Node, replace thyself + mfn.setOriginal(replacement); + return replacement; + } + + public Node.Cookie getCookie (Class clazz) { + if (clazz.isInstance (DataShadow.this)) { + return DataShadow.this; + } + Node n = die(); + return getOriginal().getCookie(clazz); + } + + + public String getDisplayName() { + //if original is null, see if the FS will supply the filename + //from attributes and we can avoid instantiating the DataObject + try { + String name = displayNameForShadow (DataShadow.this); + if (name == null) { + name = ""; + name = getOriginalPrimaryFile().getFileSystem().getStatus().annotateName(name, files()); + } + if (name != null) { + return name; + } + } catch (FileStateInvalidException fsie) { + ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, fsie); + } + return getOriginal().getNodeDelegate().getDisplayName(); + } + + public Image getOpenedIcon(int type) { + Image i = baseIcon (type); + if (i != null) { + return i; + } else { + Node n = die(); + return n.getOpenedIcon(type); + } + } + + public Image getIcon(int type) { + Image i = baseIcon (type); + if (i != null) { + return i; + } else { + Node n = die(); + return n.getIcon (type); + } + } + + Image icon; + boolean noDeclaredIcon = false; + private final Image baseIcon(int type) { + if ((icon == null) && !noDeclaredIcon) { + icon = getDeclaredIcon (DataShadow.this, type); + noDeclaredIcon = icon == null; + } + return icon; + } + /* Renames the shadow data object. + * @param name new name for the object + * @exception IllegalArgumentException if the rename failed */ + public void setName (String name) { + //Modified from ShadowNode - TDB + try { + if (!name.equals (DataShadow.this.getName ())) { + rename (name); + // (String strFile, String strFS, FileSystem origSystem) + FileObject orig = getOriginalPrimaryFile(); + String result [] = read (orig); + FileObject fo = checkOriginal (result [IDX_PATH], result [IDX_FS], orig.getFileSystem()); + if (fo.isRoot()) { + orig.setAttribute (ShadowNode.ATTR_USEOWNNAME, Boolean.TRUE); + } + fireDisplayNameChange (null, null); + fireNameChange (null, null); + } + } catch (IOException ex) { + throw new IllegalArgumentException (ex.getMessage ()); + } + } + + MessageFormat descriptionFormat = null; + /* Creates description based on the original one. */ + public String getShortDescription () { + //Note this will instantiate the DO. This node can + //remain, however, unless asked for something it can't give. + //Cannot use die() pattern here - changeOriginal() in FilterNode + //will create an endless loop because it also calls this + //method in order to fire changes. + //Lacking a spec for specifying short description for a + //node in fs layers, this means the DO will be created + //when a temnplate node is asked for a tooltip. + //IMO, this is not such a bad thing, as this means + //that the classloading will be done when the user is + //browsing the nodes, rather than waiting for a wizard + //step, etc. + return getOriginal().getNodeDelegate().getShortDescription(); + } + + public org.openide.util.actions.SystemAction[] getActions () { + Node n = die(); + return n.getActions(); + } + + public org.openide.util.actions.SystemAction[] getContextActions () { + Node n = die(); + return n.getContextActions(); + } + + public String getName () { + return DataShadow.this.getName(); + } + + /** Returns a help context. Will look for it first on the + * file pointed to by the shadow, in the attribute + * helpID. If that fails, it will look for + * the same attribute on the parent folder of the shadow + * (e.g. Templates/). If that also fails, it + * falls back to instantiating the DataObject and retrieving + * the value from its node delegate. */ + public HelpCtx getHelpCtx() { + //Find helpID as an attribute of the primary file or original's primary file, + //or parent folder of either, shadow first + Object id= attributeForShadow (DataShadow.this, "SystemFileSystem.helpID", true); //NOI18N + + if (id instanceof String) { + return new HelpCtx ((String) id); + } else if (id instanceof java.net.URL) { + return new HelpCtx ((java.net.URL) id); + } + //if the check failed, instantiate the DataObject and return it + Node n = die(); + return n.getHelpCtx(); + } + + public org.openide.util.actions.SystemAction getDefaultAction() { + Node n = die(); + return n.getDefaultAction(); + } + + public Component getCustomizer () { + Node n = die(); + return n.getCustomizer(); + } + + public Node.Handle getHandle() { + //XXX probably could return a real handle for this node, which + //will avoid creating the DO on deserialization. + Node n = die(); + return n.getHandle(); + } + + public NewType[] getNewTypes() { + Node n = die(); + return n.getNewTypes(); + } + + public PasteType getDropType (Transferable t, int action, int index) { + Node n = die(); + return n.getDropType (t, action, index); + } + + public PasteType[] getPasteType (Transferable t) { + Node n = die(); + return n.getPasteTypes(t); + } + + public Node.PropertySet[] getPropertySets() { + Node n = die(); + return n.getPropertySets(); + } + + public boolean hasCustomizer() { + Node n = die(); + return n.hasCustomizer(); + } + + public void setShortDescription (String s) { + Node n = die(); + n.setShortDescription (s); + } + + public void setDisplayName(String s) { + Node n = die(); + n.setShortDescription (s); + } + + /* @return obj.isDeleteAllowed () */ + public boolean canDestroy () { + return DataShadow.this.isDeleteAllowed (); + } + + /* Destroyes the node + */ + public void destroy () throws IOException { + synchronized (DataShadow.this.nodes) { + DataShadow.this.nodes.remove (this); + } + DataShadow.this.delete (); + // super.destroy (); + } + + /** @return true if shadow can be renamed + */ + public final boolean canRename () { + return DataShadow.this.isRenameAllowed (); + } + + /* Returns true if this object allows copying. + * @returns true if so + */ + public final boolean canCopy () { + return DataShadow.this.isCopyAllowed (); + } + + /* Returns true if this object allows cutting. + * @returns true if so + */ + public final boolean canCut () { + return DataShadow.this.isMoveAllowed (); + } + + public Node cloneNode() { + return new ProxyShadowNode(new ProxyDOChildren()); + } + } + + /**A children object that avoids forcing the original DO to be + *instantiated until addNotify() is called. */ + private class ProxyDOChildren extends Children.Keys { + + public ProxyDOChildren() { + } + + public void addNotify() { + //avoid creating the DataObject if the primary file specifies + //that there are no children + Boolean isLeaf = (Boolean) attributeForShadow (DataShadow.this, "isLeaf",false); + if ((isLeaf == null) || Boolean.FALSE.equals (isLeaf)) + setKeys (getOriginal().getNodeDelegate().getChildren().getNodes()); + else + setKeys (Collections.EMPTY_SET); + } + + public Node[] createNodes (Object key) { + Node n = (Node) key; + return new Node[] {new FilterNode (n)}; + } + + public void removeNotify() { + setKeys (Collections.EMPTY_SET); + } + } + + /** Node for a shadow object. */ protected static class ShadowNode extends FilterNode { /** message to create name of node */ *************** *** 647,653 **** try { if (!name.equals (obj.getName ())) { obj.rename (name); ! if (obj.original.getPrimaryFile ().isRoot ()) { obj.getPrimaryFile ().setAttribute (ATTR_USEOWNNAME, Boolean.TRUE); } fireDisplayNameChange (null, null); --- 1188,1194 ---- try { if (!name.equals (obj.getName ())) { obj.rename (name); ! if (obj.getOriginal().getPrimaryFile ().isRoot ()) { obj.getPrimaryFile ().setAttribute (ATTR_USEOWNNAME, Boolean.TRUE); } fireDisplayNameChange (null, null); *************** *** 684,693 **** /* 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) { --- 1225,1241 ---- /* Creates name based on the original one. */ public String getDisplayName () { + //enforce naming consistency so name cannot accidentally + //change if the ShadowNode is instantiated for a lazily + //created DataObject. Who uses lazy instantiation shall + //forfeit the right for the shadow to display a link + String n = obj.wasLazy ? + displayNameForShadow (obj) : null; + if (n != null) return n; if (format == null) { format = new MessageFormat (NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowName")); } ! n = format.format (createArguments ()); try { obj.getPrimaryFile().getFileSystem().getStatus().annotateName(n, obj.files()); } catch (FileStateInvalidException fsie) { *************** *** 700,706 **** 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... --- 1248,1254 ---- private Object[] createArguments () { String origDisp; String shadowName = obj.getName (); ! if (obj.getOriginal().isValid()) { origDisp = obj.original.getNodeDelegate().getDisplayName(); } else { // We will soon be a broken data shadow, in the meantime... *************** *** 781,787 **** // ignore } } ! return null; } /* @return obj.isDeleteAllowed () */ --- 1329,1335 ---- // ignore } } ! return getDeclaredIcon (obj, type); } /* @return obj.isDeleteAllowed () */ *************** *** 827,832 **** --- 1375,1381 ---- * @return the cookie or null */ public Node.Cookie getCookie (Class cl) { + if (cl.isInstance(DataShadow.class)) return obj; Node.Cookie c = obj.getCookie (cl); if (c != null) { return c; Index: openide/src/org/openide/loaders/TemplateWizard.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/TemplateWizard.java,v retrieving revision 1.68 diff -c -r1.68 TemplateWizard.java *** openide/src/org/openide/loaders/TemplateWizard.java 17 Feb 2003 15:23:20 -0000 1.68 --- openide/src/org/openide/loaders/TemplateWizard.java 24 Feb 2003 12:18:18 -0000 *************** *** 528,542 **** obj.getPrimaryFile().setAttribute(EA_DESCRIPTION, url); } ! /** Method to get a description for a data object. * @param obj data object to attach description to * @return the url with description or null */ public static URL getDescription (DataObject obj) { ! URL desc = (URL)obj.getPrimaryFile().getAttribute(EA_DESCRIPTION); if (desc != null) return desc; // Backwards compatibility: ! String rsrc = (String) obj.getPrimaryFile ().getAttribute (EA_DESC_RESOURCE); if (rsrc != null) { try { URL better = new URL ("nbresloc:/" + rsrc); // NOI18N --- 528,550 ---- obj.getPrimaryFile().setAttribute(EA_DESCRIPTION, url); } ! /** Get a description for a data object. The description is ! * specified as a URL attribute of the DataObject's primary file named ! * templateWizardURL. * @param obj data object to attach description to * @return the url with description or null */ public static URL getDescription (DataObject obj) { ! FileObject primaryFile; ! if (obj instanceof DataShadow) { ! primaryFile = ((DataShadow) obj).getOriginalPrimaryFile(); ! } else { ! primaryFile = obj.getPrimaryFile(); ! } ! URL desc = (URL)primaryFile.getAttribute(EA_DESCRIPTION); if (desc != null) return desc; // Backwards compatibility: ! String rsrc = (String) primaryFile.getAttribute (EA_DESC_RESOURCE); if (rsrc != null) { try { URL better = new URL ("nbresloc:/" + rsrc); // NOI18N *************** *** 592,598 **** */ public static void setIterator (DataObject obj, Iterator iter) throws IOException { ! obj.getPrimaryFile().setAttribute(EA_ITERATOR, iter); } /** Finds a custom iterator attached to a template that should --- 600,609 ---- */ public static void setIterator (DataObject obj, Iterator iter) throws IOException { ! if ((obj instanceof DataShadow) && ((DataShadow) obj).wasLazy) ! ((DataShadow) obj).getOriginalPrimaryFile().setAttribute (EA_ITERATOR, iter); ! else ! obj.getPrimaryFile().setAttribute(EA_ITERATOR, iter); } /** Finds a custom iterator attached to a template that should *************** *** 603,610 **** * @param obj the data object * @return custom iterator or null */ ! public static Iterator getIterator (DataObject obj) { ! Iterator it = (Iterator)obj.getPrimaryFile ().getAttribute(EA_ITERATOR); if (it != null) { return it; } --- 614,625 ---- * @param obj the data object * @return custom iterator or null */ ! public static Iterator getIterator (DataObject obj) { ! Iterator it; ! it = (Iterator) obj.getPrimaryFile().getAttribute (EA_ITERATOR); ! if ((it == null) && (obj instanceof DataShadow)) { ! it = (Iterator)((DataShadow) obj).getOriginalPrimaryFile ().getAttribute(EA_ITERATOR); ! } if (it != null) { return it; } Index: openide/src/org/openide/loaders/TemplateWizard1.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/TemplateWizard1.java,v retrieving revision 1.38 diff -c -r1.38 TemplateWizard1.java *** openide/src/org/openide/loaders/TemplateWizard1.java 29 Jan 2003 01:42:48 -0000 1.38 --- openide/src/org/openide/loaders/TemplateWizard1.java 24 Feb 2003 12:18:18 -0000 *************** *** 182,198 **** Node n = (Node)key; String nodeName = n.getDisplayName(); ! DataObject obj = null; ! DataShadow shadow = (DataShadow)n.getCookie (DataShadow.class); ! if (shadow != null) { ! // I need DataNode here to get localized name of the ! // shadow, but without the ugly "(->)" at the end ! DataNode dn = new DataNode(shadow, Children.LEAF); ! nodeName = dn.getDisplayName(); ! obj = shadow.getOriginal(); ! n = obj.getNodeDelegate(); ! } if (obj == null) obj = (DataObject)n.getCookie (DataObject.class); --- 182,192 ---- Node n = (Node)key; String nodeName = n.getDisplayName(); ! //try getting a shadow first, for lazy instantiation ! DataObject obj = (DataObject) n.getCookie(DataShadow.class); + //if that fails, force instantiation of the DataObject and get + //the real DataObject if (obj == null) obj = (DataObject)n.getCookie (DataObject.class); *************** *** 509,515 **** boolean enable = false; Node[] n = getExplorerManager().getSelectedNodes(); if (n.length == 1) { ! template = (DataObject)n[0].getCookie (DataObject.class); enable = template != null && template.isTemplate(); } return enable; --- 503,511 ---- boolean enable = false; Node[] n = getExplorerManager().getSelectedNodes(); if (n.length == 1) { ! template = (DataObject) n[0].getCookie (DataShadow.class); ! if (template == null) ! template = (DataObject)n[0].getCookie (DataObject.class); enable = template != null && template.isTemplate(); } return enable; *************** *** 545,551 **** public boolean isLeaf (Object o) { Node n = Visualizer.findNode(o); ! DataObject obj = (DataObject)n.getCookie (DataObject.class); return obj == null || obj.isTemplate (); } --- 541,549 ---- public boolean isLeaf (Object o) { Node n = Visualizer.findNode(o); ! DataObject obj = (DataShadow) n.getCookie (DataShadow.class); ! if (obj == null) ! obj = (DataObject)n.getCookie (DataObject.class); return obj == null || obj.isTemplate (); } Index: java/src/org/netbeans/modules/java/Bundle.properties =================================================================== RCS file: /cvs/java/src/org/netbeans/modules/java/Bundle.properties,v retrieving revision 1.70 diff -c -r1.70 Bundle.properties *** java/src/org/netbeans/modules/java/Bundle.properties 30 Jan 2003 12:12:04 -0000 1.70 --- java/src/org/netbeans/modules/java/Bundle.properties 24 Feb 2003 12:18:18 -0000 *************** *** 221,239 **** Templates/Services/Executor/JavaInternalExecutor.settings=Internal Execution # Templates ! Templates/Privileged/Package.shadow=Java Package ! Templates/Privileged/Main.shadow=Java Main Class ! Templates/Privileged/Class.shadow=Java Class ! Templates/Package=Java Package Templates/Classes=Java Classes ! Templates/Classes/Applet.java=Applet ! Templates/Classes/Class.java=Java Class ! Templates/Classes/Empty.java=Empty Java File ! Templates/Classes/Exception.java=Java Exception ! Templates/Classes/Interface.java=Java Interface ! Templates/Classes/JApplet.java=JApplet ! Templates/Classes/Main.java=Java Main Class Mount/java=Java Libraries --- 221,250 ---- Templates/Services/Executor/JavaInternalExecutor.settings=Internal Execution # Templates ! #Templates/Privileged/Package.shadow=Java Package ! #Templates/Privileged/Main.shadow=Java Main Class ! #Templates/Privileged/Class.shadow=Java Class ! #Templates/Package=Java Package Templates/Classes=Java Classes ! #Templates/Classes/Applet.java=Applet ! #Templates/Classes/Class.java=Java Class ! #Templates/Classes/Empty.java=Empty Java File ! #Templates/Classes/Exception.java=Java Exception ! #Templates/Classes/Interface.java=Java Interface ! #Templates/Classes/JApplet.java=JApplet ! #Templates/Classes/Main.java=Java Main Class ! ! #keys below allow templates to be lazily loaded. They should exactly ! #match the above template names ! templatedata/java/Package=Java Package ! templatedata/java/Applet.java=Applet ! templatedata/java/Class.java=Java Class ! templatedata/java/Empty.java=Empty Java File ! templatedata/java/Exception.java=Java Exception ! templatedata/java/Interface.java=Java Interface ! templatedata/java/JApplet.java=JApplet ! templatedata/java/Main.java=Java Main Class Mount/java=Java Libraries Index: java/src/org/netbeans/modules/java/resources/mf-layer.xml =================================================================== RCS file: /cvs/java/src/org/netbeans/modules/java/resources/mf-layer.xml,v retrieving revision 1.38 diff -c -r1.38 mf-layer.xml *** java/src/org/netbeans/modules/java/resources/mf-layer.xml 31 Jan 2003 07:51:33 -0000 1.38 --- java/src/org/netbeans/modules/java/resources/mf-layer.xml 24 Feb 2003 12:18:18 -0000 *************** *** 17,43 **** ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! --- 17,24 ---- ! ! *************** *** 45,57 **** ! ! ! ! ! ! --- 26,34 ---- + ! *************** *** 59,64 **** --- 36,42 ---- + *************** *** 68,73 **** --- 46,52 ---- + *************** *** 75,80 **** --- 54,60 ---- + *************** *** 82,93 **** --- 62,75 ---- + + *************** *** 95,100 **** --- 77,83 ---- + *************** *** 102,109 **** --- 85,165 ---- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +