Upload
wolf-paulus
View
3.050
Download
1
Embed Size (px)
DESCRIPTION
Graphical User Interfaces are described in XML documents that are parsed at runtime and rendered into UI-Widgets. While open-source projects like Thinlet and Swixml focus mainly on the GUI, they are also good examples for how declarative programming can be done in Java.
Citation preview
Wolf Paulus
Declarative Programming :UI-Generation at Runtime with SwixML
wolfpaulus.com
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
1. Declarative Programming
2. Reflection and Introspection in JAVA
3. Thinlets, Declarative GUI Toolkit for Java
4. Theodore, a Thinlet XUL Editor
1.Demo and Code
5. Swixml, GUI Generation Engine
6. Custom Tags and Bindings
7. Mapping of private members
1. Code, Demo and more Code
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Declarative programming concentrates on expressing a set of conditions, describing a solution space, but leaves finding a solution to an engine, dedicated to a certain class of problems.
Wikipedia:
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Writing programs in a declarative style in an imperative programming
language usually requires lots of runtime information.*
*for Java, this means Reflection & Introspection
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Computational reflection is the ability of a program to observe and possibly modify its high level structure.
It is most common in high-level virtual machine programming languages like Smalltalk, and less common in lower-level programming languages like C.
Wikipedia:
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Reflection in JavaJava allows a program to discover information about the fields, methods, and constructors of loaded classes. Also, a Java program can instantiate a class and operate on the underlying objects, (within security restrictions.)
Reflection is the process of looking at an object during run time in order to understand its features or properties.
Where the reflection process analyzes the object itself to discover the object’s properties, Introspection relies on the class to provide information on its behalf.
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
package com.carlsbadcubes.codecamp;
import java.lang.reflect.Method;import java.util.Vector;
public class Reflector { /** * Find all methods that can be called on the given object that * <li> start with the prefix <i>set</i> </li> * <li> take only a single parameter </li> * <li> and have the return type <i>void</i></li> * @param client <code>Object</code> * @return <code>Vector</code> containing possibly setters methods */ public static Vector<Method> getSetters(final Object client) { Vector<Method> setters = new Vector<Method>(); Method[] methods = client.getClass().getMethods(); for (Method method : methods) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && method.getReturnType() == void.class) { setters.add(method); } } return setters; }}
Reflection
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
package com.carlsbadcubes.codecamp;
import junit.framework.TestCase;import javax.swing.*;import java.lang.reflect.Method;import java.util.Vector;
public class ReflectorTest extends TestCase {
public void testFindSetters() { Object obj = new JLabel("hello"); Vector<Method> setters = Reflector.getSetters(obj);
TestCase.assertTrue(setters != null); TestCase.assertTrue(0 < setters.size());
for (Method setter : setters) System.out.println( setter.getDeclaringClass().getName() +":"+ setter); }}
Reflection TestC
ase
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Methods found:
public void javax.swing.JLabel.setUI(javax.swing.plaf.LabelUI)public void javax.swing.JLabel.setText(java.lang.String)public void javax.swing.JLabel.setIcon(javax.swing.Icon)public void javax.swing.JLabel.setDisabledIcon(javax.swing.Icon)public void javax.swing.JLabel.setVerticalAlignment(int)public void javax.swing.JLabel.setHorizontalAlignment(int)public void javax.swing.JLabel.setVerticalTextPosition(int)public void javax.swing.JLabel.setHorizontalTextPosition(int)public void javax.swing.JLabel.setIconTextGap(int)public void javax.swing.JLabel.setDisplayedMnemonicIndex(int)public void javax.swing.JLabel.setDisplayedMnemonic(int)public void javax.swing.JLabel.setDisplayedMnemonic(char)public void javax.swing.JLabel.setLabelFor(java.awt.Component)
javax.swing.JLabel
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
public void javax.swing.JComponent.setEnabled(boolean)public void javax.swing.JComponent.setVisible(boolean)public void javax.swing.JComponent.setForeground(java.awt.Color)public void javax.swing.JComponent.setBackground(java.awt.Color)public void javax.swing.JComponent.setFont(java.awt.Font)public void javax.swing.JComponent.setNextFocusableComponent(java.awt.Component)public void javax.swing.JComponent.setRequestFocusEnabled(boolean)public void javax.swing.JComponent.setVerifyInputWhenFocusTarget(boolean)public void javax.swing.JComponent.setPreferredSize(java.awt.Dimension)public void javax.swing.JComponent.setMaximumSize(java.awt.Dimension)public void javax.swing.JComponent.setMinimumSize(java.awt.Dimension)public void javax.swing.JComponent.setBorder(javax.swing.border.Border)public void javax.swing.JComponent.setAlignmentY(float)public void javax.swing.JComponent.setAlignmentX(float)public void javax.swing.JComponent.setInputVerifier(javax.swing.InputVerifier)public void javax.swing.JComponent.setDebugGraphicsOptions(int)public final void javax.swing.JComponent.setActionMap(javax.swing.ActionMap)public static void javax.swing.JComponent.setDefaultLocale(java.util.Locale)public void javax.swing.JComponent.setToolTipText(java.lang.String)public void javax.swing.JComponent.setAutoscrolls(boolean)public void javax.swing.JComponent.setTransferHandler(javax.swing.TransferHandler)public void javax.swing.JComponent.setOpaque(boolean)public void javax.swing.JComponent.setDoubleBuffered(boolean)
javax.swing.JComponent
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
public void java.awt.Container.setLayout(java.awt.LayoutManager)public void java.awt.Container.setFocusTraversalPolicy(java.awt.FocusTraversalPolicy)public void java.awt.Container.setFocusCycleRoot(boolean)
java.awt.Container
public void java.awt.Component.setName(java.lang.String)public void java.awt.Component.setSize(java.awt.Dimension)public synchronized void java.awt.Component.setDropTarget(java.awt.dnd.DropTarget)public void java.awt.Component.setLocale(java.util.Locale)public void java.awt.Component.setLocation(java.awt.Point)public void java.awt.Component.setBounds(java.awt.Rectangle)public void java.awt.Component.setCursor(java.awt.Cursor)public void java.awt.Component.setIgnoreRepaint(boolean)public void java.awt.Component.setFocusable(boolean)public void java.awt.Component.setFocusTraversalKeysEnabled(boolean)public void java.awt.Component.setComponentOrientation(java.awt.ComponentOrientation)
java.awt.Component
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Introspectionpackage com.carlsbadcubes.codecamp;import java.lang.reflect.*;
public class Introspector { /** * Trys to instantiate a given class using its default constructor * then its sets its name using a setName(String name) method. * @param classname <code>String</code> - name of the class to be loaded and instantiated * @param name <code>String</code> - parameter to be used in a setName method * @return <code>Object</code> of the given class or null. */ public static Object createNamedInstance(String classname, String name) { Object obj = null; try { Class cls = Class.forName(classname); // shortcut would be to just call cls.newInstance(); Constructor ctor = cls.getConstructor(); // gets the default ctor if available. obj = ctor.newInstance(); // call with empty parameter array Method method = cls.getMethod("setName", String.class); method.invoke(obj, name ); } catch (ClassNotFoundException e) { // intent. empty } catch (NoSuchMethodException e) { // intent. empty } catch (InstantiationException e) { // intent. empty } catch (IllegalAccessException e) { // intent. empty } catch (InvocationTargetException e) { // intent. empty } return obj; }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Introspectionpackage com.carlsbadcubes.codecamp;import java.lang.reflect.*;
public class Introspector {
public static Object createNamedInstance( String classname, String name ) { !Object obj = null; !try {
! ! ! Class cls = Class.forName( classname ); // shortcut would be to just call cls.newInstance();
Constructor ctor = cls.getConstructor(); // gets the default ctor obj = ctor.newInstance(); // call with empty parameter array
Method method = cls.getMethod( "setName", String.class ); method.invoke( obj, name ); ! ! ! } catch (Exception e) { // intent. empty !} !return obj; }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Introspection TestCasepackage com.carlsbadcubes.codecamp;
import junit.framework.TestCase;import javax.swing.*;
public class IntrospectorTest extends TestCase {
public void testInstatiate() { ! ! Object obj = ! ! Introspector.createNamedInstance("javax.swing.JLabel", "myLabel"); ! ! TestCase.assertTrue( obj != null ); !TestCase.assertTrue( JLabel.class.equals(obj.getClass()) ); !TestCase.assertEquals( "myLabel", ((JLabel) obj).getName() );! }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Alternative to code generation
Decouple GUI Definition from Code
Expressive and Compact
Maintainable and Independent
Declarative Programming for GUIs
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
XUL - XAML - MXML
• Separating the GUI layout from the GUI logic (event-handlers) is not new - describing the UI in XML is methodology de jour.
• Xml User Interface Language is a markup language for describing user interfaces.
• XUL Engine (a.k.a XUL Motor) converts XML descriptors into a User Interface at Runtime.
• XUL dialects exist for almost any platform and for many programming languages.
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Mozilla - http://www.mozilla.org Runs on Mac OS, Linux, Solaris, FreeBSD, Irix, BeOS, HPUX, OS/2, BSD, and more
Luxor - http://luxor-xul.sourceforge.net Xul toolkit includes web server, portal engine, template engine, scripting interpreter etc.
SwixML - http://www.swixml.org Tiny (less than 50k) XUL Motor in Java for creating Swing UIs for apps or applets
Xoetrope XUI -http://www.xoetrope.com/xuiXUL motor in Java for building AWT UIs running on Java 1.1.8 and up;
Thinlets - http://www.thinlet.com Tiny XUL Motor in Java (less than 40k); designed for applets or mobile devices; runs even on Java 1.1; no Swing support.
XU
L ENG
INES
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Joshua Marinacci, Member of Sun’s Swing Team: “The net result of working with SwixML is delivering better-looking applications in less time, and that is always a good thing.”
Ben Galbraith, author of Professional JSP 2.0 and Professional Apache Tomcat: “SwixML is the most mature XUL/Swing offering.If you're looking for a way to define your Swing UI's in XML, SwixML is one of the best choices around.”
Rick Jelliffe, CTO of Topologi: “By moving to SwixML, we reduced source code size by 500k and improved maintainability and startup time.”
Kate Rhodes: “I think that SwixML is the easiest way to define a Swing layout, period.”
Hans Mueller, CTO for Sun's Desktop Division: ”When defining Java Swing GUIs declaratively, SwixML is the strongest example.”
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
http://www.swixml.org/TS-7122.pdf
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
• SwiXml, is a small GUI generating engine for Java applications and applets. Graphical User Interfaces are described in XML documents that are parsed at runtime and rendered into javax.swing objects.
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Swixml, a different XUL Motorfocus is completely on javax.swing.
Programmers knowing Swing can immediately start writing descriptors. No additional XML dialect has to be learned: Class names translate into tags and method names into attributes.
fast - since no additional layers had to be added on top of the Swing objects.
small - despite the fact that the swixml.jar file is only about 50 Kbyte in size, almost all Swing objects and widgets are supported.
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Hello World - XUL Descriptor
<?xml version="1.0" encoding="UTF-8"?><frame size="440,380" title="Hello SWIXML World">
<panel constraints="BorderLayout.NORTH"> <label font="ARIAL-BOLD-16" foreground="blue" text="Hello World!"/> </panel>
<panel constraints="BorderLayout.SOUTH"> <button text="Click Here" /> </panel>
</frame>
Inside Swixml
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Inside SwixmlHello World - Java Code
public class Foo {
public Foo() throws Exception { new SwingEngine().render( "xul/gui.xml").setVisible( true ); }
public static void main( String[] args ) throws Exception { new Foo(); }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Inside SwixmlSimple Editor - XUL Descriptor
<?xml version="1.0" encoding="UTF-8"?><frame size="440,380" title="Hello SWIXML World">
<panel constraints="BorderLayout.NORTH"> <label font="ARIAL-BOLD-16" foreground="green" text="Swixml Editor"/> </panel>
<scrollpane><editorpane id=”ep”/></scrollpane>
<panel constraints="BorderLayout.SOUTH"> <button text="Click Here" action=”show”/> </panel>
</frame>
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Inside SwixmlSimple Editor - Java Code
public class Foo { private JEditorPane ep; public Action show = new AbstractAction() { public void actionPerformed( ActionEvent event ) { new SwingEngine().render( new StringReader( ep.getText() ) ).setVisible( true ); } }; public Foo() throws Exception { new SwingEngine( this ).render( "xul/gui.xml").setVisible( true ); } public static void main( String[] args ) throws Exception { new Foo(); }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Inside the Swixml MotorSwixml introspects all the registered classes for their constructors and set-methods.In case a setter method's parameters can be constructed by converting a String, the setter gets registered and can be called through an xml attribute.
<label text="My blue label" color="blue"/>
works, because a JLabel's setText(String s) methods takes a String typed parameter and Swixml provides a ColorConverter, capable of converting a String into a java.awt.Color typed object.
There is also the initclass attribute, which is useful when the class that is represented by the tag name, needs to be instantiated by something other than the default-contructor.
<combobox initclass="com.xyz.ComboModel" />
instantiates a JComboBox class by using the constructor that takes a ComboBoxModel parameter: JComboBox(ComboBoxModel aModel)
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Mapping private membersprivate void mapMembers( Object obj, Class cls ) { if (obj != null && cls != null && !Object.class.equals( cls )) { Field[] flds = cls.getDeclaredFields();
for (int i = 0; i < flds.length; i++) { Object widget = idmap.get( flds[i].getName() ); if ( widget != null && flds[i].getType().isAssignableFrom ( widget.getClass() ) && !Modifier.isTransient( flds[i].getModifiers() )) { try { boolean accessible = flds[i].isAccessible(); flds[i].setAccessible(true); flds[i].set(obj, widget); flds[i].setAccessible( accessible ); } catch ( .. ) { } } } mapMembers( obj, cls.getSuperclass() ); }}
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
I've talked a lot about how declarative programming and especially UI definition takes up less lines of code. But there are hundreds of lines of parser code supporting the declarative UI definition, remember he two phases: Declaration and Interpretation? I would rather see you think about where you can take advantage of declarative programming, like in areas where you want to abstract functionality to gain flexibility in maintenance or extensibility.
Don’t get me wrong ...
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Q & A
© 2003-2006 Carlsbad Cubes© 2009 wolfpaulus.com
Thanks for coming