Revision: 1232
Author: peterdb
Date: 2006-07-25 04:51:57 -0700 (Tue, 25 Jul 2006)
ViewCVS: http://svn.sourceforge.net/spring-rich-c/?rev=1232&view=rev
Log Message:
-----------
some refactoring to make the interceptor testable + added tests
Modified Paths:
--------------
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactory.java
Added Paths:
-----------
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptor.java
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactoryTests.java
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorTests.java
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/TestBean.java
Added:
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptor.java
===================================================================
---
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptor.java
(rev 0)
+++
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptor.java
2006-07-25 11:51:57 UTC (rev 1232)
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.form.builder.support;
+
+import java.awt.BorderLayout;
+
+import java.awt.Insets;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import java.util.Locale;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+import javax.swing.JPanel;
+
+import org.springframework.binding.form.FormModel;
+import org.springframework.binding.value.ValueChangeDetector;
+import org.springframework.binding.value.support.ValueHolder;
+import org.springframework.context.MessageSource;
+import org.springframework.richclient.application.ApplicationServicesLocator;
+import org.springframework.richclient.factory.AbstractControlFactory;
+import org.springframework.richclient.image.IconSource;
+import org.springframework.richclient.util.OverlayHelper;
+
+/**
+ * Adds a "dirty overlay" to a component that is triggered by user editing.
The overlaid
+ * image is retrieved by the image key "dirty.overlay". The image is placed at
the
+ * top-left corner of the component, and the image's tooltip is set to a
message
+ * (retrieved with key "dirty.message") such as "{field} has changed, original
value was
+ * {value}.". It also adds a small revert button that resets the value of the
field.
+ *
+ * @author Peter De Bruycker
+ */
+public class DirtyIndicatorInterceptor extends
AbstractFormComponentInterceptor {
+ private static final String DIRTY_ICON_KEY = "dirty.overlay";
+ private static final String DIRTY_MESSAGE_KEY = "dirty.message";
+ private static final String REVERT_ICON_KEY = "revert.overlay";
+ private static final String REVERT_MESSAGE_KEY = "revert.message";
+
+ private ValueChangeDetector valueChangeDetector;
+
+ public DirtyIndicatorInterceptor(FormModel formModel) {
+ super(formModel);
+ }
+
+ public void processComponent(final String propertyName, final JComponent
component) {
+ final OriginalValueHolder originalValueHolder = new
OriginalValueHolder();
+ final DirtyOverlay overlay = new DirtyOverlay(getFormModel(),
propertyName, originalValueHolder);
+
+ final ValueHolder reset = new ValueHolder(Boolean.FALSE);
+ getFormModel().getValueModel(propertyName).addValueChangeListener(new
PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (reset.getValue() == Boolean.TRUE) {
+ originalValueHolder.reset();
+ reset.setValue(Boolean.FALSE);
+
+ overlay.setVisible(false);
+
+ return;
+ }
+
+ if (!originalValueHolder.isInitialized()) {
+
originalValueHolder.setOriginalValue(evt.getOldValue());
+ }
+
+ Object oldValue = originalValueHolder.getValue();
+ Object newValue = evt.getNewValue();
+
overlay.setVisible(getValueChangeDetector().hasValueChanged(oldValue,
newValue));
+ }
+ });
+ getFormModel().getFormObjectHolder().addValueChangeListener(new
PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent evt) {
+ // reset original value, new "original" value is in
the form model as
+ // the form object has changed
+ reset.setValue(Boolean.TRUE);
+ }
+ });
+
+ InterceptorOverlayHelper.attachOverlay(overlay.getControl(),
component, OverlayHelper.NORTH_WEST, 3, 0);
+ overlay.setVisible(false);
+ }
+
+ private ValueChangeDetector getValueChangeDetector() {
+ if (valueChangeDetector == null) {
+ valueChangeDetector =
+
(ValueChangeDetector)ApplicationServicesLocator.services().getService(ValueChangeDetector.class);
+ }
+
+ return valueChangeDetector;
+ }
+
+ private static class DirtyOverlay extends AbstractControlFactory {
+ private JButton revertButton;
+ private JLabel dirtyLabel;
+ private FormModel formModel;
+ private String propertyName;
+ private OriginalValueHolder originalValueHolder;
+
+ public DirtyOverlay(FormModel formModel, String propertyName,
OriginalValueHolder originalValueHolder) {
+ this.formModel = formModel;
+ this.propertyName = propertyName;
+ this.originalValueHolder = originalValueHolder;
+ }
+
+ protected JComponent createControl() {
+ JPanel control = new JPanel(new BorderLayout());
+ control.setName("dirtyOverlay");
+
+ control.setOpaque(false);
+
+ IconSource iconSource =
(IconSource)ApplicationServicesLocator.services().getService(IconSource.class);
+ Icon icon = iconSource.getIcon(DIRTY_ICON_KEY);
+ dirtyLabel = new JLabel(icon);
+ control.add(dirtyLabel, BorderLayout.CENTER);
+
+ createRevertButton();
+ control.add(revertButton, BorderLayout.LINE_END);
+
+ return control;
+ }
+
+ private void createRevertButton() {
+ IconSource iconSource =
(IconSource)ApplicationServicesLocator.services().getService(IconSource.class);
+ Icon icon = iconSource.getIcon(REVERT_ICON_KEY);
+
+ revertButton = new JButton(icon);
+ revertButton.setBorderPainted(false);
+ revertButton.setContentAreaFilled(false);
+ revertButton.setFocusable(false);
+ revertButton.setMargin(new Insets(-3, -3, -3, -3));
+ revertButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // reset
+
formModel.getValueModel(propertyName).setValue(originalValueHolder.getValue());
+ }
+ });
+ }
+
+ public void setVisible(boolean visible) {
+ getControl().setVisible(visible);
+ // manually set the size, otherwise sometimes the overlay is not
shown (it has size 0,0)
+ getControl().setSize(getControl().getPreferredSize());
+
+ if (visible) {
+ MessageSource messageSource =
+
(MessageSource)ApplicationServicesLocator.services().getService(MessageSource.class);
+ String dirtyTooltip =
+ messageSource.getMessage(DIRTY_MESSAGE_KEY, new Object[] {
formModel.getFieldFace(propertyName).getDisplayName(),
+
originalValueHolder.getValue() },
+ Locale.getDefault());
+ dirtyLabel.setToolTipText(dirtyTooltip);
+
+ String revertTooltip =
+ messageSource.getMessage(REVERT_MESSAGE_KEY, new Object[]
{ formModel.getFieldFace(propertyName).getDisplayName() },
+ Locale.getDefault());
+ revertButton.setToolTipText(revertTooltip);
+ }
+ }
+ }
+
+ private static class OriginalValueHolder {
+ private boolean initialized;
+ private Object originalValue;
+
+ public void setOriginalValue(Object value) {
+ initialized = true;
+ originalValue = value;
+ }
+
+ public void reset() {
+ initialized = false;
+ originalValue = null;
+ }
+
+ public Object getValue() {
+ return originalValue;
+ }
+
+ public boolean isInitialized() {
+ return initialized;
+ }
+ }
+}
Modified:
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactory.java
===================================================================
---
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactory.java
2006-07-25 11:31:31 UTC (rev 1231)
+++
trunk/spring-richclient/support/src/main/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactory.java
2006-07-25 11:51:57 UTC (rev 1232)
@@ -15,137 +15,17 @@
*/
package org.springframework.richclient.form.builder.support;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.Locale;
-
-import javax.swing.Icon;
-import javax.swing.JComponent;
-import javax.swing.JLabel;
-import javax.swing.SwingConstants;
-
import org.springframework.binding.form.FormModel;
-import org.springframework.binding.value.support.ValueHolder;
-import org.springframework.context.MessageSource;
-import org.springframework.richclient.application.ApplicationServicesLocator;
import org.springframework.richclient.form.builder.FormComponentInterceptor;
import
org.springframework.richclient.form.builder.FormComponentInterceptorFactory;
-import org.springframework.richclient.image.IconSource;
-import org.springframework.util.ObjectUtils;
/**
- * Adds a "dirty overlay" to a component that is triggered by user editing.
The overlaid
- * image is retrieved by the image key "dirty.overlay". The image is placed at
the
- * top-left corner of the component, and the image's tooltip is set to a
message
- * (retrieved with key "dirty.message") such as "{field} has changed, original
value was
- * {value}.".
+ * Factory for <code>DirtyIndicatorInterceptor</code> instances.
*
- *
* @author Peter De Bruycker
*/
public class DirtyIndicatorInterceptorFactory implements
FormComponentInterceptorFactory {
- private static final String DEFAULT_ICON_KEY = "dirty.overlay";
- private static final String DEFAULT_MESSAGE_KEY = "dirty.message";
-
- private String iconKey = DEFAULT_ICON_KEY;
-
- public FormComponentInterceptor getInterceptor( FormModel formModel ) {
- IconSource iconSource = (IconSource)
ApplicationServicesLocator.services().getService(IconSource.class);
- return new DirtyIndicatorInterceptor( formModel, iconSource.getIcon(
iconKey ) );
+ public FormComponentInterceptor getInterceptor(FormModel formModel) {
+ return new DirtyIndicatorInterceptor(formModel);
}
-
- public void setIconKey( String iconKey ) {
- this.iconKey = iconKey;
- }
-
- public String getIconKey() {
- return iconKey;
- }
-
- private static class DirtyIndicatorInterceptor extends
AbstractFormComponentInterceptor {
- private Icon icon;
-
- private DirtyIndicatorInterceptor( FormModel formModel, Icon icon ) {
- super( formModel );
- this.icon = icon;
- }
-
- public void processComponent( final String propertyName, final
JComponent component ) {
- final JLabel overlay = new JLabel( icon );
-
- final OriginalValueHolder originalValueHolder = new
OriginalValueHolder();
- final ValueHolder reset = new ValueHolder( Boolean.FALSE );
- getFormModel().getValueModel( propertyName
).addValueChangeListener( new PropertyChangeListener() {
- public void propertyChange( PropertyChangeEvent evt ) {
- if( reset.getValue() == Boolean.TRUE ) {
- originalValueHolder.reset();
- reset.setValue( Boolean.FALSE );
-
- overlay.setVisible( false );
-
- return;
- }
-
- if( !originalValueHolder.isInitialized() ) {
- originalValueHolder.setOriginalValue(
evt.getOldValue() );
- }
-
- if( isDirty(originalValueHolder.getValue(),
evt.getNewValue()) ) {
- MessageSource messageSource =
(MessageSource)ApplicationServicesLocator.services().getService(MessageSource.class);
- String tooltip = messageSource.getMessage(
- DEFAULT_MESSAGE_KEY,
- new Object[] {
- messageSource.getMessage( propertyName
+ ".label", null,
- Locale.getDefault() ),
originalValueHolder.getValue() }, null );
- overlay.setToolTipText( tooltip );
- overlay.setVisible( true );
- } else {
- overlay.setVisible( false );
- }
- }
- } );
- getFormModel().getFormObjectHolder().addValueChangeListener( new
PropertyChangeListener() {
- public void propertyChange( PropertyChangeEvent evt ) {
- // reset original value, new "original" value is in the
form model as
- // the form object has changed
- reset.setValue( Boolean.TRUE );
- }
- } );
-
- InterceptorOverlayHelper.attachOverlay( overlay, component,
SwingConstants.NORTH_WEST, 0, 0 );
- overlay.setVisible( false );
- }
-
- private boolean isDirty( Object oldValue, Object newValue ) {
- if( oldValue == null && newValue instanceof String ) {
- // hack for string comparison: null equals empty string
- return !ObjectUtils.nullSafeEquals( "", newValue );
- }
-
- return !ObjectUtils.nullSafeEquals( oldValue, newValue );
- }
- }
-
- private static class OriginalValueHolder {
- private boolean initialized;
- private Object originalValue;
-
- public void setOriginalValue( Object value ) {
- initialized = true;
- originalValue = value;
- }
-
- public void reset() {
- initialized = false;
- originalValue = null;
- }
-
- public Object getValue() {
- return originalValue;
- }
-
- public boolean isInitialized() {
- return initialized;
- }
- }
}
Added:
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactoryTests.java
===================================================================
---
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactoryTests.java
(rev 0)
+++
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorFactoryTests.java
2006-07-25 11:51:57 UTC (rev 1232)
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.form.builder.support;
+
+import junit.framework.TestCase;
+
+import org.springframework.binding.form.FormModel;
+import org.springframework.binding.form.support.DefaultFormModel;
+
+/**
+ * Sanity tests for <code>DirtyIndicatorInterceptorFactory</code>
+ *
+ * @author Peter De Bruycker
+ */
+public class DirtyIndicatorInterceptorFactoryTests extends TestCase {
+ public void testGetInterceptor() {
+ DirtyIndicatorInterceptorFactory factory = new
DirtyIndicatorInterceptorFactory();
+
+ FormModel formModel = new DefaultFormModel();
+ DirtyIndicatorInterceptor interceptor =
(DirtyIndicatorInterceptor)factory.getInterceptor(formModel);
+
+ assertNotNull("factory returned null", interceptor);
+ }
+
+ public void testGetInterceptorWithNullFormModel() {
+ try {
+ DirtyIndicatorInterceptorFactory factory = new
DirtyIndicatorInterceptorFactory();
+ factory.getInterceptor(null);
+ } catch (IllegalArgumentException ex) {
+ // test passes
+ }
+
+ }
+}
Added:
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorTests.java
===================================================================
---
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorTests.java
(rev 0)
+++
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/DirtyIndicatorInterceptorTests.java
2006-07-25 11:51:57 UTC (rev 1232)
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.form.builder.support;
+
+import java.util.Locale;
+
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JTextField;
+
+import org.springframework.binding.form.FormModel;
+import org.springframework.binding.form.support.DefaultFormModel;
+import org.springframework.binding.value.ValueModel;
+import org.springframework.context.support.StaticMessageSource;
+import
org.springframework.richclient.application.support.DefaultApplicationServices;
+import org.springframework.richclient.form.binding.Binding;
+import org.springframework.richclient.form.binding.swing.SwingBindingFactory;
+import org.springframework.richclient.test.SpringRichTestCase;
+
+/**
+ * Tests for <code>DirtyIndicatorInterceptor</code>.
+ *
+ * @author Peter De Bruycker
+ */
+public class DirtyIndicatorInterceptorTests extends SpringRichTestCase {
+ public void testProcessComponent() throws InterruptedException {
+ TestBean bean = new TestBean();
+ bean.setProperty("original value");
+
+ FormModel formModel = new DefaultFormModel(bean);
+
+ DirtyIndicatorInterceptor interceptor = new
DirtyIndicatorInterceptor(formModel);
+ assertEquals(formModel, interceptor.getFormModel());
+
+ Binding binding = new
SwingBindingFactory(formModel).createBinding("property");
+ JTextField field = (JTextField)binding.getControl();
+ field.setColumns(25);
+ assertNotNull("sanity check: binding defines no component", field);
+
+ interceptor.processComponent("property", field);
+
+ // start a frame to trigger visual updates
+ JFrame frame = new JFrame("test");
+ frame.getContentPane().add(field);
+ frame.pack();
+ frame.setVisible(true);
+
+ // trigger a show of the overlay, so we can get a reference to it
+ ValueModel valueModel = formModel.getValueModel("property");
+ valueModel.setValue("dirty");
+
+ // sleep for a while so the gui can update itself
+ Thread.sleep(500);
+ formModel.revert();
+
+ // find a reference to the overlay component
+ JLayeredPane layeredPane = frame.getRootPane().getLayeredPane();
+ assertEquals("sanity check: assume the layered pane only has one
component, and that it is a panel and the overlay",
+ 2, layeredPane.getComponentCount());
+ // the overlay is the first component
+ JComponent overlay = (JComponent)layeredPane.getComponent(0);
+ assertEquals("unable to locate overlay", "dirtyOverlay",
overlay.getName());
+
+ assertFalse("Overlay must be hidden", overlay.isVisible());
+
+ // mimic user editing
+ valueModel.setValue("ttt");
+ assertTrue("Value is dirty, so overlay must be visible",
overlay.isVisible());
+
+ // user reverts the edit
+ valueModel.setValue("original value");
+ assertFalse("value is not dirty, so overlay must be hidden",
overlay.isVisible());
+
+ // dispose of the frame
+ frame.dispose();
+ }
+
+ protected void registerBasicServices(DefaultApplicationServices
applicationServices) {
+ super.registerBasicServices(applicationServices);
+
+ StaticMessageSource messageSource = new StaticMessageSource();
+ messageSource.addMessage("dirty.message", Locale.getDefault(), "{0}
has changed, original value was {1}.");
+ messageSource.addMessage("revert.message", Locale.getDefault(),
"Revert value to {0}.");
+
+ messageSource.addMessage("property.label", Locale.getDefault(),
"Property");
+
+ applicationServices.setMessageSource(messageSource);
+ }
+}
Added:
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/TestBean.java
===================================================================
---
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/TestBean.java
(rev 0)
+++
trunk/spring-richclient/support/src/test/java/org/springframework/richclient/form/builder/support/TestBean.java
2006-07-25 11:51:57 UTC (rev 1232)
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package org.springframework.richclient.form.builder.support;
+
+/**
+ * Bean for testing purposes.
+ *
+ * @author Peter De Bruycker
+ */
+public class TestBean {
+ private String property;
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
spring-rich-c-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/spring-rich-c-cvs