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.
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);
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"/>
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"/>
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( ... );
}
}
<DataList Model="DataSource:xyzDS:select name from
users"/>
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.
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"/>
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 ;-)