NetBeans Forums

 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister   ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
  

Action instance files and instanceCreate factory methods

 
Post new topic   Reply to topic    NetBeans Forums -> NetBeans Platform Users
View previous topic :: View next topic  
Author Message
StuartCray



Joined: 27 Apr 2009
Posts: 5

PostPosted: Mon Apr 27, 2009 3:21 pm    Post subject: Action instance files and instanceCreate factory methods Reply with quote

Hello,

I have a question regarding defining actions in the layer.xml file.

At present our 'actions' are defined in classes that implement an interface Command and do not implement javax.swing.Action. The Commnad interface has methods to obtain a javax.swing.Action whose actionPerformed method delegates to the Command. Part of the reason for this is that the command can maintain multiple instances of the action associated with different scopes. For example if there are two TreeView explorer components with different views of the same data, changing the selection in one should not necessarily change the state of an action in both components.

This works fine for adding actions to TopComponents as toolbars, popup menus and buttons. The issue I am having is adding these to the main menu and toolbar. Ideally I would like to be able to define these in the Actions and Menus folders in the layers.xml file and let Netbeans take care of the rest.

I have added a static method createAction() to the Command implementation class to return an action of type CommandAction, which implements javax.swing.Action. I have tried various combinations of file name and instanceCreate, instanceClass and instanceOf attributes, but always end up with either an instatiation exception or sometimes a ClassNotFoundException.

I have not found much documentation on files and their attributes in the file system except for the FAQ in the Wiki, which as far as I can tell does not cover this situation.

One thing that is unclear (to me) is whether the name of an instance file has to represent the class of the instance to be created or can be any arbitrary name.

What I want to aceive is something along the lines of
Code:

  <folder name="Actions">
     <folder name="File">
       <file name="myActionIdentifier.instance" > -- All actions will be of the same type.
         <attr name="instanceCreate" methodvalue="my.package.MyCommand.createAction" />
          <!--
             Any other attributes?
          -->
       </file>
     </folder>
  </folder>

I hope this makes some sense. Thanks in advance for any help.

Stuart
Back to top
Jesse Glick
Posted via mailing list.





PostPosted: Mon Apr 27, 2009 8:10 pm    Post subject: Re: Action instance files and instanceCreate factory methods Reply with quote

StuartCray wrote:
Quote:
I have added a static method createAction() to the Command implementation class to return an action of type CommandAction, which implements javax.swing.Action.

It it possible to do this.

Quote:
I have not found much documentation on files and their attributes in the file system except for the FAQ in the Wiki

There is

http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/doc-files/api.html#lookup

though it is somewhat old and probably should be updated.

Quote:
One thing that is unclear (to me) is whether the name of an instance file has to represent the class of the instance to be created or can be any arbitrary name.

It can be arbitrary. If you have no attributes at all then the name is used to pick a class to load, but for all other scenarios the filename is ignored.

Quote:
<file name="myActionIdentifier.instance">
<attr name="instanceCreate" methodvalue="my.package.MyCommand.createAction" />
<!-- Any other attributes? -->

This is OK if MyCommand.createAction takes no parameters returns an Action based on MyCommand. However earlier you said that you had a generic Command.createAction,
presumably taking a Command argument. Probably what you really want is this:

public class CommandFactory {
/* no instances */ private CommandFactory() {}
public static Action create(Map<String,?> params) {
Command c = (Command) params.get("command");
return Command.create(c);
}
}

Now you can write Command impls without any mention of Action in them. To make an Action from one, say MyCommand where this class has a public no-arg constructor:

<file name="whatever.instance">
<attr name="instanceCreate" methodvalue="pkg1.CommandFactory.create"/>
<attr name="command" newvalue="pkg2.MyCommand"/>
</file>

Here CommandFactory.create is being passed a map {"command" -> pkg2.MyCommand@123456} and returning a corresponding action. In general, a factory method can request a Map
of all the attributes on the file object. (It can also accept FileObject as a parameter, though this is now semi-deprecated since it introduces an otherwise unnecessary
dependency on the Filesystems API.)
Back to top
StuartCray



Joined: 27 Apr 2009
Posts: 5

PostPosted: Mon Apr 27, 2009 9:26 pm    Post subject: Reply with quote

Jesse

Thank you very much for your reply.

The fact that the factory method can accept the attributes as a Map parameter is perfect, if I understand you correctly. I am at home at the moment so cannot test this out, but will try in the morning (UK time).

Once again, many thanks

Stuart
Back to top
StuartCray



Joined: 27 Apr 2009
Posts: 5

PostPosted: Tue Apr 28, 2009 2:00 pm    Post subject: Reply with quote

I have managed to get this working but have some observations.

My layers.xml looks like

Code:

<file name="saveStorableAction.instance">
  <attr name="instanceCreate"
         methodvalue="com.salica.aden.client.ui.action.CommandActionFactory.getAction" />
  <attr name="commandId" stringvalue="saveStorableCommand" />
</file>


Tne CommandActionFactory
Code:

public class CommandActionFactory {

    public static Action getAction(Map<String, ?> attributes) {
        log.debug("getAction(Map)");
        String commandId = (String)attributes.get("commandId");
        log.debug("commandId = " + commandId);
        return getAction(commandId);
    }

    public static Action getActionById(String commandId) {
        log.debug("getActionById(commandId = " + commandId + ")");
        CommandManager commandManager = CommandUtil.getCommandManager();
        Command command = commandManager.getCommand(commandId);
        log.debug("command = " + command);
        Action action = command.getGlobalAction();
        log.debug("action = " + action);
        return action;
    }
    private static Log log = LogFactory.getLog(CommandActionFactory.class);
}




1. Originally my getAction method passed the attributes map to the logger call in the first line. This causes a StackOverflowError

Code:

java.lang.StackOverflowError
   at java.security.AccessController.doPrivileged(Native Method)
   at java.io.PrintWriter.<init>(PrintWriter.java:78)
   at java.io.PrintWriter.<init>(PrintWriter.java:62)
   at org.netbeans.core.startup.TopLogging$NbFormatter.print(TopLogging.java:584)
   at org.netbeans.core.startup.TopLogging$NbFormatter.format(TopLogging.java:551)
   at java.util.logging.StreamHandler.publish(StreamHandler.java:179)
   at org.netbeans.core.startup.TopLogging$NonClose.publish(TopLogging.java:428)
   at java.util.logging.Logger.log(Logger.java:472)
   at java.util.logging.Logger.doLog(Logger.java:494)
   at java.util.logging.Logger.log(Logger.java:583)
   at org.netbeans.core.startup.layers.BinaryFS$AttrImpl.getValue(BinaryFS.java:505)
   at org.netbeans.core.startup.layers.BinaryFS$BFSBase.getAttribute(BinaryFS.java:370)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:826)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:786)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:822)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:786)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:719)
   at org.netbeans.core.startup.layers.BinaryFS$FOEntry.getValue(BinaryFS.java:916)
   at java.util.AbstractMap.toString(AbstractMap.java:487)
   at java.lang.String.valueOf(String.java:2827)
   at java.lang.StringBuilder.append(StringBuilder.java:115)
   at com.salica.aden.client.ui.nbp.action.NBCommandActionFactory.getAction(NBCommandActionFactory.java:19)
   at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:597)
   at org.netbeans.core.startup.layers.BinaryFS$AttrImpl.methodValue(BinaryFS.java:541)
   at org.netbeans.core.startup.layers.BinaryFS$AttrImpl.getValue(BinaryFS.java:485)
   at org.netbeans.core.startup.layers.BinaryFS$BFSBase.getAttribute(BinaryFS.java:370)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:826)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:786)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:822)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:786)
   at org.openide.filesystems.MultiFileObject.getAttribute(MultiFileObject.java:719)
   at org.netbeans.core.startup.layers.BinaryFS$FOEntry.getValue(BinaryFS.java:916)
   at java.util.AbstractMap.toString(AbstractMap.java:487)
   at java.lang.String.valueOf(String.java:2827)
   at java.lang.StringBuilder.append(StringBuilder.java:115)
   at com.salica.aden.client.ui.nbp.action.NBCommandActionFactory.getAction(NBCommandActionFactory.java:19)
        ... etc



2. The getActionById method taking a String was originally an overloaded version of getAction. When both methods were called getAction, the String version was called rather than the Map version, but the value passed in was "instanceCreate". This seems a little strange to me as this is an attribute name not a value.

Neither of these issues is a problem for me, I just thought I would post my experiences in case it is of any use to any one else.

Thanks again for your help.

Stuart
Back to top
Jesse Glick
Posted via mailing list.





PostPosted: Tue Apr 28, 2009 4:05 pm    Post subject: Re: Action instance files and instanceCreate factory methods Reply with quote

StuartCray wrote:
Quote:
1. Originally my getAction method passed the attributes map to the logger call in the first line. This causes a StackOverflowError

at java.lang.StringBuilder.append(StringBuilder.java:115)
at com.salica.aden.client.ui.nbp.action.NBCommandActionFactory.getAction(NBCommandActionFactory.java:19)

Yes, because the Map.toString() is asking for .get("instanceCreate"), which it is in the process of loading. Probably the NB infrastructure code could specially override
toString() to exclude an attr being loaded, but better to just not pass the map to the logger.

Quote:
2. The getActionById method taking a String was originally an overloaded version of getAction. When both methods were called getAction, the String version was called
rather than the Map version, but the value passed in was "instanceCreate". This seems a little strange to me as this is an attribute name not a value.

IIRC you can have various signatures for a factory method:

.methname()
.methname(String) // attr name
.methname(FileObject) // *.instance
.methname(FileObject, String) // *.instance, attr name
.methname(String, FileObject) // same
.methname(Map<String,?>) // all attrs

It is best not to have multiple overloads of the factory method, since "methodvalue" specifies only a method name, not a signature.
Back to top
StuartCray



Joined: 27 Apr 2009
Posts: 5

PostPosted: Tue Apr 28, 2009 4:26 pm    Post subject: Reply with quote

Quote:
Yes, because the Map.toString() is asking for .get("instanceCreate"), which it is in the process of loading.


That makes sense. Not a problem - just something to be aware of.

Quote:
IIRC you can have various signatures for a factory method:


I can see the usage of the FileObject and Map versions - you can get attribute values from their names. Just out of curiosity, when would you use the versions that take an attribute name parameter? What can you do with the attribute name, particularly in the version that doesn't also take a FileObject? And which attribute would it be if there are multiple attributes?
Back to top
Jesse Glick
Posted via mailing list.





PostPosted: Wed Apr 29, 2009 2:04 pm    Post subject: Re: Action instance files and instanceCreate factory methods Reply with quote

StuartCray wrote:
Quote:
when would you use the versions that
take an attribute name parameter?

No idea, actually.

Quote:
which attribute would
it be if there are multiple attributes?

The one being loaded.
Back to top
StuartCray



Joined: 27 Apr 2009
Posts: 5

PostPosted: Wed Apr 29, 2009 2:12 pm    Post subject: Reply with quote

Jesse,

Thanks very much for your time and help.

As I am still on the learning curve with the Netbeans Platform, I expect I will be back!

Stuart
Back to top
Display posts from previous:   
Post new topic   Reply to topic    NetBeans Forums -> NetBeans Platform Users All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Powered by phpBB
By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo