A recent post on the forum started:
"I've written a WidgetBuilder to handle collections... [by using] Metawidget to produce a [nested Metawidget] component for each object in the collection..."
We've covered something like this before, but this time things were complicated by the need to support BeansBindingProcessor with both save and rebind.
Here's what we came up with (needs Metawidget 2.3):
package com.myapp;
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import org.metawidget.inspector.annotation.UiComesAfter;
import org.metawidget.swing.SwingMetawidget;
import org.metawidget.swing.widgetbuilder.SwingWidgetBuilder;
import org.metawidget.swing.widgetprocessor.binding.beansbinding.BeansBindingProcessor;
import org.metawidget.util.*;
import org.metawidget.widgetbuilder.composite.*;
import org.metawidget.widgetbuilder.iface.WidgetBuilder;
public class Application {
public static void main( final String[] args ) {
// Model
final Person homerSimpson = new Person( "Homer Simpson", "Bart", "Lisa",
"Maggie" );
final Person nedFlanders = new Person( "Ned Flanders", "Rod", "Todd" );
// UI
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// Metawidget
final SwingMetawidget metawidget = new SwingMetawidget();
metawidget.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
CompositeWidgetBuilderConfig<JComponent, SwingMetawidget> config = new CompositeWidgetBuilderConfig<JComponent, SwingMetawidget>();
config.setWidgetBuilders( new CollectionWidgetBuilder(),
new SwingWidgetBuilder() );
metawidget
.setWidgetBuilder( new CompositeWidgetBuilder<JComponent, SwingMetawidget>(
config ) );
metawidget.addWidgetProcessor( new CollectionBeansBindingProcessor() );
metawidget.setToInspect( homerSimpson );
frame.add( metawidget, BorderLayout.CENTER );
// Buttons
JButton rebindButton = new JButton( "Rebind" );
rebindButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed( ActionEvent e ) {
try {
BeansBindingProcessor beansBindingProcessor = metawidget
.getWidgetProcessor( BeansBindingProcessor.class );
beansBindingProcessor.save( metawidget );
JOptionPane.showMessageDialog(
frame,
"Saved children as "
+ ( (Person) metawidget.getToInspect() )
.getChildren() );
beansBindingProcessor.rebind( nedFlanders, metawidget );
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( frame, ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE );
return;
}
}
} );
JPanel buttonPanel = new JPanel();
buttonPanel.add( rebindButton );
frame.add( buttonPanel, BorderLayout.SOUTH );
frame.setSize( 400, 200 );
frame.setVisible( true );
}
//
// Model
//
public static class Person {
private String mName;
private List<Person> mChildren = CollectionUtils.newArrayList();
public Person( String name, String... children ) {
mName = name;
for ( String child : children ) {
mChildren.add( new Person( child ) );
}
}
public String getName() {
return mName;
}
public void setName( String name ) {
mName = name;
}
@UiComesAfter( "name" )
public List<Person> getChildren() {
return mChildren;
}
@Override
public String toString() {
return mName;
}
}
//
// Widget Builder
//
public static class CollectionWidgetBuilder
implements WidgetBuilder<JComponent, SwingMetawidget> {
@Override
public JComponent buildWidget( String elementName,
Map<String, String> attributes, SwingMetawidget metawidget ) {
// Not for us?
Class<?> clazz = WidgetBuilderUtils.getActualClassOrType(
attributes, null );
if ( clazz == null ) {
return null;
}
if ( !Collection.class.isAssignableFrom( clazz ) ) {
return null;
}
// Create nested Metawidgets
List<?> list = (List<?>) ClassUtils.getProperty(
metawidget.getToInspect(), attributes.get( NAME ) );
if ( list == null || list.isEmpty() ) {
return null;
}
JPanel panel = new JPanel();
panel.setLayout( new BoxLayout( panel, javax.swing.BoxLayout.Y_AXIS ) );
for ( Object child : list ) {
SwingMetawidget nestedMetawidget = new SwingMetawidget();
Map<String, String> emptyAttributes = Collections.emptyMap();
metawidget.initNestedMetawidget( nestedMetawidget,
emptyAttributes );
nestedMetawidget.setToInspect( child );
nestedMetawidget.setPath( child.getClass().getName() );
panel.add( nestedMetawidget );
}
return panel;
}
}
//
// Widget Processor
//
public static class CollectionBeansBindingProcessor
extends BeansBindingProcessor {
@Override
public JComponent processWidget( JComponent component,
String elementName, Map<String, String> attributes,
SwingMetawidget metawidget ) {
// Support nested widgets from the CollectionWidgetBuilder
if ( component instanceof JPanel ) {
getNestedPanels( metawidget ).put( attributes.get( NAME ),
(JPanel) component );
return component;
}
return super.processWidget( component, elementName, attributes,
metawidget );
}
@Override
public void save( SwingMetawidget metawidget ) {
super.save( metawidget );
// Nested save
for ( JPanel nestedPanel : getNestedPanels( metawidget ).values() ) {
for ( Component nestedComponent : nestedPanel.getComponents() ) {
if ( nestedComponent instanceof SwingMetawidget ) {
SwingMetawidget nestedMetawidget = (SwingMetawidget) nestedComponent;
save( nestedMetawidget );
}
}
}
}
@Override
public void rebind( Object toRebind, SwingMetawidget metawidget ) {
super.rebind( toRebind, metawidget );
// Cannot expect to rebind nestedPanels, so rebuild them instead
for ( Map.Entry<String, JPanel> entry : getNestedPanels( metawidget )
.entrySet() ) {
// Clear the panel
JPanel panel = entry.getValue();
panel.removeAll();
// Use a dummy Metawidget to create just the child Metawidgets
// of the Collection
SwingMetawidget dummyMetawidget = new SwingMetawidget();
Map<String, String> attributes = CollectionUtils.newHashMap();
attributes.put( NAME, entry.getKey() );
metawidget.initNestedMetawidget( dummyMetawidget, attributes );
// Copy them into our real Metawidget
for ( Component child : ( (JPanel) dummyMetawidget
.getComponent( 1 ) ).getComponents() ) {
panel.add( child );
}
}
metawidget.validate();
}
private Map<String, JPanel> getNestedPanels( SwingMetawidget metawidget ) {
Map<String, JPanel> nestedPanels = (Map<String, JPanel>) metawidget
.getClientProperty( CollectionBeansBindingProcessor.class );
if ( nestedPanels == null ) {
nestedPanels = CollectionUtils.newHashMap();
metawidget.putClientProperty(
CollectionBeansBindingProcessor.class, nestedPanels );
}
return nestedPanels;
}
}
}
import static org.metawidget.inspector.InspectionResultConstants.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import org.metawidget.inspector.annotation.UiComesAfter;
import org.metawidget.swing.SwingMetawidget;
import org.metawidget.swing.widgetbuilder.SwingWidgetBuilder;
import org.metawidget.swing.widgetprocessor.binding.beansbinding.BeansBindingProcessor;
import org.metawidget.util.*;
import org.metawidget.widgetbuilder.composite.*;
import org.metawidget.widgetbuilder.iface.WidgetBuilder;
public class Application {
public static void main( final String[] args ) {
// Model
final Person homerSimpson = new Person( "Homer Simpson", "Bart", "Lisa",
"Maggie" );
final Person nedFlanders = new Person( "Ned Flanders", "Rod", "Todd" );
// UI
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// Metawidget
final SwingMetawidget metawidget = new SwingMetawidget();
metawidget.setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
CompositeWidgetBuilderConfig<JComponent, SwingMetawidget> config = new CompositeWidgetBuilderConfig<JComponent, SwingMetawidget>();
config.setWidgetBuilders( new CollectionWidgetBuilder(),
new SwingWidgetBuilder() );
metawidget
.setWidgetBuilder( new CompositeWidgetBuilder<JComponent, SwingMetawidget>(
config ) );
metawidget.addWidgetProcessor( new CollectionBeansBindingProcessor() );
metawidget.setToInspect( homerSimpson );
frame.add( metawidget, BorderLayout.CENTER );
// Buttons
JButton rebindButton = new JButton( "Rebind" );
rebindButton.addActionListener( new ActionListener() {
@Override
public void actionPerformed( ActionEvent e ) {
try {
BeansBindingProcessor beansBindingProcessor = metawidget
.getWidgetProcessor( BeansBindingProcessor.class );
beansBindingProcessor.save( metawidget );
JOptionPane.showMessageDialog(
frame,
"Saved children as "
+ ( (Person) metawidget.getToInspect() )
.getChildren() );
beansBindingProcessor.rebind( nedFlanders, metawidget );
} catch ( Exception ex ) {
JOptionPane.showMessageDialog( frame, ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE );
return;
}
}
} );
JPanel buttonPanel = new JPanel();
buttonPanel.add( rebindButton );
frame.add( buttonPanel, BorderLayout.SOUTH );
frame.setSize( 400, 200 );
frame.setVisible( true );
}
//
// Model
//
public static class Person {
private String mName;
private List<Person> mChildren = CollectionUtils.newArrayList();
public Person( String name, String... children ) {
mName = name;
for ( String child : children ) {
mChildren.add( new Person( child ) );
}
}
public String getName() {
return mName;
}
public void setName( String name ) {
mName = name;
}
@UiComesAfter( "name" )
public List<Person> getChildren() {
return mChildren;
}
@Override
public String toString() {
return mName;
}
}
//
// Widget Builder
//
public static class CollectionWidgetBuilder
implements WidgetBuilder<JComponent, SwingMetawidget> {
@Override
public JComponent buildWidget( String elementName,
Map<String, String> attributes, SwingMetawidget metawidget ) {
// Not for us?
Class<?> clazz = WidgetBuilderUtils.getActualClassOrType(
attributes, null );
if ( clazz == null ) {
return null;
}
if ( !Collection.class.isAssignableFrom( clazz ) ) {
return null;
}
// Create nested Metawidgets
List<?> list = (List<?>) ClassUtils.getProperty(
metawidget.getToInspect(), attributes.get( NAME ) );
if ( list == null || list.isEmpty() ) {
return null;
}
JPanel panel = new JPanel();
panel.setLayout( new BoxLayout( panel, javax.swing.BoxLayout.Y_AXIS ) );
for ( Object child : list ) {
SwingMetawidget nestedMetawidget = new SwingMetawidget();
Map<String, String> emptyAttributes = Collections.emptyMap();
metawidget.initNestedMetawidget( nestedMetawidget,
emptyAttributes );
nestedMetawidget.setToInspect( child );
nestedMetawidget.setPath( child.getClass().getName() );
panel.add( nestedMetawidget );
}
return panel;
}
}
//
// Widget Processor
//
public static class CollectionBeansBindingProcessor
extends BeansBindingProcessor {
@Override
public JComponent processWidget( JComponent component,
String elementName, Map<String, String> attributes,
SwingMetawidget metawidget ) {
// Support nested widgets from the CollectionWidgetBuilder
if ( component instanceof JPanel ) {
getNestedPanels( metawidget ).put( attributes.get( NAME ),
(JPanel) component );
return component;
}
return super.processWidget( component, elementName, attributes,
metawidget );
}
@Override
public void save( SwingMetawidget metawidget ) {
super.save( metawidget );
// Nested save
for ( JPanel nestedPanel : getNestedPanels( metawidget ).values() ) {
for ( Component nestedComponent : nestedPanel.getComponents() ) {
if ( nestedComponent instanceof SwingMetawidget ) {
SwingMetawidget nestedMetawidget = (SwingMetawidget) nestedComponent;
save( nestedMetawidget );
}
}
}
}
@Override
public void rebind( Object toRebind, SwingMetawidget metawidget ) {
super.rebind( toRebind, metawidget );
// Cannot expect to rebind nestedPanels, so rebuild them instead
for ( Map.Entry<String, JPanel> entry : getNestedPanels( metawidget )
.entrySet() ) {
// Clear the panel
JPanel panel = entry.getValue();
panel.removeAll();
// Use a dummy Metawidget to create just the child Metawidgets
// of the Collection
SwingMetawidget dummyMetawidget = new SwingMetawidget();
Map<String, String> attributes = CollectionUtils.newHashMap();
attributes.put( NAME, entry.getKey() );
metawidget.initNestedMetawidget( dummyMetawidget, attributes );
// Copy them into our real Metawidget
for ( Component child : ( (JPanel) dummyMetawidget
.getComponent( 1 ) ).getComponents() ) {
panel.add( child );
}
}
metawidget.validate();
}
private Map<String, JPanel> getNestedPanels( SwingMetawidget metawidget ) {
Map<String, JPanel> nestedPanels = (Map<String, JPanel>) metawidget
.getClientProperty( CollectionBeansBindingProcessor.class );
if ( nestedPanels == null ) {
nestedPanels = CollectionUtils.newHashMap();
metawidget.putClientProperty(
CollectionBeansBindingProcessor.class, nestedPanels );
}
return nestedPanels;
}
}
}
2 comments:
What is the way to render the nested widgets with Javascript in AngularJS ?
See this blog entry: http://blog.kennardconsulting.com/2014/05/angularjs-create-editable-tables-with.html
Download the example. Inside the example, inside directives.js, edit the line that says 'var input = $( '<input type="text"' to instead say 'var input = $( '<metawidget ng-model="' and go from there.
Post a Comment