/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.ui.ridgets.swt; import java.beans.PropertyChangeSupport; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Map; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.riena.core.marker.IMarkable; import org.eclipse.riena.core.test.RienaTestCase; import org.eclipse.riena.core.test.collect.UITestCase; import org.eclipse.riena.core.util.ReflectionUtils; import org.eclipse.riena.internal.ui.ridgets.swt.TextRidget; import org.eclipse.riena.ui.core.marker.ErrorMarker; import org.eclipse.riena.ui.core.marker.ICustomMarker; import org.eclipse.riena.ui.core.marker.MandatoryMarker; import org.eclipse.riena.ui.core.marker.NegativeMarker; import org.eclipse.riena.ui.core.marker.OutputMarker; import org.eclipse.riena.ui.ridgets.IControlDecoration; import org.eclipse.riena.ui.ridgets.ITextRidget; import org.eclipse.riena.ui.swt.lnf.ILnfResource; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; import org.eclipse.riena.ui.swt.lnf.rienadefault.RienaDefaultLnf; import org.eclipse.riena.ui.swt.utils.SwtUtilities; /** * Test the {@code MarkSupport} class. */ @UITestCase public class MarkerSupportTest extends RienaTestCase { private static final String HIDE_DISABLED_RIDGET_CONTENT = "HIDE_DISABLED_RIDGET_CONTENT"; private Display display; private Shell shell; @Override protected void setUp() throws Exception { super.setUp(); display = Display.getDefault(); shell = new Shell(display); } @Override protected void tearDown() { SwtUtilities.dispose(shell); display = null; } /** * Bug 294024: The control decoration is always associated with a UI element and must be reset when <tt>setUIControl(...)</tt> is called on a ridget. We can * not reuse the control decoration after the ui control has been changed. * <p> * see {@link ControlDecoration} */ public void testUpdateControlDecorationOnUpdateControl() throws Exception { final DefaultRealm realm = new DefaultRealm(); try { final Text control1 = new Text(shell, SWT.NONE); final Text control2 = new Text(shell, SWT.NONE); final ITextRidget ridget = new TextRidget(); ridget.setUIControl(control1); ridget.setErrorMarked(true); final MarkerSupport support = ReflectionUtils.getHidden(ridget, "markerSupport"); final IControlDecoration ed1 = ReflectionUtils.invokeHidden(support, "getOrCreateErrorDecorationForControl", control1); ridget.setUIControl(control2); assertNotSame(ed1, ReflectionUtils.invokeHidden(support, "getOrCreateErrorDecorationForControl", control2)); } finally { realm.dispose(); } } public void testHideDisabledRidgetContentSystemProperty() throws IOException { System.clearProperty(HIDE_DISABLED_RIDGET_CONTENT); assertFalse(getHideDisabledRidgetContent()); System.setProperty(HIDE_DISABLED_RIDGET_CONTENT, Boolean.FALSE.toString()); assertFalse(getHideDisabledRidgetContent()); System.setProperty(HIDE_DISABLED_RIDGET_CONTENT, Boolean.TRUE.toString()); assertTrue(getHideDisabledRidgetContent()); } /** * Tests the <i>private</i> method {@code createErrorDecoration}. * * @throws Exception * handled by JUnit */ public void testCreateErrorDecoration() throws Exception { final RienaDefaultLnf originalLnf = LnfManager.getLnf(); final DefaultRealm realm = new DefaultRealm(); try { final Text text = new Text(shell, SWT.NONE); final ITextRidget ridget = new TextRidget(); ridget.setUIControl(text); MarkerSupport support = new MarkerSupport(ridget, null); LnfManager.setLnf(new MyLnf()); ControlDecoration deco = ReflectionUtils.invokeHidden(support, "createErrorDecoration", text); assertEquals(100, deco.getMarginWidth()); assertNotNull(deco.getImage()); LnfManager.setLnf(new MyNonsenseLnf()); deco = ReflectionUtils.invokeHidden(support, "createErrorDecoration", text); assertEquals(1, deco.getMarginWidth()); assertNotNull(deco.getImage()); support = null; SwtUtilities.dispose(text); } finally { LnfManager.setLnf(originalLnf); realm.dispose(); } } private static boolean getHideDisabledRidgetContent() throws IOException { final MarkSupportClassLoader freshLoader = new MarkSupportClassLoader(); final Class<?> markerSupportClass = freshLoader.getFreshMarkSupportClass(); return ReflectionUtils.getHidden(markerSupportClass, HIDE_DISABLED_RIDGET_CONTENT); } public void testDisabledMarker() throws Exception { final DefaultRealm realm = new DefaultRealm(); try { final Text control = new Text(shell, SWT.NONE); final TextRidget ridget = new TextRidget(); ridget.setUIControl(control); final BasicMarkerSupport msup = ReflectionUtils.invokeHidden(ridget, "createMarkerSupport"); final Object visualizer = ReflectionUtils.invokeHidden(msup, "getDisabledMarkerVisualizer", (Object[]) null); assertNotNull(visualizer); ridget.setEnabled(false); assertEquals(1, control.getListeners(SWT.Paint).length); ridget.setEnabled(true); assertEquals(0, control.getListeners(SWT.Paint).length); } finally { realm.dispose(); } } public void testClearAllMarkes() { final DefaultRealm realm = new DefaultRealm(); try { final Text control = new Text(shell, SWT.NONE); shell.setVisible(true); final Color background = control.getBackground(); final Color foreground = control.getForeground(); final MyMarkerSupport markerSupport = new MyMarkerSupport(); // the LNF creates a MarkerSupport for every Ridget, so we have to provide our custom MarkerSupport at this point final ITextRidget ridget = new TextRidget() { @Override protected org.eclipse.riena.ui.ridgets.AbstractMarkerSupport createMarkerSupport() { return markerSupport; } }; ridget.setUIControl(control); markerSupport.init(ridget, new PropertyChangeSupport(new Object())); assertTrue(background.equals(control.getBackground())); ridget.addMarker(new MandatoryMarker()); markerSupport.updateMarkers(); assertFalse(background.equals(control.getBackground())); markerSupport.clearAllMarkers(control); assertEquals(background, control.getBackground()); ridget.addMarker(new OutputMarker()); markerSupport.updateMarkers(); assertFalse(background.equals(control.getBackground())); markerSupport.clearAllMarkers(control); assertEquals(background, control.getBackground()); ridget.addMarker(new NegativeMarker()); markerSupport.updateMarkers(); assertFalse(foreground.equals(control.getForeground())); markerSupport.clearAllMarkers(control); assertEquals(foreground, control.getForeground()); ridget.addMarker(new ErrorMarker()); markerSupport.updateMarkers(); final ControlDecoration errorDecoration = ReflectionUtils.invokeHidden(markerSupport, "getErrorDecorationForControl", control); assertNotNull(errorDecoration); assertTrue(errorDecoration.isVisible()); markerSupport.clearAllMarkers(control); assertFalse(errorDecoration.isVisible()); } finally { realm.dispose(); } } /** * Test reflection usage which does not cause an exception in method getControlBackground() */ public void testReflectionUtilsUsageInGetControlBackground() { final CCombo combo = new CCombo(shell, SWT.NONE); final Control text = ReflectionUtils.getHidden(combo, "list"); //$NON-NLS-1$ text.getBackground(); } /** * Tests the <i>private</i> method {@code getCustomBackground()}. */ public void testGetCustomBackground() { final DefaultRealm realm = new DefaultRealm(); try { final TextRidget ridget = new TextRidget(); final MarkerSupport support = new MarkerSupport(ridget, null); Color bg = ReflectionUtils.invokeHidden(support, "getCustomBackground", OutputMarker.class); assertNull(bg); ridget.addMarker(new CustomOutputMarker()); bg = ReflectionUtils.invokeHidden(support, "getCustomBackground", OutputMarker.class); Color expected = display.getSystemColor(SWT.COLOR_GREEN); assertEquals(expected, bg); ridget.addMarker(new Custom2OutputMarker()); bg = ReflectionUtils.invokeHidden(support, "getCustomBackground", OutputMarker.class); expected = display.getSystemColor(SWT.COLOR_BLUE); assertEquals(expected, bg); ridget.addMarker(new CustomMandatoryMarker()); bg = ReflectionUtils.invokeHidden(support, "getCustomBackground", OutputMarker.class); expected = display.getSystemColor(SWT.COLOR_BLUE); assertEquals(expected, bg); ridget.addMarker(new CustomMandatoryMarker()); bg = ReflectionUtils.invokeHidden(support, "getCustomBackground", MandatoryMarker.class); expected = display.getSystemColor(SWT.COLOR_YELLOW); assertEquals(expected, bg); } finally { realm.dispose(); } } /** * Tests the <i>private</i> method {@code getCustomForeground()}. */ public void testGetCustomForeground() { final DefaultRealm realm = new DefaultRealm(); try { final TextRidget ridget = new TextRidget(); final MarkerSupport support = new MarkerSupport(ridget, null); Color fg = ReflectionUtils.invokeHidden(support, "getCustomForeground", OutputMarker.class); assertNull(fg); ridget.addMarker(new CustomOutputMarker()); fg = ReflectionUtils.invokeHidden(support, "getCustomForeground", OutputMarker.class); Color expected = display.getSystemColor(SWT.COLOR_DARK_GREEN); assertEquals(expected, fg); ridget.addMarker(new Custom2OutputMarker()); fg = ReflectionUtils.invokeHidden(support, "getCustomForeground", OutputMarker.class); expected = display.getSystemColor(SWT.COLOR_CYAN); assertEquals(expected, fg); ridget.addMarker(new CustomMandatoryMarker()); fg = ReflectionUtils.invokeHidden(support, "getCustomForeground", OutputMarker.class); expected = display.getSystemColor(SWT.COLOR_CYAN); assertEquals(expected, fg); ridget.addMarker(new CustomMandatoryMarker()); fg = ReflectionUtils.invokeHidden(support, "getCustomForeground", MandatoryMarker.class); assertNull(fg); } finally { realm.dispose(); } } /** * Tests the <i>private</i> method {@code getNegativeForeground()}. */ public void testGetNegativeForeground() { final DefaultRealm realm = new DefaultRealm(); final Text text = new Text(shell, SWT.NONE); try { final TextRidget ridget = new TextRidget(); final MarkerSupport support = new MarkerSupport(ridget, null); ridget.setUIControl(text); Color fg = ReflectionUtils.invokeHidden(support, "getNegativeForeground"); Color expected = display.getSystemColor(SWT.COLOR_RED); assertEquals(expected, fg); ridget.addMarker(new CustomNegativeMarker()); fg = ReflectionUtils.invokeHidden(support, "getNegativeForeground"); expected = display.getSystemColor(SWT.COLOR_DARK_RED); assertEquals(expected, fg); } finally { SwtUtilities.dispose(text); realm.dispose(); } } // helping classes ////////////////// /** * This {@code ClassLoader}s method {@code getFreshMarkSupportClass()} retrieves with each call a new, fresh {@code MarkSupport} class. This allows testing * of the static field which gets initialized on class load. */ private static class MarkSupportClassLoader extends ClassLoader { public MarkSupportClassLoader() { super(MarkSupportClassLoader.class.getClassLoader()); } public Class<?> getFreshMarkSupportClass() throws IOException { final String resource = MarkerSupport.class.getName().replace('.', '/') + ".class"; final URL classURL = MarkerSupportTest.class.getClassLoader().getResource(resource); final InputStream is = classURL.openStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); int ch = -1; while ((ch = is.read()) != -1) { baos.write(ch); } final byte[] bytes = baos.toByteArray(); final Class<?> cl = super.defineClass(MarkerSupport.class.getName(), bytes, 0, bytes.length); resolveClass(cl); return cl; } } /** * Look and Feel with correct setting. */ private static class MyLnf extends RienaDefaultLnf { @Override protected void initializeTheme() { super.initializeTheme(); putLnfSetting(LnfKeyConstants.ERROR_MARKER_MARGIN, 100); putLnfSetting(LnfKeyConstants.ERROR_MARKER_HORIZONTAL_POSITION, SWT.RIGHT); putLnfSetting(LnfKeyConstants.ERROR_MARKER_VERTICAL_POSITION, SWT.BOTTOM); } } /** * Look and Feel with invalid setting: no settings and no images */ private static class MyNonsenseLnf extends RienaDefaultLnf { @Override protected void initializeTheme() { super.initializeTheme(); final Map<String, ILnfResource> resourceTable = ReflectionUtils.getHidden(MyNonsenseLnf.this, "resourceTable"); resourceTable.clear(); final Map<String, Object> settingTable = ReflectionUtils.getHidden(MyNonsenseLnf.this, "settingTable"); settingTable.clear(); } } /** * This extension changes the visibility of some protected methods for testing. */ private static class MyMarkerSupport extends MarkerSupport { @Override public void clearAllMarkers(final Control control) { super.clearAllMarkers(control); } @Override public void clearMandatory(final Control control) { super.clearMandatory(control); } } private class CustomOutputMarker extends OutputMarker implements ICustomMarker { public Object getBackground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_GREEN); } public Object getForeground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_DARK_GREEN); } } private class Custom2OutputMarker extends OutputMarker implements ICustomMarker { public Object getBackground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_BLUE); } public Object getForeground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_CYAN); } } private class CustomMandatoryMarker extends MandatoryMarker implements ICustomMarker { public Object getBackground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_YELLOW); } public Object getForeground(final IMarkable markable) { return null; } } private class CustomNegativeMarker extends NegativeMarker implements ICustomMarker { public Object getBackground(final IMarkable markable) { return null; } public Object getForeground(final IMarkable markable) { return display.getSystemColor(SWT.COLOR_DARK_RED); } } }