Subclassing code generators (example: Swing UI) [message #1386854] |
Sat, 21 June 2014 15:36 |
Gary Worsham Messages: 176 Registered: September 2013 |
Senior Member |
|
|
I'm extending my DSL to help create UI components of my Swing application.
For example, I am using JSliders and JCheckBoxes.
Each type of Swing control has a few things that need to happen. This is independent of Xtend, I am just trying to describe the design challenge.
#1 Declare the variable that the control is going to adjust.
#2 Declare and initialize the control itself.
#3 Add the control to the Swing JFrame that this class uses, inside of a "Runnable" construct that puts the code on the event thread which is Swing requirement for thread safety.
#4 Set the Listener for the control to "this".
#5 Create a suitable "Listener" for the Swing control. For example, JSliders use a ChangeListener and JCheckBoxes use ActionListeners, ChangeListeners, and ItemListeners. In my code I am only interested in creating an ItemListener for CheckBoxes.
#6 Create setters and getters for the variable declared in step #1.
#7 Create class specific conversion functions for relationships between the JSlider and the variable it represents. For example, I may want a slider that displays 0 to 100 but the variable is adjusted between 0.01 and 0.25 (arbitrary linear scaling) or possibly the slider going from -12 to +6 representing a variable from 0.25 to 2.0 (logarithmic scaling).
I created a top class "ControlPanel" in Xtend that has methods for items #2 through #6. This is more of an Interface as the methods don't do anything. Then I created a class "SliderLabel" which is a combination JSlider and JLabel and extends the ControlPanel class. Similarly, I created a "CheckBox" class that handles these things for CheckBoxes. The similarly named methods in these functions perform the actual code generation. Strictly speaking they are not overloaded because the type of the method argument is not the same as the super-class.
Any of you who have ever written a Swing program can see the utility of this I hope! It is a massive amount of code to accomplish something simple, and even if you get it conceptually, writing it by hand is tedious and error prone. Xtend to the rescue!
Here's the problem I'm trying to solve:
Back in my main code generator, I have places in the template where the above things need to happen. What I'd like to do for each control as I iterate through my model is simply to call down to the ControlPanel generator and have it figure out which type of control it is and call the appropriate methods. The problem is that the methods which help create a JCheckBox are expecting an argument of type "CheckBox" within my code and I can't send them a higher-level class reference "ControlPanel".
Now, in my code generator, I iterate through the entire model at each of these various steps and perform a switch statement to see if the element (e) is a CheckBox or a SliderLabel. Then I call either CheckBox.declare(e) or SliderLabel.declare(e) accordingly. I'd rather say in my grammar that a ControlPanel can be either a CheckBox or a SliderLabel, and then during code generation, determine that the element is a ControlPanel and simply call ControlPanel.declare(e). The method that would get called to spit out template code would be the overloaded method within the subclass. If there were no overloaded method, for example there would be no ChangeListener method called out for CheckBox, then nothing would happen.
The issue is with the typing of the model element (e) not matching the methods.
I'm guessing that this issue is not specific to Xtend because I can't think of how I would solve it in Java either. Any ideas?
Many thanks!
GW
[Updated on: Sat, 21 June 2014 15:49] Report message to a moderator
|
|
|
|
|
|
Re: Subclassing code generators (example: Swing UI) [message #1386896 is a reply to message #1386879] |
Sun, 22 June 2014 16:32 |
Gary Worsham Messages: 176 Registered: September 2013 |
Senior Member |
|
|
I think one of the problems I was facing is that I wanted elements of my grammar to adopt a class/subclass structure, so that I did not have to have each one specifically be called out in the main code generator. I realize this was naive, but let me continue.
Suppose I had the parser rules:
CheckBox: name = ID value = ID;
SliderLabel: name = ID value = ID minVal = int maxVal = int
You can see that there is some commonality between these two structures so there is a tendency to want to create a class that combines the common parts, while the subclasses would add the unique parts. In Java I have some flexibility to refer to a derived class either by its own name or that of its superclass, as long as I am just accessing the common elements.
I thought (again naively) that adding a rule:
Control: CheckBox | SliderLabel ;
would somehow create this relationship, so that I could reduce the number of separate calls required when iterating the model in the code generator.
So, rather than needing:
«FOR Element e : pr.elements»
«switch e {
SliderLabel: { SliderLabel.genChangeListener(e)}
CheckBox: { CheckBox.genChangeListener(e)}
}»
«ENDFOR»
I could have:
«switch e {
Control: { Control.genChangeListener(e)}
}»
and then this would call the genChangeListener method for either SliderLabel or CheckBox, whichever the current element corresponded to. In this case, CheckBox has no genChangeListener method, but if the base class had an empty genChangeListener method, then that would get called and nothing would be generated, which is exactly what I wanted.
But I think I was getting confused between classes and some simplifying things in the DSL grammar (like the ability to OR two rules together).
}
[Updated on: Sun, 22 June 2014 16:34] Report message to a moderator
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.04096 seconds