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);
> }