Some of the applications of your idea seem to be inclusive of functions in 
other APIs like Flexjson's Transformers or Metawidget's dynamic UIs.
All really great applications coming from a single enhancement, sounds 
excellent indeed.

http://flexjson.sourceforge.net
http://metawidget.org

Thom


On 2011-07-02, at 4:36 PM, Chris Bartlett wrote:

> Firstly, my apologies for the length of this email (and if there are
> lots of typos).  It might be easier to just jump to the example BXML
> fragments which demonstrate possibilities of the proposed change, and
> then skip back to the more wordy stuff.
> 
> I will post some proof of concept code in the morning if there is any
> interest in this.
> 
> 
> * Notes
> The examples are meant to demonstrate ideas, so might seem a little contrived.
> Although Transformables are designed to be triggered by
> BXMLSerializer, they can just as easily be used programmatically.
> The transform() method could be provided with a specific classloader
> by BXMLSerializer if required
> 
> 
> * References
> http://pivot.apache.org/tutorials/bxml-primer.html
> Specifically the section titled 'Class Instances'
> 
> http://svn.apache.org/repos/asf/pivot/trunk/core/src/org/apache/pivot/beans/BXMLSerializer.java
> 
> 
> * Summary
> Make a backwardly compatible change to BXMLSerializer to enable
> support for a simple 'Transformable<T>' interface.
> 
> public interface Transformable<T> {
>    public Collection<T> transform();
> }
> 
> The interface would have a single method named 'transform()' which
> would return a java.util.Collection<T>.
> 
> A bean implementing the interface would be constructed as normal by
> BXMLSerializer, but would have its 'transform()' method called when
> the bean would usually be returned (if it was the root element) or
> added to the parent object somehow (if it had a parent).
> 
> If the bean is the root element, the Collection would be returned as
> the result of the BXMLSerializer.readObject(InputStream) method;
> 
> If the bean is not the root element, BXMLSerializer would the iterate
> over the items in the returned Collection and process them in exactly
> the same way as the original bean would have been processed.
> 
> i.e. If the original bean was going to be added to a Sequence defined
> by a DefaultProperty, then each of the items in the Collection would
> be added to the same Sequence instead.
> 
> i.e. If the original bean was going to be set as the value for a
> writeable property, each of the items in the Collection would be set
> as values for the same writeable property, in the order defined by the
> Collection's iterator)
> 
> 
> * What difference would that make?
> This simple change would provide flexibility by allowing a special
> type of 'Class Instance' (an implementation of Transformable) to
> decide how many items to return (and of what types) when it is
> 'created', rather than exactly 1 object as currently happens.
> 
> 
> * How could that help me?
> It essentially turns Transformable objects into macros that can
> perform arbitrary tasks.
> 
> 
> 
> Example 1 - User defined, simplified API for creating object graphs
> 
> This is the original problem that lead to the proposed idea.
> Being able to create simplified APIs for Pivot Components means that
> designers could be provided with a much smaller and simpler 'toolkit'
> to work with.  Having a smaller API would also be helpful for people
> who add in auto-complete functionality into their editor tools.
> Even just the ability to refer to a Component by a different name can
> be very useful, especially when there are similarly named widgets in
> various UI frameworks.
> 
> <!-- This would create and populate a layout without needing to know
> how it was being implemented behind the scenes -->
> <ThreeColumnLayout>
>  <leftColumn>
>    ...
>  </leftColumn>
>  <middleColumn>
>    ...
>  </middleColumn>
>  <rightColumn>
>    ...
>  </rightColumn>
> </ThreeColumnLayout>
> 
> 
> Here is a more complete example...
> 
> TablePaneBuilder implements Transformable<TablePane> and has 'columns'
> and 'components' properties.  The latter being a Sequence<Component>
> annotated as the DefaultProperty.  Its 'transform()' method returns a
> newly created TablePane with the requested number of columns, and
> sufficient rows to populate the cells using the supplied Components.
> 
> @DefaultProperty("components")
> public class TablePaneBuilder implements Transformable<TablePane> {
> 
>    private final List<Component> components = new ArrayList<Component>();
>    private int columns = 1;
> 
>    public Sequence<Component> getComponents() {
>        return components;
>    }
> 
>    public int getColumns() {
>        return columns;
>    }
>    public void setColumns(int columns) {
>        if (columns < 1) {
>            throw new IllegalArgumentException();
>        }
>        this.columns = columns;
>    }
> 
>    @Override
>    public Collection<TablePane> transform() {
>        final TablePane tablePane = new TablePane();
>        for (int i = 0; i < columns; i++) {
>            tablePane.getColumns().add(new Column());
>        }
>        TablePane.Row row = null;
>        for (Component component : components) {
>            if (row == null || row.getLength() == columns) {
>                row = new TablePane.Row();
>                tablePane.getRows().add(row);
>            }
>            row.add(component);
>        }
>        return Arrays.asList(tablePane);
>    }
> }
> 
> Usage in BXML
> <TablePaneBuilder columns="2" >
>  <!-- Row 1 -->
>  <wtk:Label text="Label 1" />
>  <wtk:Label text="Label 2" />
> 
>  <!-- Row 2 -->
>  <wtk:PushButton buttonData="PushButton 1" />
>  <wtk:PushButton buttonData="PushButton 2" />
> 
>  <!-- Row 3 -->
>  <wtk:TextInput text="TextInput 1" />
>  <wtk:TextInput text="TextInput 2" />
> </TablePaneBuilder>
> 
> 
> 
> Example 2 - Ability to instantiate classes which do not have a no-arg
> constructor
> 
> public final class UnfriendlyPOJO {
>    public final int mandatory;
>    public UnfriendlyPOJO(int mandatory) {
>        this.mandatory = mandatory;
>    }
> }
> 
> public class UnfriendlyPOJOBuilder implements Transformable<UnfriendlyPOJO> {
>    private int mandatory;
>    public int getMandatory() {
>        return mandatory;
>    }
>    public void setMandatory(int mandatory) {
>        this.mandatory = mandatory;
>    }
> 
>    @Override
>    public Collection<UnfriendlyPOJO> transform() {
>        return Arrays.asList(new UnfriendlyPOJO(mandatory));
>    }
> }
> 
> 
> <UnfriendlyPOJOBuilder mandatory="99" />
> 
> 
> 
> Example 3 - Can be used to hide attribute ordering restrictions
> 
> This BXML will work, as the selectedIndex is being set once the
> ListView has data
> <wtk:ListView listData="['One', 'Two', 'Three']" selectedIndex="0"/>
> 
> But this BXML will fail, as the selectedIndex is being set before the
> ListView has data
> <wtk:ListView selectedIndex="0" listData="['One', 'Two', 'Three']"/>
> 
> 
> @DefaultProperty("listView")
> public class ListViewBuilder implements Transformable<ListView> {
>    private final ListView listView = new ListView();
>    private int selectedIndex;
>    private List<?> listData;
> 
>    public int getSelectedIndex() {
>        return selectedIndex;
>    }
>    public void setSelectedIndex(int selectedIndex) {
>        this.selectedIndex = selectedIndex;
>    }
> 
>    public void setListData(String listData) {
>        try {
>            this.listData = JSONSerializer.parseList(listData);
>        } catch (SerializationException exception) {
>            throw new IllegalArgumentException(exception);
>        }
>    }
> 
>    @Override
>    public Collection<ListView> transform() {
>        listView.setListData(listData);
>        // Ensure that the index selection happens after the list population
>        listView.setSelectedIndex(selectedIndex);
>        return Arrays.asList(listView);
>    }
> }
> 
> <ListViewBuilder selectedIndex="0" listData="['One', 'Two', 'Three']" />
> 
> 
> 
> Example 4 - Provide similar functionality to named styles only for properties
> 
> The same could essentially be achieved by subclassing a Component and
> hard coding some properties into its constructor.
> Using a Transformable is another option, and could also be used with
> final classes.
> 
> @DefaultProperty("textInput")
> public class PasswordInput implements Transformable<TextInput> {
>    private TextInput textInput = null;
>    public void setTextInput(TextInput textInput) {
>        this.textInput = textInput;
>    }
> 
>    @Override
>    public Collection<TextInput> transform() {
>        if (textInput == null) {
>            textInput = new TextInput();
>        }
>        textInput.setPassword(true);
>        textInput.setPrompt("Enter password");
>        return Arrays.asList(textInput);
>    }
> }
> 
> <!-- Creates and returns a new TextInput -->
> <PasswordInput />
> <!-- Uses the supplied TextInput -->
> <PasswordInput>
>  <wtk:TextInput tooltipText="Using supplied TextInput" text="secret" />
> </PasswordInput>
> 
> 
> This idea can be taken further by a Transformable that 'clones'
> specified objects (0..n times).
> It would create new instances based on the supplied source objects,
> and then iterate over the source's properties and styles while setting
> the same values on the newly created 'clone'.  (I knocked up a working
> example of this in about 20 mins)
> 
> 
> 
> Example 5 - Transformable that returns an empty Collection
> 
> This could be used to conditionally include sections of BXML file
> based on any arbitrary logic.
> (Result of a webquery, result of a JVM scripting function, JVM
> version, availability of classes on the classpath, Operating System,
> user name etc)
> If the critera is met, the Transformable would return a Collection
> containing the BXML, otherwise just an empty Collection.
> 
> <WindowsOS>
>  <WindowsWidget />
>  <bxml:reference id="$commonToAllOperatingSystems"/>
>  ...
> </WindowsOS>
> <MacOS>
>  <MacWidget />
>  <bxml:reference id="$commonToAllOperatingSystems"/>
>  ...
> </MacOS>
> 
> 
> 
> Example 6 - Enhanced BXML include
> 
> <!-- Include all files that match the regular expression or wildcard
> syntax, retrieving them asynchronously -->
> <Include async="true" regex="/path/to/bxml/files/widget[5-7].bxml" />
> 
> or even
> 
> <Async>
>  <Include regex="/path/to/bxml/files/widget[5-7].bxml" />
> </Async>
> 
> The Async would queue a callback to populate the same position with
> data once it had been retrieved from a potentially slow source.
> (This is just an example of the sorts of options which are made
> available by this change - not a proposal to create such a new
> 'Includer')
> 
> 
> 
> Example 7 - Dynamic UI creation (think automatic CRUD forms for a POJO/bean)
> 
> <!-- Show a 'person' bean in a form -->
> <FormBuilder editable="true" source="$person" fieldOrder="['id',
> 'title', 'firstName', 'lastName', 'age']" />
> or
> <!-- Show a 'person' bean in a name/value TableView where the values
> are editable -->
> <TableViewBuilder editable="true" source="$person" fieldOrder="['id',
> 'title', 'firstName', 'lastName', 'age']" />
> 
> (These examples are for generic XXXBuilders, but they could obviously
> have internal knowledge of certain types.)
> 
> 
> 
> Example 8 - Reuseable and sharable
> 
> Due to the simplicity of the interface, Transformables can easily be
> shared among Pivot developers.
> This could lead to an online repository of reuseable components (even
> just wiki pages of source).
> 
> There have been many mailing list questions about how to achieve
> particular layouts.
> Common layouts could be implemented as Transformables and mailed to
> the list for easy reuse.
> 
> (I suppose Transformables could be considered similar to a JSP tag library)
> 
> 
> Chris

Reply via email to