Index: openide/src/org/openide/loaders/TemplateWizard.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/TemplateWizard.java,v retrieving revision 1.65 diff -r1.65 TemplateWizard.java 484c484,486 < /** Method to get a description for a data object. --- > /** Get a description for a data object. The description is > * specified as a URL attribute of the DataObject's primary file named > * templateWizardURL. 489c491,497 < URL desc = (URL)obj.getPrimaryFile().getAttribute(EA_DESCRIPTION); --- > FileObject primaryFile; > if (obj instanceof DataShadow) { > primaryFile = ((DataShadow) obj).getOriginalPrimaryFile(); > } else { > primaryFile = obj.getPrimaryFile(); > } > URL desc = (URL)primaryFile.getAttribute(EA_DESCRIPTION); 492c500 < String rsrc = (String) obj.getPrimaryFile ().getAttribute (EA_DESC_RESOURCE); --- > String rsrc = (String) primaryFile.getAttribute (EA_DESC_RESOURCE); 558,559c566,572 < public static Iterator getIterator (DataObject obj) { < Iterator it = (Iterator)obj.getPrimaryFile ().getAttribute(EA_ITERATOR); --- > public static Iterator getIterator (DataObject obj) { > Iterator it; > if (obj instanceof DataShadow) { > it = (Iterator)((DataShadow) obj).getOriginalPrimaryFile ().getAttribute(EA_ITERATOR); > } else { > it = (Iterator)obj.getPrimaryFile ().getAttribute(EA_ITERATOR); > } 713a727 > //XXX - handle replacing the template here Index: openide/src/org/openide/loaders/DataObject.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataObject.java,v retrieving revision 1.83 diff -r1.83 DataObject.java 341a342,348 > return isTemplateImpl(); > } > > /** Implementation of isTemplate test. Overridden by > * DataShadow to return the template value for the > * file pointed to. */ > boolean isTemplateImpl () { 344c351 < if (o instanceof Boolean) --- > if (o instanceof Boolean) 347c354 < } --- > } Index: openide/src/org/openide/loaders/DataShadow.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataShadow.java,v retrieving revision 1.48 diff -r1.48 DataShadow.java 16c16,17 < import java.awt.datatransfer.Transferable; --- > import java.awt.Component; > import java.awt.datatransfer.*; 31c32 < import org.openide.util.datatransfer.ExTransferable; --- > import org.openide.util.datatransfer.*; 183a185,203 > /** 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. > */ > public DataShadow (FileObject fo, DataLoader loader) throws DataObjectExistsException { > super (fo, loader); > } > 188c208 < if (original == null) --- > if ((original == null) && (!canInstantiateLazily (getPrimaryFile()))) 212a233,234 > > 313a336,352 > > /** Returns the primary file of the DataObject this > * shadow links to. If this DataShadow was instantiated > * without the DataShadow 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. > */ > public final FileObject getOriginalPrimaryFile() { > try { > return validate (getPrimaryFile()); > } catch (java.io.IOException ioe) { > ErrorManager.getDefault().notify (ErrorManager.INFORMATIONAL, ioe); > return getOriginal().getPrimaryFile(); > } > } 322a362,392 > 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) { > return (fo.getAttribute("SystemFileSystem.icon") != null && //NOI18N > fo.getAttribute ("SystemFileSystem.localizingBundle") != null) && > Boolean.TRUE.equals (fo.getAttribute("isLazy")); //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) { > ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe); > } > 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 { 325c395 < return DataObject.find (fo); --- > return fo; 327a398 > 423a495,497 > if (original == null) { > resolveOriginal(); > } 426a501,512 > /** For the case of lazy initialization, find the > * DataObject. > */ > private void resolveOriginal () { > try { > FileObject fo = this.getPrimaryFile(); > init (deserialize (fo)); > } catch (java.io.IOException ioe) { > ErrorManager.getDefault().notify(ErrorManager.ERROR, ioe); > } > } > 433a520,539 > /**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 (o != null) && Boolean.TRUE.equals (o); > } > } catch (java.io.IOException ioe) { > ErrorManager.getDefault().notify (ErrorManager.INFORMATIONAL, ioe); > } > } > return getOriginal().isTemplate(); > } > 437c543,550 < return new ShadowNode (this); --- > Node realNode; > if ((original == null) && canInstantiateLazily (getPrimaryFile())) { > realNode = new ProxyShadowNode(new ProxyDOChildren()); > } else { > realNode = new ShadowNode(this); > } > MutableFilterNode result = new MutableFilterNode (realNode); > return result; 483c596 < return original.handleCreateShadow (f); --- > return getOriginal().handleCreateShadow (f); 491c604 < return original.getCookie (this, c); --- > return getOriginal().getCookie (this, c); 504c617 < if (checkOriginal(original) != null) --- > if (checkOriginal(getOriginal()) != null) 517c630 < FileObject pf = original.getPrimaryFile (); --- > FileObject pf = getOriginal().getPrimaryFile (); 567c680 < final FileObject primary = shadow.original.getPrimaryFile (); --- > final FileObject primary = shadow.getOriginal().getPrimaryFile (); 598a712 > 599a714,1124 > /** 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) { > ResourceBundle rb = NbBundle.getBundle(bundlename); > if (rb != null) { > try { > return rb.getString (fo.toString()); > } catch (MissingResourceException mre) { > ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, mre); > } > } > } > return null; > } > > /** 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 (!getOriginalPrimaryFile().getFileSystem().equals ( > // Repository.getDefault().getDefaultFileSystem())) return null; > > Object iconRes = attributeForShadow (ds, "SystemFileSystem.icon",false); //NOI18N > if (iconRes instanceof java.net.URL) { > Image i = java.awt.Toolkit.getDefaultToolkit ().getImage ((java.net.URL) iconRes); > ds.getPrimaryFile().getFileSystem().getStatus().annotateIcon (i, type, ds.files()); > 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 == DataShadow.class) { > return DataShadow.this; > } > if (clazz == DataObject.class) { > Thread.dumpStack(); > return getOriginal(); > } > > Node n = die(); > return n.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); > name = getOriginalPrimaryFile().getFileSystem().getStatus().annotateName(name, files()); > if (name.length() > 0) 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.getIcon(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; > /* > //code below should theoretically work but doesn't. > Image result = super.getIcon(type); > try { > FileObject fo = getOriginalPrimaryFile(); > fo.getFileSystem().getStatus().annotateIcon(result, type, files()); > } catch (FileStateInvalidException fsie) { > ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, fsie); > } > return result; > */ > } > > /* 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() { > return getOriginal().getNodeDelegate().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); > } > } > > 651c1176 < if (obj.original.getPrimaryFile ().isRoot ()) { --- > if (obj.getOriginal().getPrimaryFile ().isRoot ()) { 688,691c1213,1214 < if (format == null) { < format = new MessageFormat (NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowName")); < } < String n = format.format (createArguments ()); --- > // String n = displayNameForShadow(obj); > String n = ""; 692a1216,1224 > if (n!=null && n.length() != 0) { > obj.getPrimaryFile().getFileSystem().getStatus().annotateName(n, obj.files()); > //if a display name is explicitly declared, do not use link format > return n; > } > if (format == null) { > format = new MessageFormat (NbBundle.getBundle (DataShadow.class).getString ("FMT_shadowName")); > } > n = format.format (createArguments ()); 704c1236 < if (obj.original.isValid()) { --- > if (obj.getOriginal().isValid()) { 785c1317 < return null; --- > return getDeclaredIcon (obj, type); 830a1363 > if (cl == DataShadow.class) return obj; Index: openide/src/org/openide/loaders/DataLoaderPool.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataLoaderPool.java,v retrieving revision 1.94 diff -r1.94 DataLoaderPool.java 816,817c816,830 < DataObject d = DataShadow.deserialize (primaryFile); < if (d != null) return new DataShadow (primaryFile, d, this); --- > //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); > }