Note: This is a page in development, everything here is tentative, and some has not been finished. Updates come as time is available to the author(s).
When trying to understand how something works, a starting point is needed. If it is simple, the starting point is obvious. However, when it is complicated, finding a starting point can in fact become a hindrance to understanding. This means that this document should serve two purposes. The first is to be step by step instructions to a programmer that wants to create a new Widget. The second is to be a starting point to understanding how Fluency works.
Part 1: How to Create a Widget
Widgets are built on top of classes. In fact, Widgets at their core are just wrappers for classes. This means that to create a Widget there must be a class to start with. One could create a Widget from scratch and do the equivalent of just inlining the class, but the goal of the design is to make previously made classes (even entire other programs) able to be easily translated into Fluency, as well as retain the ability to easily create new code as well.
So, a class is needed. For the purposes of this walkthrough, the following class is going to be used and turned into a Widget.
class Multiplier
{
int answer = 0;
public int multiply(int a, int b)
{
answer = a * b;
return answer;
}
public int getAnswer()
{
return answer;
}
}
This class's main use is to multiply two numbers. It also maintains the last answer calculated and returns it on demand. A class that does this one thing is perfectly acceptable (if not all that interesting or exciting) in a typical program, and it will also make a perfectly acceptable Fluency Widget.
After picking the class, the next step is to wrap it in something Fluency understands and expects. That is, turn it into a Widget. To accomplish this goal, there is a wrapper interface that is in fact named Widget.
public interface Widget { public Harbor getNextHarbor(); }
This interface, while simple, has a few implicit requirements. Perhaps the first thing to notice is that Widgets contain something called Harbors (see design document for naming justifications, <<insert link here>>). Harbors, in essence, are wrappers for methods. They contain everything that has to do with a method including specification of the number and types of inputs, the type of the output and most importantly the method call itself. The actual inner workings of the Harbor are unimportant when implementing a Widget. All a Widget creator needs to know is that for each method in the class being wrapped, a Harbor is created. Each Harbor is told what the inputs are, what the outputs are, and then it is told how to call the method.
In C++, life would be easier. Functions are able to be passed as objects themselves and therefore a list could be kept and simply referenced. In Java this is not the case, or at least not obviously so. The best way around this (or, rather, the way that was chosen because it was presumed to be the best) is to wrap each method call in a generic call of a different class type. An example of another application of this is using a MouseListener in Java's Swing toolkit. Thus, Harbors are in fact anonymous inner classes that contain all the information needed to perform a method call.
All of this method wrapping is done in the constructor of the class that implements the Widget interface. Using the example of the Multiplier class, the MultiplierWidget wrapping it is as follows.
class MultiplierWidget implements Widget { Multiplier m; ArrayList harborList; ListIterator iterator; public MultiplierWidget() { /* * We add our Harbors (publically available functions) in * the constructor. This is no problem because they are * just references to existing functions. */ harborList.add(new Harbor { public Harbor() { name = "Multiply"; longDef = "This function multiplies two integers."; /* Let the Harbor know what the output is. */ outDock = new OutputDock<Integer>(); /* Let the Harbor know what input arguments there are. */ inDocks.add("a",new InputDock<Integer>()); inDocks.add("b",new InputDock<Integer>()); } public void trigger() { /* Call the method with appropriate arguments and capture return value. */ outDock.setCargo(m.multiply(this.getDock("a").getCargo(), this.getDock("b").getCargo()) } }); harborList.add(new Harbor { public Harbor() { name = "getAnswer"; longDef = "This method returns the answer calculated last by the multiplier."; /* Let the Harbor know what input arguments there are. */ inDocks.add("null", new InputDock<null>()); /* Let the Harbor know what the output is. */ outDock = new OutputDock<Integer>()); } public void trigger() { /* Call the method with appropriate arguments (none) and capture return value. */ outDock.setCargo(m.getAnswer()); } }); iterator = harborList.listIterator(); } public Harbor getNextHarbor() { if(iterator.hasNext()) return iterator.next(); else { iterator = harborList.listIterator(); return null; } } }
While there is alot of code to add relative to the desired functionality, in reality most of it is very formulaic. For each public method in the class that is being wrapped (in this case the Multiplier class), a list is created to hold the Harbors (here it was ArrayList but this can be any data structure that the programmer wants), an anonymous inner class of Harbor is created for each function, and that Harbor is added to the list of Harbors. The anonymous inner class must contains two things:
The first is the constructor of the class that assigns a name and description to the class, as well as creating Docks (see design document) for the inputs and the output. There should be one InputDock line for each argument, with the name of the argument and a dock that encapsulates its type. The OutputDock encapsulates the return type of the method. The last thing the constructor does is retrieve an iterator from the list that was just populated. These Docks will be used by Fluency to route input and output to the methods.
The second thing the Widget contains is the method that needs to be implemented to maintain compliance with the Widget interface, getNextHarbor. In the example, the MultiplierWidget uses an ArrayList to keep track of the Harbors it contains. In reality, this can be any sort of data structure that allows the list of harbors to be returned one by one from the getNextHarbor method call.
The relationship between each of the classes can be seen here:

This is all the Widget programmer needs to do to create a Widget wrapper. Once the class is wrapped and the Widget wrapper class (in this case MultiplierWidget) is created, there is only one more thing to do. The Widget programmer must tell Fluency that the Widget exists.
Currently this must be hardcoded into a Widget list that Fluency knows about. However, in the future, dropping the class files of classes such as MultiplierWidget into a specific folder should show them to Fluency. The best way to go about this is a matter of some debate but should be relatively easy to do.
Part 2: A Widget's Place in the Bigger, Scarier World of Fluency
The goal in designing Widgets this way is to make them generic enough so that creating them is easy, while at the same time maintaining a general enough interface to the rest of Fluency to allow it to work. In theory, each Widget could be separately hard coded, but this would be a horrible way to create an extensible framework.
So, after a class is both turned into a Widget that Fluency can recognize and made known to Fluency, what happens?
FluencyWidgets
A Widget in Fluency is in fact more complicated than just a wrapped class. The added functionality is standard between Fluency Widgets, however. Each FluencyWidget needs to know about all its Harbors so that it can advertise them to the world, as well as tell Fluency itself about them. A FluencyWidget also needs to keep track of the Shipping Channels that it can currently output to (that is, which Docks in which Harbors are connected to which Shipping Channels).
This, then, is the next layer above the Widget layer that was shown in the last section. Widgets such as the MultiplierWidget will have another layer wrapped around them that does bookkeeping. This layer is a class that is called FluencyWidget. This class expects to be holding an implementation of the Widget class. It then has public methods that Fluency knows about that allow the global "go" signal to be received, Shipping Channels to be connected, Ships to be received and sent off, etc.
class FluencyWidget
{
private Widget widget;
private String name = "";
private ArrayList harborList = new ArrayList<Harbor>;
public FluencyWidget(Widget w, String n)
{
name = n;
widget = w;
/* Iterate through the Widget's Harbor list and capture the Harbors. */
Harbor harbor = widget.getNextHarbor();
while(harbor != null)
harborList.add(harbor);
}
/* A method to connect an outgoing Shipping Channel to emit Ships. */
/* A method to send something back to connect a Shipping Channel to receive Ships. */
/* A method to report back to Fluency what its Harbors are. */
/* A method to advertise what the Harbors and Docks are. */
}
At this layer there is then a collection of FluencyWidget objects that in turn each contain a different Widget implementations. Each of these FluencyWidgets can then be used to instantiate objects that are used when creating a Fluency application.
Fluency
Is this, then, the outermost layer? Of course not, there is one more. Fluency as a whole needs certain functionality. It needs to know about all the Harbors in each of its FluencyWidgets. This allows it to iterate through the global list of Harbors and fire each in turn, which allows all Ships to be passed when they're ready.

Fluency also needs to keep track of all its FluencyWidgets and Shipping Channels.
The outermost Fluency layer has a direct tie in to editing capabilities, as well as visual capabilities, as it will contain the glass pane that captures events, the outermost actual visual holder, etc.
To be continued.
Comments (12)
Oct 01, 2006
Vivek S. Thakre says:
Please explain When there is an execution of any action,...Please explain
When there is an execution of any action, how will you handle the 'acknowledgement' that all the needed parameters received and the 'acknowledgement' of the successful execution of an action or it is the case in your design that these acknowledgements are not needed.its not clear to me.
Oct 02, 2006
Jaliya Ekanayake says:
Let me guess... So the actions are performed by looking at the names provided in...Let me guess...
So the actions are performed by looking at the names provided in the methods or Habours? If that is the case we should add some unique names.
Oct 02, 2006
Dennis Hostetler says:
Vivek, There doesn't need to be acknowledgment of any sort. When all the appropr...Vivek,
There doesn't need to be acknowledgment of any sort. When all the appropriate parameters are received, the method is called on the next Fluency global "go" signal (I don't know that we have a name for this or not). Acknowledgement of any sort between Widgets needs to be done through ships regardless. Any built in acknowledgement implies that Widgets know about each other which defeats the purpose of having them operate in a vacuum, not knowing about anything else.
Jaliya,
You're right in that we might need unique names, but in reality we could always leave it to the Widget programmer and if they use a name more than twice, then the first one that is found is the one that is used. Most Widget programmers should be encouraged to use the names of their methods anyway. If this becomes a problem, it would be easy to force name uniqueness, though.
Oct 02, 2006
whoknows says:
two small comments so far: so far, 'Widget' looks a lot like Iterator. if you ad...two small comments so far:
so far, 'Widget' looks a lot like Iterator. if you added public boolean hasNextHarbor() to the interface that's exactly what it would be. perhaps that might also make coding mistakes like in 'FluencyWidget' less likely. for example, this code:
/* Iterate through the Widget's Harbor list and capture the Harbors. */
Harbor harbor = widget.getNextHarbor();
while(harbor != null)
harborList.add(harbor);
can't work. the while loop control is in the wrong place. and instead of testing against null, you could test against hasNextHarbor().
another small comment: the style guide for this class:
http://www.roxie.org/books/bleeding/appendices/ap01.html
(which is linked to from the class homepage:
http://www.cs.indiana.edu/~rawlins/b490.html)
is pretty anal about naming (among several other things). although the example here is trivial,
i'd prefer something like this.name = name in the constructor for 'FluencyWidget'. there's no need for temp names like 'n' in java (leave your c++ baggage behind
Oct 02, 2006
Russell Duhon says:
I don't think an iterator is the way to go. Harbors should rarely, if ever, chan...I don't think an iterator is the way to go. Harbors should rarely, if ever, change, and should always be in the same order. There's already a structure designed for carrying that sort of information, and its a List.
Oct 02, 2006
Russell Duhon says:
Oh yeah, plus then the widget has to maintain annoying additional state, particu...Oh yeah, plus then the widget has to maintain annoying additional state, particularly if we want that to be threadsafe. Much easier and safer to merely have widgets return Lists.
Oct 02, 2006
whoknows says:
i'm fine with List. gjer ps. anyone know where the pdf is that mark announced i...i'm fine with List.
-gjer
ps. anyone know where the pdf is that mark announced in class? can't find it here,
and a general search of the whole wiki finds no string 'pdf' anywhere. the dashboard
also reports no new design thing added since i last posted 8hrs ago.
Oct 03, 2006
Allen Lee says:
I'm not one to tout XML but in this case it seems better to have widget and harb...I'm not one to tout XML but in this case it seems better to have widget and harbor details in configuration files, not code. Something like Spring's applicationContext.xml... Anyways, I think I understand what you're getting at with the separation of Harbors and Docks even though my soul whimpers at the thought of YetAnotherLayerOfIndirection in the Fluency codebase.
It would be a nice smell to get away from though, specifying widget metadata outside the code.
Oct 03, 2006
whoknows says:
so far i've only had one runthrough of the design doc (the pdf currently attache...so far i've only had one runthrough of the design doc (the pdf currently attached to the team page here:
http://fluency.knownspace.org/confluence/download/attachments/1898/DDRough.pdf
but it doesn't appear as that much more indirection. it's true that
they've added one more layer (i.e. Harbors) on top of Widget but it's
below FluencyWidget. in theory, it shouldn't matter much to anything
else and it gives some nice grouping properties so i don't think it's
a bad thing. it's part of a bigger change though and that's the worry
for me (see my recent posting on the mailing list examining (what i
think is) the redesign.
as for the business of XML config files instead of hardcoding in the codelayer above the raw Widget
class (in their naming, 'Widget' is any java class. there's no delegate standing between it and
Fluency. in their world, FluencyWidget has to package up the raw class (Widget) for the rest of Fluency.)
i'm of course in favor. it's the better way.
BUT it may add more time to development, depending on the class's familiarity with XML and XML support tools.
so i propose doing the kind of quick-and-dirty thing they're likely to suggest as well: let's
do it the hardcoded way as everyone will be familiar with raw hackage then later refactor
that back out into XML config files. anyway, that's where i am right now on this question.
-gjer
Oct 03, 2006
Mark Christensen says:
Ok, so sorry for the confusion but the PDF is posted under our Team 1 page. At t...Ok, so sorry for the confusion but the PDF is posted under our Team 1 page. At the time, that made the most sense... The updated version is up. I sent out an email about it but we will see if it actually appears at any time...
Oct 03, 2006
Adam Hinz says:
This may have been brought up in class, but I'm going to ask it again anyway: Do...This may have been brought up in class, but I'm going to ask it again anyway: Do harbors have any kind of graphical representation in the interface?
You talked about how the interface won't have to be changed at all (or very much, at least), but I think harbors is something that requires a bit of UI coding. Inside of a single widget, the author has to be able to connect the input docks to the output docks.
If I'm wrong about something, pounce away. I can take it.
Oct 04, 2006
Aaron Sarazan says:
Nicely done. I haven't read through the whole design doc yet, but I'm liking wha...Nicely done. I haven't read through the whole design doc yet, but I'm liking what I see here. I do shed a tear for our tragic loss of XML, however. Gotta do what you gotta do.