What does it mean?
Pipes in fluency are visual representation of "Shipping Lanes" that connect two widgets. Pipes does not perform type conversion when passing data from output dock of Widget A to input dock of Widget B, where the pipe connects Widget A to Widget B. Following example will make it more clear.
Example:
Consider the following scenario:
A fluency author wants to connect a textbox widget to a calculator widget. A textbox widget will return the text as String object, but the Calucator widget's input dock wants a double or a float. Thus if a calculator widget wants to work with a textbox, then it has to implement the logic to do type conversion for all possible widgets it can link to.
Why does Fluency need it?
Fluency will be having(soon) a web service widget which will ship data with different datatype from its output dock. To let the Webservice widget link to other widgets in Fluency, there should be a way(independent of Webservice widget) to do type conversion such that the widget which accepts data from the Webservice widgets output dock, gets the data in a form which is accepted by its input dock.
The above paragraph discusses the issue with respect to the Webservice widget, but this issue also hold true for the calculator widget and many widgets that might be added in future.
How different is current implementation from the design?
Currently, pipes are visual representation of "Shipping Lane" and the logic of shipping lanes is implemented as paths. Thus pipes are implemented as shipping lanes. This means, that there is always a path associated with one pipe. "Shipping Lanes" are implemented as Lists in Output Dock.
The following code snippet shows how Shipping Lanes are actually implemented:
/** * Ships sent out from OutputDocks must know where they are going, so * OutputDocks are responsible for maintaining Shipping Lanes. The way * to create a Shipping Lane is to pass an InputDock into this method. * The OutputDock then knows about it and any Ship will be sent on to * the InputDock. * * @param inputDock The InputDock that a Shipping Lane connects to. * @param laneID The PathID for the connection between the output and input dock. */ public void addShippingLane(InputDock inputDock, PathID laneID) { if( laneIDs.get(inputDock) == null ) { laneIDs.put(inputDock, new ArrayList<PathID>()); shippingLanes.add(inputDock); DebugLogger.sendDebugMessage("Dock", "Attaching("+this.getID().toXML()+")" + " " + inputDock.getID().toXML()); } List<PathID> paths = laneIDs.get(inputDock); if( !paths.contains(laneID) ) { paths.add(laneID); } if( ConstantsFile.TRACK_COMMANDS || ConstantsFile.TRACK_CONNECTION_COMMANDS ) System.out.println(inputDock + " added to " + this + "\nNow " + this + " has " + shippingLanes.size() + " shipping lanes."); }
The following link has more information about the actual concept of shipping lanes http://fluency.knownspace.org/confluence/display/fdoc/Paths.
How are we planning to do it?
We are planning to executing this idea, using the strategy pattern.
Here's the plan:
- Create an interface called Converter.
- Create "Shipping Lane" class which will implement Converter and will be responsible for doing the type conversion required.
- Widgets should not worry about what type of data is accepted by widgets they are linking to.
- Add a FluencyTypeConversionRegistry.
Idea behind adding FluencyTypeConversionRegistry
Lets say that we need to convert a String passed from the output dock of widget A to an integer type accepted by the input dock of widget B, the process is as follows:
- First the PipeConnectorTool is called
- Check if the types match. If the pipes donot match, call autoloadConverters()
- When the type conversion can be autoloaded. (Refer: What is autoloadConverter? (below))
autoloadConverters calls the FluencyTypeConversionRegistry::getAutoConverters(<input>, <output>), which loops through the list of available auto converters of simple type (such as string to int, int to float etc) with the canConvert(<input>,<output>). If the converter can perform the conversion, then it returns true.
- When the type conversion cannot be autoloaded,
autoloadConverters calls the FluencyTypeConversionRegistry::getConverters(<input>,<output>), which loops through the list of all the available converters. These converters can be of any type (xml converters, java bean converters, date converters). If the canConvert(<input>,<output>) returns true then it sent back as a list of capable converters for that type of conversion.
Additionally the FluencyTypeConversionRegistry can do some basic required type conversion from primitive to wrapper types. For example to convert from primitive int type to INTEGER type etc. For this FluencyTypeConversionRegistry::convertPrimitivesToWrappers() is invoked when ever a conversion is requested from a pipe. This call to convertPrimitivesToWrappers is made inside the FluencyTypeConversionRegistry.
Other things to note about FluencyTypeConversionRegistry
Internally FluencyTypeConversionRegistry, has a register method through which the various type converters register in the FluencyTypeConversionRegister
What is autoloadConverter?
Conversion can happen in 2 ways
- Automatic
- Provide the user with a list of options and his him to pick what he wants to do.
For simple conversion like string to int, float to int etc, we need not ask the user on what to do. So these are done automatically. In the current version of the codebase the SimpleTypeConverter does this type of conversion. In the future, additional types of conversions can be added to the auto list by passing 'true' when the register(<Converter>, boolean <Auto>) is called.
For complex conversions like getting specific information from an xml document, the needs to be asked on what to do about it, so it's handled as a complex or not-auto kind of converter.