Menu

#12 Create an alternative AttributeHandler for "pure" attributes

open
None
5
2005-06-18
2005-06-18
No

Implement an AttributeHandler, that uses simple syntax
for setting properties on a JavaBean. This
AttributeHandler should be limited to Properties, so it
can only be used with single-argument methods:

Examples:
<JPanel Size="255,21"/>
<JPanel Color="RED"/>
<JPanel Color="#FF0000"/>
<JLabel Text="Hello World"/>

The implementations should first find any single
argument methods matching the "set<attribute-name>"
pattern and then try to convert the attribute-value to
the argument-type of this method.

For this purpose a converter registry should be
provided that contains converter-implementations from
string to a specified type:

class ConverterRegistry
{
void registerService(Class type, Converter converter)
Object convert(Class type, String value);
}

interface Converter
{
Object convert(Class type, String value);
}

Maybe the converters should be stored in the
PackageRegistry with a special key-syntax (eg.
"converter.java.lang.Double") to allow central
configuration of this service.

Discussion

  • Shawn Curry

    Shawn Curry - 2005-06-19

    Logged In: YES
    user_id=1118529

    I do not feel that explicit mappings will be necessary. In
    fact, I think this class can be implemented without any
    additional architecture. Perhaps I will lay the foundation
    for this new AttributeHandler; if any additional custom
    syntax is required, it should be very simple to modify.

    The new AttributeHandler would work as follows:

    // Step 1 - find the property
    PackageList packageList = windowContext.getPackageList();
    Class c = packageList.findClass(elem.qName);
    Method[] methods = c.getMethods( c, attr.qName );
    // try alternative names (append "set", ...)
    if( methods == null )
    return super.handleAttribute( ... );
    Method m = null;
    for( int i = methods.length; i-->0 && m == null; )
    if( methods[i].getParameterTypes().length == 1 ) m =
    methods[i];
    if( m == null )
    return super.handleAttribute( ... );

    // Step 2: resolve the argument
    Object[] args = new Object[1];
    Class clazz = method.getParameterTypes()[0];
    Constructor[] con = c.getConstructors();
    // This is the tricky part: which one did you mean?
    Object cargs = null;
    if( attr.value.trim().length() > 0 ) // fall through for
    default constructor
    {
    // insert special cases here (regex could be used)

    // try to "guess" the constructor arguments
    cargs = Primative.parsePrimatives(attr.value);
    if( cargs == null )
    {
    Object o = reflectionHandler.getConstant( clazz,
    attr.value );
    if( o != null )
    cargs = new Object[] { o };
    if( cargs == null )
    cargs = new Object[] { attr.value };
    }
    }
    args[0] = reflectionHandler.callConstructor( clazz, cargs );

    // Step 3: set the property
    reflectionHandler.callMethod(c, elem.obj, method.getName(),
    args);

     
  • Shawn Curry

    Shawn Curry - 2005-06-24

    Logged In: YES
    user_id=1118529

    I have started working on this, and I'm remembering the
    possible pitfalls.

    class A {
    A( String str ) {}
    }

    class B {
    B( int i ) {}
    }

    class C {
    C( double d ) {}
    C( String s ) {}
    }

    class D {
    set( A a ) {}
    set( B b ) {}
    set( C c ) {}
    set( int i ) {}
    set( String s ) {}
    }

    <D set="12345678"/>

    This is the reason I use single quotes. I suppose that it
    shouldn't matter for properly designed libraries, because
    whichever path is chosen should have the same logical
    effect. However, I doubt that will be the case at the
    "moment of truth".

    I'd like to hear your ideas. I wanted to avoid the explicit
    mappings, I feel that using reflection will be a much more
    flexible solution at a much lower price. Explicit mappings
    would have the same problem.

    registerService( int.class, converter0 );
    registerService( String.class, converter1 );
    registerService( A.class, converter2 );
    registerService( B.class, converter3 );

    <D set="1234"/>

    I just thought of a really cool one:

    class A {
    set( A a ) {}
    set( String s ) {}
    A hello;
    }

    <A set="hello"/>

     
  • Mathias Henze

    Mathias Henze - 2005-06-24

    Logged In: YES
    user_id=14631

    Please keep in mind, that the pure attributes should allow
    easy setting of well known properties with a consistent
    look. It is not for general purpose expression handling. If
    this is needed, the user should be encouraged to use
    JFCMLScript with the bracket syntax.

    Pure attributes could be resolved in the following order:
    - first numbers (int, float, etc.)
    - then other primitives (char, ?)
    - then explicit object converters (A,B,C,Color,ListModel,etc.)
    - at last Strings

    The explicit object converter mapping is more a framework,
    to allow users to extend the attribute syntax. For example,
    this could be used to query a database:

    <JList model="DataSource:xyzDS:select name from users"/>

     
  • Shawn Curry

    Shawn Curry - 2005-06-25

    Logged In: YES
    user_id=1118529

    I suppose I could see the value in that. However, I suppose
    I still resist the explicit mappings like you resist using
    JFCMLScript ;)

    If you prefer to not to use the scripting features, the
    preferred method of implementing something like this is to
    design a Java class:

    public class DataList extends JList {
    public void setModel( String dsn ) {
    super.setModel( ... );
    }
    }

    &lt;DataList Model="DataSource:xyzDS:select name from
    users"/&gt;

    I resist the explict mappings because the 0.9 version is
    recently moving away from an explicit mapping architecture.
    Because of the header that Java places in each class file,
    these relatively small mapping classes tend add up very
    quickly. For example, the above class listing is ~100
    bytes, however it will generate a class file ~1024 bytes in
    size, a 1000% increase!

    The preferred method of overriding the functionality of
    default attributes is by adding an AttributeHandler. All
    the 'Handler classes in com.metasolutions.jfcml.extend are
    designed as extension points which may be chained together.

     
  • Shawn Curry

    Shawn Curry - 2005-06-25

    Logged In: YES
    user_id=1118529

    Also, the reflection approach will handle the heirarchical
    nature of classes more naturally. Registering converters by
    class in a HashMap will have the effect of statically
    binding the converter to one particular permutation of a
    class. For example:

    public class MyConverter implements Converter {
    public Object convert( Class type, String value )
    {
    if( type == ImageIcon.class )
    {
    return new ImageIcon( getClass().getResouce(
    value ) );
    }
    }
    }

    registerService( ImageIcon.class, new MyConverter() );

    <JButton Icon="/images/combo/laraeyes.jpg"/>

    This would not work, because the convterter would need to
    additionally be registered to Icon.class. This seems like a
    lot of work for the user compared to what is currently
    supported:

    public class MyButton extends JButton {
    public void setIcon( String resource ) {
    super.setIcon( new ImageIcon( ... ) );
    }
    }

    <MyButton Icon="/images/combo/laraeyes.jpg"/>

     
  • Mathias Henze

    Mathias Henze - 2005-06-26

    Logged In: YES
    user_id=14631

    the explicit mapping could be implemented in a way to take
    the class hierarchy into account, but since I want to use
    the already provided features of JFCML to resolve most of
    the attibutes and use the mapping just for extension, I must
    admit that we should put this discussion to a rest for now
    and wait for user request for such a feature.

    I will try to implement the AttributeHandler just with
    already existing JFCML functionality, so I yield to your
    arguments for now ;-)

     

Log in to post a comment.

MongoDB Logo MongoDB