summaryrefslogtreecommitdiff
path: root/propertysheet/src/org/eclipse/wb/internal/core/utils
diff options
context:
space:
mode:
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/utils')
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java81
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java174
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java70
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java361
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java37
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java292
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java24
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java26
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java192
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java75
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java327
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java337
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java541
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java123
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java44
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java72
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java42
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java221
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java77
-rw-r--r--propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java118
20 files changed, 3234 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java
new file mode 100644
index 0000000..3df0e9b
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils;
+
+import com.google.common.base.Objects;
+
+/**
+ * Pair of two objects.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public final class Pair<L, R> {
+ private final L left;
+ private final R right;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public Pair(L left, R right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Object
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Pair<?, ?>)) {
+ return false;
+ }
+ Pair<?, ?> other = (Pair<?, ?>) o;
+ return Objects.equal(getLeft(), other.getLeft())
+ && Objects.equal(getRight(), other.getRight());
+ }
+
+ @Override
+ public int hashCode() {
+ int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
+ int hRight = getRight() == null ? 0 : getRight().hashCode();
+ return hLeft + 37 * hRight;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public L getLeft() {
+ return left;
+ }
+
+ public R getRight() {
+ return right;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Factory
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public static <L, R> Pair<L, R> create(L left, R right) {
+ return new Pair<L, R>(left, right);
+ }
+} \ No newline at end of file
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java
new file mode 100644
index 0000000..155b55c
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.binding.editors.controls;
+
+import com.google.common.collect.Lists;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.expressions.EvaluationResult;
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.ExpressionInfo;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;
+
+import java.util.List;
+
+/**
+ * Manager for installing/unistalling global handlers for {@link Control} actions commands.
+ *
+ * @author sablin_aa
+ * @author mitin_aa
+ */
+public abstract class AbstractControlActionsManager {
+ @SuppressWarnings("deprecation")
+ protected final Object[] COMMAND_HANDLER_IDS = new Object[]{
+ IWorkbenchActionDefinitionIds.COPY,
+ IWorkbenchActionDefinitionIds.CUT,
+ IWorkbenchActionDefinitionIds.PASTE,
+ IWorkbenchActionDefinitionIds.DELETE,
+ IWorkbenchActionDefinitionIds.SELECT_ALL,
+ IWorkbenchActionDefinitionIds.UNDO,
+ IWorkbenchActionDefinitionIds.REDO};
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected final Control m_control;
+ private boolean m_active = false;
+
+ public AbstractControlActionsManager(final Control control) {
+ m_control = control;
+ m_control.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ activateHandlers();
+ m_active = true;
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+ deactivateHandlers();
+ m_active = false;
+ }
+ });
+ m_control.addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ if (m_active) {
+ // deactivate on dispose
+ deactivateHandlers();
+ }
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Handlers
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected static final IHandler EMPTY_HANDLER = new AbstractHandler() {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ // do nothing
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ // of course, it is enabled ;)
+ return true;
+ }
+
+ @Override
+ public boolean isHandled() {
+ // we do not handle the actions; since action not handled, its' underlying SWT event
+ // would not be filtered by workbench, so it get a chance to be handled by SWT code
+ // or to be passed to the OS; see WorkbenchKeyboard.press() method.
+ return false;
+ }
+ };
+
+ /**
+ * @returns the global handler service.
+ */
+ public static IHandlerService getHandlerService() {
+ return (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
+ }
+
+ /**
+ * Activates the handlers for list of commands (see COMMAND_HANDLERS) with:<br>
+ * 1. The empty handler (except 'selectAll'), so underlying SWT event wouldn't be filtered by the
+ * workbench;<br>
+ * 2. Highest priority {@link Expression}, so this handler has a chance to be set.
+ */
+ protected void activateHandlers() {
+ IHandlerService service = getHandlerService();
+ for (int i = 0; i < COMMAND_HANDLER_IDS.length; ++i) {
+ String actionName = (String) COMMAND_HANDLER_IDS[i];
+ IHandler handler = getHandlerFor(actionName);
+ activateHandler(actionName, service, handler, new Expression() {
+ @Override
+ public EvaluationResult evaluate(IEvaluationContext context) throws CoreException {
+ return EvaluationResult.TRUE;
+ }
+
+ @Override
+ public void collectExpressionInfo(ExpressionInfo info) {
+ // get the highest priority
+ // note, if someone else has such priority, there will be key-binding conflicts logged.
+ info.markSystemPropertyAccessed();
+ info.markDefaultVariableAccessed();
+ info.addVariableNameAccess(ISources.ACTIVE_MENU_NAME);
+ }
+ });
+ }
+ }
+
+ protected IHandler getHandlerFor(String actionName) {
+ return EMPTY_HANDLER;
+ }
+
+ /**
+ * Activates handler and stores it into a collection for further deactivation.
+ */
+ private final List<IHandlerActivation> m_activations = Lists.newLinkedList();
+
+ private void activateHandler(String actionName,
+ IHandlerService service,
+ IHandler handler,
+ Expression highPriorityExpression) {
+ // activate handler and store it into a collection for further deactivation
+ m_activations.add(service.activateHandler(actionName, handler, highPriorityExpression));
+ }
+
+ /**
+ * Deactivates all handlers and clears handlers collection.
+ */
+ protected void deactivateHandlers() {
+ getHandlerService().deactivateHandlers(m_activations);
+ m_activations.clear();
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java
new file mode 100644
index 0000000..ff048bc
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.binding.editors.controls;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.IHandler;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds;
+
+/**
+ * Default manager for installing/unistalling global handlers for {@link Control} actions commands.
+ *
+ * @author sablin_aa
+ */
+public class DefaultControlActionsManager extends AbstractControlActionsManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public DefaultControlActionsManager(final Control control) {
+ super(control);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Handlers
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ protected IHandler getHandlerFor(String actionName) {
+ if (actionName.equalsIgnoreCase(IWorkbenchActionDefinitionIds.SELECT_ALL)) {
+ return SELECTALL_HANDLER;
+ }
+ return super.getHandlerFor(actionName);
+ }
+
+ /**
+ * Handler for process "Select all" action.
+ */
+ private final IHandler SELECTALL_HANDLER = new AbstractHandler() {
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ selectAllExecuted();
+ return null;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isHandled() {
+ return true;
+ }
+ };
+
+ protected void selectAllExecuted() {
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java
new file mode 100644
index 0000000..28de0b6
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.check;
+
+import java.text.MessageFormat;
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks in code. The predicate
+ * methods all test a condition and throw some type of unchecked exception if the condition does not
+ * hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are thrown when something is
+ * misbehaving. Assertion failures are invariably unspecified behavior; consequently, clients should
+ * never rely on these being thrown (and certainly should not being catching them specifically).
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public final class Assert {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private Assert() {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // "legal"
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that an argument is legal. If the given boolean is not <code>true</code>, an
+ * <code>IllegalArgumentException</code> is thrown.
+ *
+ * @param expression
+ * the boolean expression of the check
+ * @return <code>true</code> if the check passes (does not return if the check fails)
+ * @exception IllegalArgumentException
+ * if the legality test failed
+ */
+ public static boolean isLegal(boolean expression) {
+ return isLegal(expression, ""); //$NON-NLS-1$
+ }
+
+ /**
+ * Asserts that an argument is legal. If the given boolean is not <code>true</code>, an
+ * <code>IllegalArgumentException</code> is thrown. The given message is included in that
+ * exception, to aid debugging.
+ *
+ * @param expression
+ * the boolean expression of the check
+ * @param message
+ * the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return if the check fails)
+ * @exception IllegalArgumentException
+ * if the legality test failed
+ */
+ public static boolean isLegal(boolean expression, String message) {
+ if (!expression) {
+ throw new IllegalArgumentException(message);
+ }
+ return expression;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // "null"
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that the given object is <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown.
+ *
+ * @param object
+ * the value to test
+ */
+ public static void isNull(Object object) {
+ isNull(object, ""); //$NON-NLS-1$
+ }
+
+ /**
+ * Asserts that the given object is <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param object
+ * the value to test
+ * @param message
+ * the message to include in the exception
+ */
+ public static void isNull(Object object, String message) {
+ if (object != null) {
+ throw new AssertionFailedException("null argument expected: " + message); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param object
+ * the value to test
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link String#format(String, Object...)}. For example
+ * <code>"Execution flow problem. %s expected, but %s found."</code>.
+ * @param args
+ * the arguments for {@code errorFormat}
+ */
+ public static void isNull(Object object, String errorFormat, Object... args) {
+ if (object != null) {
+ fail("null argument expected: " + String.format(errorFormat, args)); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @param errorFormat
+ * the format of error message suitable for {@link MessageFormat}.
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link MessageFormat}. For example
+ * <code>"Execution flow problem. {0} expected, but {1} found."</code>.
+ */
+ public static void isNull2(Object object, String errorFormat, Object... args) {
+ if (object != null) {
+ String message = "null argument expected: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$
+ fail(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // not "null"
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown.
+ *
+ * @param object
+ * the value to test
+ */
+ public static void isNotNull(Object object) {
+ isNotNull(object, ""); //$NON-NLS-1$
+ }
+
+ /**
+ * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param object
+ * the value to test
+ * @param message
+ * the message to include in the exception
+ */
+ public static void isNotNull(Object object, String message) {
+ if (object == null) {
+ fail("null argument: " + message); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param object
+ * the value to test
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link String#format(String, Object...)}. For example
+ * <code>"Execution flow problem. %s expected, but %s found."</code>.
+ * @param args
+ * the arguments for {@code errorFormat}
+ */
+ public static void isNotNull(Object object, String errorFormat, Object... args) {
+ if (object == null) {
+ fail("null argument: " + String.format(errorFormat, args)); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @param errorFormat
+ * the format of error message suitable for {@link MessageFormat}.
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link MessageFormat}. For example
+ * <code>"Execution flow problem. {0} expected, but {1} found."</code>.
+ */
+ public static void isNotNull2(Object object, String errorFormat, Object... args) {
+ if (object == null) {
+ String message = "null argument: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$
+ fail(message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Fail
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Fails with given message.
+ *
+ * @param message
+ * the message to include in the exception
+ */
+ public static void fail(String message) {
+ throw new AssertionFailedException(message);
+ }
+
+ /**
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link MessageFormat}. For example <code>"{0} expected, but {1} found."</code>.
+ */
+ public static void fail(String errorFormat, Object... args) {
+ String message = MessageFormat.format(errorFormat, args);
+ throw new AssertionFailedException(message);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // "true"
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of
+ * unchecked exception is thrown.
+ *
+ * @param expression
+ * the boolean expression of the check
+ * @return <code>true</code> if the check passes (does not return if the check fails)
+ */
+ public static boolean isTrue(boolean expression) {
+ return isTrue(expression, ""); //$NON-NLS-1$
+ }
+
+ /**
+ * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param expression
+ * the boolean expression of the check
+ * @param message
+ * the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return if the check fails)
+ */
+ public static boolean isTrue(boolean expression, String message) {
+ if (!expression) {
+ fail("assertion failed: " + message); //$NON-NLS-1$
+ }
+ return expression;
+ }
+
+ /**
+ * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param expression
+ * the boolean expression of the check
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link String#format(String, Object...)}. For example
+ * <code>"Execution flow problem. %s expected, but %s found."</code>.
+ * @param args
+ * the arguments for {@code errorFormat}
+ * @return <code>true</code> if the check passes (does not return if the check fails)
+ */
+ public static boolean isTrue(boolean expression, String errorFormat, Object... args) {
+ if (!expression) {
+ fail("assertion failed: " + String.format(errorFormat, args)); //$NON-NLS-1$
+ }
+ return expression;
+ }
+
+ /**
+ * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param expression
+ * the boolean expression to check.
+ * @param errorFormat
+ * the format of error message to produce if the check fails, as expected by
+ * {@link MessageFormat}. For example <code>"{0} expected, but {1} found."</code>.
+ */
+ public static boolean isTrue2(boolean expression, String errorFormat, Object... args) {
+ if (!expression) {
+ fail(errorFormat, args);
+ }
+ return expression;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // equals
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that given actual value equals expected value. If this is not the case, some kind of
+ * unchecked exception is thrown.
+ *
+ * @param expected
+ * the expected value
+ * @param the
+ * actual value to check
+ */
+ public static void equals(int expected, int actual) {
+ equals(expected, actual, expected + " expected, but " + actual + " found");
+ }
+
+ /**
+ * Asserts that given actual value equals expected value. If this is not the case, some kind of
+ * unchecked exception is thrown. The given message is included in that exception, to aid
+ * debugging.
+ *
+ * @param expected
+ * the expected value
+ * @param the
+ * actual value to check
+ * @param message
+ * the message to include in the exception
+ */
+ public static void equals(int expected, int actual, String message) {
+ if (expected != actual) {
+ fail("assertation failed: " + message);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // instanceOf
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Asserts that given object is not <code>null</code> and has class compatible with given.
+ */
+ public static void instanceOf(Class<?> expectedClass, Object o) {
+ if (o == null) {
+ fail(expectedClass.getName() + " expected, but 'null' found.");
+ }
+ if (!expectedClass.isAssignableFrom(o.getClass())) {
+ fail(expectedClass.getName() + " expected, but " + o.getClass().getName() + " found.");
+ }
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java
new file mode 100644
index 0000000..d0c9794
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.check;
+
+/**
+ * <code>AssertionFailedException</code> is a runtime exception thrown by some of the methods in
+ * <code>Assert</code>.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public final class AssertionFailedException extends RuntimeException {
+ private static final long serialVersionUID = 0L;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Constructs a new exception with the given message.
+ *
+ * @param detail
+ * the message
+ */
+ public AssertionFailedException(String detail) {
+ super(detail);
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java
new file mode 100644
index 0000000..d80ff94
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.execution;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.wb.internal.core.DesignerPlugin;
+import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
+
+import java.beans.Beans;
+
+/**
+ * Utilities for executing actions, such as {@link RunnableEx}.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public class ExecutionUtils {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private ExecutionUtils() {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Sleep
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sleeps given number of milliseconds, ignoring exceptions.
+ */
+ public static void sleep(final int millis) {
+ runIgnore(new RunnableEx() {
+ @Override
+ public void run() throws Exception {
+ Thread.sleep(millis);
+ }
+ });
+ }
+
+ /**
+ * Waits given number of milliseconds and runs events loop every 1 millisecond. At least one
+ * events loop will be executed. If current thread is not UI thread, then this method works just
+ * as {@link #sleep(int)}.
+ */
+ public static void waitEventLoop(int millis) {
+ Display display = Display.getCurrent();
+ if (display != null) {
+ long nanos = millis * 1000000L;
+ long start = System.nanoTime();
+ do {
+ sleep(0);
+ while (display.readAndDispatch()) {
+ // do nothing
+ }
+ } while (System.nanoTime() - start < nanos);
+ } else {
+ sleep(millis);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // void
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Runs given {@link RunnableEx} and ignores exceptions.
+ *
+ * @return <code>true</code> if execution was finished without exception.
+ */
+ public static boolean runIgnore(RunnableEx runnable) {
+ try {
+ runnable.run();
+ return true;
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}.
+ *
+ * @return <code>true</code> if execution was finished without exception.
+ */
+ public static boolean runLog(RunnableEx runnable) {
+ try {
+ runnable.run();
+ return true;
+ } catch (Throwable e) {
+ DesignerPlugin.log(e);
+ return false;
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}.
+ */
+ public static void runRethrow(RunnableEx runnable) {
+ try {
+ runnable.run();
+ } catch (Throwable e) {
+ throw ReflectionUtils.propagate(e);
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}.
+ */
+ public static void runRethrow(RunnableEx runnable, String format, Object... args) {
+ try {
+ runnable.run();
+ } catch (Throwable e) {
+ String message = String.format(format, args);
+ throw new RuntimeException(message, e);
+ }
+ }
+
+ /**
+ * Ensures that {@link Beans#isDesignTime()} returns <code>true</code> and runs given
+ * {@link RunnableEx}.
+ */
+ public static void runDesignTime(RunnableEx runnable) throws Exception {
+ boolean old_designTime = Beans.isDesignTime();
+ try {
+ Beans.setDesignTime(true);
+ runnable.run();
+ } finally {
+ Beans.setDesignTime(old_designTime);
+ }
+ }
+
+ /**
+ * Ensures that {@link Beans#isDesignTime()} returns <code>true</code> and runs given
+ * {@link RunnableEx}.
+ */
+ public static <T> T runDesignTime(RunnableObjectEx<T> runnable) throws Exception {
+ boolean old_designTime = Beans.isDesignTime();
+ try {
+ Beans.setDesignTime(true);
+ return runnable.runObject();
+ } finally {
+ Beans.setDesignTime(old_designTime);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // UI
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}.
+ *
+ * @return <code>true</code> if {@link RunnableEx} was executed without any {@link Exception}.
+ */
+ public static boolean runLogUI(final RunnableEx runnable) {
+ final boolean[] success = new boolean[1];
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ success[0] = ExecutionUtils.runLog(runnable);
+ }
+ });
+ return success[0];
+ }
+
+ /**
+ * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}.
+ */
+ public static void runRethrowUI(final RunnableEx runnable) {
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ ExecutionUtils.runRethrow(runnable);
+ }
+ });
+ }
+
+ /**
+ * Runs given {@link RunnableEx} within UI thread using {@link Display#asyncExec(Runnable)}. Logs
+ * a {@link Throwable} which may occur.
+ */
+ public static void runAsync(final RunnableEx runnable) {
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ ExecutionUtils.runLog(runnable);
+ }
+ });
+ }
+
+ /**
+ * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T runObjectUI(final RunnableObjectEx<T> runnable) {
+ final Object[] result = new Object[1];
+ runRethrowUI(new RunnableEx() {
+ @Override
+ public void run() throws Exception {
+ result[0] = runObject(runnable);
+ }
+ });
+ return (T) result[0];
+ }
+
+ /**
+ * Runs given {@link RunnableEx} as {@link #runLog(RunnableEx)}, but using
+ * {@link Display#asyncExec(Runnable)}.
+ */
+ public static void runLogLater(final RunnableEx runnable) {
+ Display.getDefault().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ ExecutionUtils.runLog(runnable);
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Object
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}.
+ *
+ * @return the {@link Object} returned by {@link RunnableEx#run()}.
+ */
+ public static <T> T runObject(RunnableObjectEx<T> runnable) {
+ try {
+ return runnable.runObject();
+ } catch (Throwable e) {
+ throw ReflectionUtils.propagate(e);
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}.
+ *
+ * @return the {@link Object} returned by {@link RunnableEx#run()}.
+ */
+ public static <T> T runObject(RunnableObjectEx<T> runnable, String format, Object... args) {
+ try {
+ return runnable.runObject();
+ } catch (Throwable e) {
+ String message = String.format(format, args);
+ throw new Error(message, e);
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and ignores exceptions.
+ *
+ * @return the {@link Object} returned by {@link RunnableEx#run()} or <code>defaultValue</code> if
+ * exception happened.
+ */
+ public static <T> T runObjectIgnore(RunnableObjectEx<T> runnable, T defaultValue) {
+ try {
+ return runnable.runObject();
+ } catch (Throwable e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}.
+ *
+ * @return the {@link Object} returned by {@link RunnableEx#run()} or <code>defaultValue</code> if
+ * exception was logged.
+ */
+ public static <T> T runObjectLog(RunnableObjectEx<T> runnable, T defaultValue) {
+ try {
+ return runnable.runObject();
+ } catch (Throwable e) {
+ DesignerPlugin.log(e);
+ return defaultValue;
+ }
+ }
+
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java
new file mode 100644
index 0000000..18c141f
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.execution;
+
+/**
+ * Analog of {@link Runnable} where method <code>run</code> can throw {@link Exception}.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public interface RunnableEx {
+ /**
+ * Executes operation that can cause {@link Exception}.
+ */
+ void run() throws Exception;
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java
new file mode 100644
index 0000000..02a2865
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.execution;
+
+/**
+ * Analog of {@link Runnable} where method <code>run</code> can throw {@link Exception}.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public interface RunnableObjectEx<T> {
+ /**
+ * Executes operation that can cause {@link Exception}.
+ *
+ * @return some {@link Object} result for caller.
+ */
+ T runObject() throws Exception;
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java
new file mode 100644
index 0000000..b74ba88
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.reflect;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Helper for setting properties for {@link ClassLoader}.
+ * <p>
+ * http://java.dzone.com/articles/classloaderlocal-how-avoid
+ *
+ * @author Jevgeni Kabanov
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+@SuppressWarnings("unchecked")
+public class ClassLoaderLocalMap implements Opcodes {
+ private static final String NAME = "GEN$$ClassLoaderProperties";
+ private static final Map<Object, Object> globalMap =
+ Collections.synchronizedMap(new WeakHashMap<Object, Object>());
+ private static Method defineMethod;
+ private static Method findLoadedClass;
+ static {
+ try {
+ defineMethod =
+ ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{
+ String.class,
+ byte[].class,
+ int.class,
+ int.class});
+ defineMethod.setAccessible(true);
+ findLoadedClass =
+ ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class});
+ findLoadedClass.setAccessible(true);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Map
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public static boolean containsKey(ClassLoader cl, Object key) {
+ if (cl == null) {
+ return globalMap.containsKey(key);
+ }
+ // synchronizing over ClassLoader is usually safest
+ synchronized (cl) {
+ if (!hasHolder(cl)) {
+ return false;
+ }
+ return getLocalMap(cl).containsKey(key);
+ }
+ }
+
+ public static void put(ClassLoader cl, Object key, Object value) {
+ if (cl == null) {
+ globalMap.put(key, value);
+ return;
+ }
+ // synchronizing over ClassLoader is usually safest
+ synchronized (cl) {
+ getLocalMap(cl).put(key, value);
+ }
+ }
+
+ public static Object get(ClassLoader cl, Object key) {
+ if (cl == null) {
+ return globalMap.get(key);
+ }
+ // synchronizing over ClassLoader is usually safest
+ synchronized (cl) {
+ return getLocalMap(cl).get(key);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Implementation
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static boolean hasHolder(ClassLoader cl) {
+ String propertiesClassName = NAME;
+ try {
+ Class<?> clazz = (Class<?>) findLoadedClass.invoke(cl, new Object[]{propertiesClassName});
+ if (clazz == null) {
+ return false;
+ }
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getTargetException());
+ }
+ return true;
+ }
+
+ private static Map<Object, Object> getLocalMap(ClassLoader cl) {
+ String holderClassName = NAME;
+ Class<?> holderClass;
+ try {
+ holderClass = (Class<?>) findLoadedClass.invoke(cl, new Object[]{holderClassName});
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getTargetException());
+ }
+ if (holderClass == null) {
+ byte[] classBytes = buildHolderByteCode(holderClassName);
+ try {
+ holderClass =
+ (Class<?>) defineMethod.invoke(
+ cl,
+ new Object[]{
+ holderClassName,
+ classBytes,
+ Integer.valueOf(0),
+ Integer.valueOf(classBytes.length)});
+ } catch (InvocationTargetException e1) {
+ throw new RuntimeException(e1.getTargetException());
+ } catch (Throwable e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ try {
+ return (Map<Object, Object>) holderClass.getDeclaredField("localMap").get(null);
+ } catch (Throwable e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+
+ private static byte[] buildHolderByteCode(String holderClassName) {
+ ClassWriter cw = new ClassWriter(0);
+ FieldVisitor fv;
+ MethodVisitor mv;
+ cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, holderClassName, null, "java/lang/Object", null);
+ {
+ fv =
+ cw.visitField(
+ ACC_PUBLIC + ACC_FINAL + ACC_STATIC,
+ "localMap",
+ "Ljava/util/Map;",
+ null,
+ null);
+ fv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+ mv.visitCode();
+ mv.visitTypeInsn(NEW, "java/util/WeakHashMap");
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/util/WeakHashMap", "<init>", "()V");
+ mv.visitFieldInsn(PUTSTATIC, holderClassName, "localMap", "Ljava/util/Map;");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 0);
+ mv.visitEnd();
+ }
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+ return cw.toByteArray();
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java
new file mode 100644
index 0000000..ce0d339
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.reflect;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link Map}-like interface for mapping {@link Class} to value.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public final class ClassMap<V> {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Creates new instance of {@link ClassMap}.
+ */
+ public static <V> ClassMap<V> create() {
+ return new ClassMap<V>();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Map
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public void put(Class<?> key, V value) {
+ getMap(key).put(key, value);
+ }
+
+ public V get(Class<?> key) {
+ return getMap(key).get(key);
+ }
+
+ public void remove(Class<?> key) {
+ getMap(key).remove(key);
+ }
+
+ public void clear(ClassLoader classLoader) {
+ getMap(classLoader).clear();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Implementation
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private Map<Class<?>, V> getMap(Class<?> key) {
+ ClassLoader classLoader = key.getClassLoader();
+ return getMap(classLoader);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<Class<?>, V> getMap(ClassLoader classLoader) {
+ Object map = ClassLoaderLocalMap.get(classLoader, this);
+ if (map == null) {
+ map = new HashMap<Class<?>, V>();
+ ClassLoaderLocalMap.put(classLoader, this, map);
+ }
+ return (Map<Class<?>, V>) map;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java
new file mode 100644
index 0000000..b362211
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java
@@ -0,0 +1,327 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.reflect;
+
+import com.google.common.collect.Maps;
+
+import org.eclipse.wb.internal.core.utils.check.Assert;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Map;
+
+/**
+ * Contains different Java reflection utilities.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public class ReflectionUtils {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private ReflectionUtils() {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Signature
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @param runtime
+ * is <code>true</code> if we need name for class loading, <code>false</code> if we need
+ * name for source generation.
+ *
+ * @return the fully qualified name of given {@link Type}.
+ */
+ public static String getFullyQualifiedName(Type type, boolean runtime) {
+ Assert.isNotNull(type);
+ // Class
+ if (type instanceof Class<?>) {
+ Class<?> clazz = (Class<?>) type;
+ // array
+ if (clazz.isArray()) {
+ return getFullyQualifiedName(clazz.getComponentType(), runtime) + "[]";
+ }
+ // object
+ String name = clazz.getName();
+ if (!runtime) {
+ name = name.replace('$', '.');
+ }
+ return name;
+ }
+ // GenericArrayType
+ if (type instanceof GenericArrayType) {
+ GenericArrayType genericArrayType = (GenericArrayType) type;
+ return getFullyQualifiedName(genericArrayType.getGenericComponentType(), runtime) + "[]";
+ }
+ // ParameterizedType
+ if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ Type rawType = parameterizedType.getRawType();
+ // raw type
+ StringBuilder sb = new StringBuilder();
+ sb.append(getFullyQualifiedName(rawType, runtime));
+ // type arguments
+ sb.append("<");
+ boolean firstTypeArgument = true;
+ for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
+ if (!firstTypeArgument) {
+ sb.append(",");
+ }
+ firstTypeArgument = false;
+ sb.append(getFullyQualifiedName(typeArgument, runtime));
+ }
+ sb.append(">");
+ // done
+ return sb.toString();
+ }
+ // WildcardType
+ if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ return "? extends " + getFullyQualifiedName(wildcardType.getUpperBounds()[0], runtime);
+ }
+ // TypeVariable
+ TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+ return typeVariable.getName();
+ }
+
+ /**
+ * Appends fully qualified names of given parameter types (appends also <code>"()"</code>).
+ */
+ private static void appendParameterTypes(StringBuilder buffer, Type[] parameterTypes) {
+ buffer.append('(');
+ boolean firstParameter = true;
+ for (Type parameterType : parameterTypes) {
+ if (firstParameter) {
+ firstParameter = false;
+ } else {
+ buffer.append(',');
+ }
+ buffer.append(getFullyQualifiedName(parameterType, false));
+ }
+ buffer.append(')');
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Method
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return all declared {@link Method}'s, including protected and private.
+ */
+ public static Map<String, Method> getMethods(Class<?> clazz) {
+ Map<String, Method> methods = Maps.newHashMap();
+ // process classes
+ for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
+ for (Method method : c.getDeclaredMethods()) {
+ String signature = getMethodSignature(method);
+ if (!methods.containsKey(signature)) {
+ method.setAccessible(true);
+ methods.put(signature, method);
+ }
+ }
+ }
+ // process interfaces
+ for (Class<?> interfaceClass : clazz.getInterfaces()) {
+ for (Method method : interfaceClass.getDeclaredMethods()) {
+ String signature = getMethodSignature(method);
+ if (!methods.containsKey(signature)) {
+ method.setAccessible(true);
+ methods.put(signature, method);
+ }
+ }
+ }
+ // done
+ return methods;
+ }
+
+ /**
+ * @return signature for given {@link Method}. This signature is not same signature as in JVM or
+ * JDT, just some string that unique identifies method in its {@link Class}.
+ */
+ public static String getMethodSignature(Method method) {
+ Assert.isNotNull(method);
+ return getMethodSignature(method.getName(), method.getParameterTypes());
+ }
+
+ /**
+ * Returns the signature of {@link Method} with given combination of name and parameter types.
+ * This signature is not same signature as in JVM or JDT, just some string that unique identifies
+ * method in its {@link Class}.
+ *
+ * @param name
+ * the name of {@link Method}.
+ * @param parameterTypes
+ * the types of {@link Method} parameters.
+ *
+ * @return signature of {@link Method}.
+ */
+ public static String getMethodSignature(String name, Type... parameterTypes) {
+ Assert.isNotNull(name);
+ Assert.isNotNull(parameterTypes);
+ //
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(name);
+ appendParameterTypes(buffer, parameterTypes);
+ return buffer.toString();
+ }
+
+ private static final ClassMap<Map<String, Method>> m_getMethodBySignature = ClassMap.create();
+
+ /**
+ * Returns the {@link Method} defined in {@link Class}. This method can have any visibility, i.e.
+ * we can find even protected/private methods. Can return <code>null</code> if no method with
+ * given signature found.
+ *
+ * @param clazz
+ * the {@link Class} to get method from it, or its superclass.
+ * @param signature
+ * the signature of method in same format as {@link #getMethodSignature(Method)}.
+ *
+ * @return the {@link Method} for given signature, or <code>null</code> if no such method found.
+ */
+ public static Method getMethodBySignature(Class<?> clazz, String signature) {
+ Assert.isNotNull(clazz);
+ Assert.isNotNull(signature);
+ // prepare cache
+ Map<String, Method> cache = m_getMethodBySignature.get(clazz);
+ if (cache == null) {
+ cache = getMethods(clazz);
+ m_getMethodBySignature.put(clazz, cache);
+ }
+ // use cache
+ return cache.get(signature);
+ }
+
+ /**
+ * @return the {@link Object} result of invoking method with given signature.
+ */
+ public static Object invokeMethod(Object object, String signature, Object... arguments)
+ throws Exception {
+ Assert.isNotNull(object);
+ Assert.isNotNull(arguments);
+ // prepare class/object
+ Class<?> refClass = getRefClass(object);
+ Object refObject = getRefObject(object);
+ // prepare method
+ Method method = getMethodBySignature(refClass, signature);
+ Assert.isNotNull(method, "Can not find method " + signature + " in " + refClass);
+ // do invoke
+ try {
+ return method.invoke(refObject, arguments);
+ } catch (InvocationTargetException e) {
+ throw propagate(e.getCause());
+ }
+ }
+
+ /**
+ * Invokes method by name and parameter types.
+ *
+ * @param object
+ * the object to call, may be {@link Class} for invoking static method.
+ * @param name
+ * the name of method.
+ * @param parameterTypes
+ * the types of parameters.
+ * @param arguments
+ * the values of argument for invocation.
+ *
+ * @return the {@link Object} result of invoking method.
+ */
+ public static Object invokeMethod2(Object object,
+ String name,
+ Class<?>[] parameterTypes,
+ Object[] arguments) throws Exception {
+ Assert.equals(parameterTypes.length, arguments.length);
+ String signature = getMethodSignature(name, parameterTypes);
+ return invokeMethod(object, signature, arguments);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Utils
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return the {@link Class} of given {@link Object} or casted object, if it is {@link Class}
+ * itself.
+ */
+ private static Class<?> getRefClass(Object object) {
+ return object instanceof Class<?> ? (Class<?>) object : object.getClass();
+ }
+
+ /**
+ * @return the {@link Object} that should be used as argument for {@link Field#get(Object)} and
+ * {@link Method#invoke(Object, Object[])}.
+ */
+ private static Object getRefObject(Object object) {
+ return object instanceof Class<?> ? null : object;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Throwable propagation
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Helper class used in {@link #propagate(Throwable)}.
+ */
+ private static class ExceptionThrower {
+ private static Throwable throwable;
+
+ private ExceptionThrower() throws Throwable {
+ if (System.getProperty("wbp.ReflectionUtils.propagate().InstantiationException") != null) {
+ throw new InstantiationException();
+ }
+ if (System.getProperty("wbp.ReflectionUtils.propagate().IllegalAccessException") != null) {
+ throw new IllegalAccessException();
+ }
+ throw throwable;
+ }
+
+ public static synchronized void spit(Throwable t) {
+ if (System.getProperty("wbp.ReflectionUtils.propagate().dontThrow") == null) {
+ ExceptionThrower.throwable = t;
+ try {
+ ExceptionThrower.class.newInstance();
+ } catch (InstantiationException e) {
+ } catch (IllegalAccessException e) {
+ } finally {
+ ExceptionThrower.throwable = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Propagates {@code throwable} as-is without any wrapping. This is trick.
+ *
+ * @return nothing will ever be returned; this return type is only for your convenience, to use
+ * this method in "throw" statement.
+ */
+ public static RuntimeException propagate(Throwable throwable) {
+ if (System.getProperty("wbp.ReflectionUtils.propagate().forceReturn") == null) {
+ ExceptionThrower.spit(throwable);
+ }
+ return null;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java
new file mode 100644
index 0000000..f7cc09d
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import com.google.common.io.Closeables;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.wb.draw2d.IColorConstants;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Utilities for drawing on {@link GC}.
+ *
+ * @author scheglov_ke
+ * @coverage core.ui
+ */
+public class DrawUtils {
+ private static final String DOTS = "...";
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Drawing
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Draws given text clipped horizontally and centered vertically.
+ */
+ public static final void drawStringCV(GC gc, String text, int x, int y, int width, int height) {
+ Rectangle oldClipping = gc.getClipping();
+ try {
+ gc.setClipping(new Rectangle(x, y, width, height));
+ //
+ int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2;
+ gc.drawString(clipString(gc, text, width), x, textStartY, true);
+ } finally {
+ gc.setClipping(oldClipping);
+ }
+ }
+
+ /**
+ * Draws given text clipped or centered horizontally and centered vertically.
+ */
+ public static final void drawStringCHCV(GC gc, String text, int x, int y, int width, int height) {
+ int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2;
+ Point textSize = gc.stringExtent(text);
+ //
+ if (textSize.x > width) {
+ gc.drawString(clipString(gc, text, width), x, textStartY);
+ } else {
+ gc.drawString(text, x + (width - textSize.x) / 2, textStartY);
+ }
+ }
+
+ /**
+ * Draws image at given <code>x</code> and centered vertically.
+ */
+ public static final void drawImageCV(GC gc, Image image, int x, int y, int height) {
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds();
+ gc.drawImage(image, x, y + (height - imageBounds.height) / 2);
+ }
+ }
+
+ /**
+ * Draws image at given <code>x</code> and centered vertically.
+ */
+ public static final void drawImageCHCV(GC gc, Image image, int x, int y, int width, int height) {
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds();
+ int centerX = (width - imageBounds.width) / 2;
+ int centerY = y + (height - imageBounds.height) / 2;
+ gc.drawImage(image, x + centerX, centerY);
+ }
+ }
+
+ /**
+ * Draws {@link Image} on {@link GC} centered in given {@link Rectangle}. If {@link Image} is
+ * bigger that {@link Rectangle}, {@link Image} will be scaled down as needed with keeping
+ * proportions.
+ */
+ public static void drawScaledImage(GC gc, Image image, Rectangle targetRectangle) {
+ int imageWidth = image.getBounds().width;
+ int imageHeight = image.getBounds().height;
+ // prepare scaled image size
+ int newImageWidth;
+ int newImageHeight;
+ if (imageWidth <= targetRectangle.width && imageHeight <= targetRectangle.height) {
+ newImageWidth = imageWidth;
+ newImageHeight = imageHeight;
+ } else {
+ // prepare minimal scale
+ double k;
+ {
+ double k_w = targetRectangle.width / (double) imageWidth;
+ double k_h = targetRectangle.height / (double) imageHeight;
+ k = Math.min(k_w, k_h);
+ }
+ // calculate scaled image size
+ newImageWidth = (int) (imageWidth * k);
+ newImageHeight = (int) (imageHeight * k);
+ }
+ // draw image centered in target rectangle
+ int destX = targetRectangle.x + (targetRectangle.width - newImageWidth) / 2;
+ int destY = targetRectangle.y + (targetRectangle.height - newImageHeight) / 2;
+ gc.drawImage(image, 0, 0, imageWidth, imageHeight, destX, destY, newImageWidth, newImageHeight);
+ }
+
+ /**
+ * @return the string clipped to have width less than given. Clipping is done as trailing "...".
+ */
+ public static String clipString(GC gc, String text, int width) {
+ if (width <= 0) {
+ return "";
+ }
+ // check if text already fits in given width
+ if (gc.stringExtent(text).x <= width) {
+ return text;
+ }
+ // use average count of characters as base
+ int count = Math.min(width / gc.getFontMetrics().getAverageCharWidth(), text.length());
+ if (gc.stringExtent(text.substring(0, count) + DOTS).x > width) {
+ while (count > 0 && gc.stringExtent(text.substring(0, count) + DOTS).x > width) {
+ count--;
+ }
+ } else {
+ while (count < text.length() - 1
+ && gc.stringExtent(text.substring(0, count + 1) + DOTS).x < width) {
+ count++;
+ }
+ }
+ return text.substring(0, count) + DOTS;
+ }
+
+ /**
+ * Draws {@link String} in rectangle, wraps at any character (not by words).
+ */
+ public static void drawTextWrap(GC gc, String text, int x, int y, int width, int height) {
+ int y_ = y;
+ int x_ = x;
+ int lineHeight = 0;
+ for (int i = 0; i < text.length(); i++) {
+ String c = text.substring(i, i + 1);
+ Point extent = gc.stringExtent(c);
+ if (x_ + extent.x > x + width) {
+ y_ += lineHeight;
+ if (y_ > y + height) {
+ return;
+ }
+ x_ = x;
+ }
+ gc.drawText(c, x_, y_);
+ x_ += extent.x;
+ lineHeight = Math.max(lineHeight, extent.y);
+ }
+ }
+
+ /**
+ * Draws 3D highlight rectangle.
+ */
+ public static void drawHighlightRectangle(GC gc, int x, int y, int width, int height) {
+ int right = x + width - 1;
+ int bottom = y + height - 1;
+ //
+ Color oldForeground = gc.getForeground();
+ try {
+ gc.setForeground(IColorConstants.buttonLightest);
+ gc.drawLine(x, y, right, y);
+ gc.drawLine(x, y, x, bottom);
+ //
+ gc.setForeground(IColorConstants.buttonDarker);
+ gc.drawLine(right, y, right, bottom);
+ gc.drawLine(x, bottom, right, bottom);
+ } finally {
+ gc.setForeground(oldForeground);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Images
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return the {@link Image} loaded relative to given {@link Class}.
+ */
+ public static Image loadImage(Class<?> clazz, String path) {
+ try {
+ URL resource = clazz.getResource(path);
+ if (resource != null) {
+ InputStream stream = resource.openStream();
+ try {
+ return new Image(null, stream);
+ } finally {
+ Closeables.closeQuietly(stream);
+ }
+ }
+ } catch (Throwable e) {
+ }
+ return null;
+ }
+
+ /**
+ * @return the thumbnail {@link Image} of required size for given "big" {@link Image}, centered or
+ * scaled down.
+ */
+ public static Image getThubmnail(Image image,
+ int minWidth,
+ int minHeight,
+ int maxWidth,
+ int maxHeight) {
+ Rectangle imageBounds = image.getBounds();
+ int imageWidth = imageBounds.width;
+ int imageHeight = imageBounds.height;
+ if (imageWidth < minWidth && imageHeight < minHeight) {
+ // create "thumbnail" Image with required size
+ Image thumbnail = new Image(null, minWidth, minHeight);
+ GC gc = new GC(thumbnail);
+ try {
+ drawImageCHCV(gc, image, 0, 0, minWidth, minHeight);
+ } finally {
+ gc.dispose();
+ }
+ // recreate "thumbnail" Image with transparent pixel
+ try {
+ ImageData thumbnailData = thumbnail.getImageData();
+ thumbnailData.transparentPixel = thumbnailData.getPixel(0, 0);
+ return new Image(null, thumbnailData);
+ } finally {
+ thumbnail.dispose();
+ }
+ } else if (imageWidth <= maxWidth && imageHeight <= maxHeight) {
+ return new Image(null, image, SWT.IMAGE_COPY);
+ } else {
+ double kX = (double) maxWidth / imageWidth;
+ double kY = (double) maxHeight / imageHeight;
+ double k = Math.max(kX, kY);
+ int dWidth = (int) (imageWidth * k);
+ int dHeight = (int) (imageHeight * k);
+ ImageData scaledImageData = image.getImageData().scaledTo(dWidth, dHeight);
+ return new Image(null, scaledImageData);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Colors
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return new {@link Color} based on given {@link Color} and shifted on given value to make it
+ * darker or lighter.
+ */
+ public static Color getShiftedColor(Color color, int delta) {
+ int r = Math.max(0, Math.min(color.getRed() + delta, 255));
+ int g = Math.max(0, Math.min(color.getGreen() + delta, 255));
+ int b = Math.max(0, Math.min(color.getBlue() + delta, 255));
+ return new Color(color.getDevice(), r, g, b);
+ }
+
+ /**
+ * @return <code>true</code> if the given <code>color</code> is dark.
+ */
+ public static boolean isDarkColor(Color c) {
+ int value =
+ (int) Math.sqrt(c.getRed()
+ * c.getRed()
+ * .241
+ + c.getGreen()
+ * c.getGreen()
+ * .691
+ + c.getBlue()
+ * c.getBlue()
+ * .068);
+ return value < 130;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Fonts
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return the bold version of given {@link Font}.
+ */
+ public static Font getBoldFont(Font baseFont) {
+ FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD);
+ return new Font(Display.getCurrent(), boldData);
+ }
+
+ /**
+ * @return the italic version of given {@link Font}.
+ */
+ public static Font getBoldItalicFont(Font baseFont) {
+ FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD | SWT.ITALIC);
+ return new Font(Display.getCurrent(), boldData);
+ }
+
+ /**
+ * @return the italic version of given {@link Font}.
+ */
+ public static Font getItalicFont(Font baseFont) {
+ FontData[] boldData = getModifiedFontData(baseFont, SWT.ITALIC);
+ return new Font(Display.getCurrent(), boldData);
+ }
+
+ /**
+ * @return the array of {@link FontData} with the specified style.
+ */
+ private static FontData[] getModifiedFontData(Font baseFont, int style) {
+ FontData[] baseData = baseFont.getFontData();
+ FontData[] styleData = new FontData[baseData.length];
+ for (int i = 0; i < styleData.length; i++) {
+ FontData base = baseData[i];
+ styleData[i] = new FontData(base.getName(), base.getHeight(), base.getStyle() | style);
+ }
+ return styleData;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java
new file mode 100644
index 0000000..4ccda40
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java
@@ -0,0 +1,541 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2011 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * This class provides a convienient shorthand for creating and initializing GridData. This offers
+ * several benefits over creating GridData normal way:
+ *
+ * <ul>
+ * <li>The same factory can be used many times to create several GridData instances</li>
+ * <li>The setters on GridDataFactory all return "this", allowing them to be chained</li>
+ * <li>GridDataFactory uses vector setters (it accepts Points), making it easy to set X and Y values
+ * together</li>
+ * </ul>
+ *
+ * <p>
+ * GridDataFactory instances are created using one of the static methods on this class.
+ * </p>
+ *
+ * <p>
+ * Example usage:
+ * </p>
+ * <code>
+ *
+ * ////////////////////////////////////////////////////////////
+ * // Example 1: Typical grid data for a non-wrapping label
+ *
+ * // GridDataFactory version
+ * GridDataFactory.fillDefaults().applyTo(myLabel);
+ *
+ * // Equivalent SWT version
+ * GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
+ * myLabel.setLayoutData(labelData);
+ *
+ * ///////////////////////////////////////////////////////////
+ * // Example 2: Typical grid data for a wrapping label
+ *
+ * // GridDataFactory version
+ * GridDataFactory.fillDefaults()
+ * .align(SWT.FILL, SWT.CENTER)
+ * .hint(150, SWT.DEFAULT)
+ * .grab(true, false)
+ * .applyTo(wrappingLabel);
+ *
+ * // Equivalent SWT version
+ * GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER);
+ * wrappingLabelData.minimumWidth = 1;
+ * wrappingLabelData.widthHint = 150;
+ * wrappingLabel.setLayoutData(wrappingLabelData);
+ *
+ * //////////////////////////////////////////////////////////////
+ * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.)
+ *
+ * // GridDataFactory version
+ * GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox);
+ *
+ * // Equivalent SWT version
+ * GridData listBoxData = new GridData(GridData.FILL_BOTH);
+ * listBoxData.widthHint = 150;
+ * listBoxData.heightHint = 150;
+ * listBoxData.minimumWidth = 1;
+ * listBoxData.minimumHeight = 1;
+ * listBox.setLayoutData(listBoxData);
+ *
+ * /////////////////////////////////////////////////////////////
+ * // Example 4: Typical grid data for a button
+ *
+ * // GridDataFactory version
+ * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
+ * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ * GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(hint).applyTo(button);
+ *
+ * // Equivalent SWT version
+ * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
+ * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize);
+ * GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER);
+ * buttonData.widthHint = hint.x;
+ * buttonData.heightHint = hint.y;
+ * button.setLayoutData(buttonData);
+ * </code>
+ *
+ * <p>
+ * IMPORTANT: WHEN ASSIGNING LAYOUT DATA TO A CONTROL, BE SURE TO USE
+ * gridDataFactory.applyTo(control) AND NEVER control.setLayoutData(gridDataFactory).
+ * </p>
+ *
+ * @since 3.2
+ */
+public final class GridDataFactory {
+ private final Control m_control;
+ private final PixelConverter m_pixelConverter;
+ private final GridData m_data;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private GridDataFactory(Control control, GridData gridData) {
+ m_control = control;
+ m_pixelConverter = new PixelConverter(m_control);
+ //
+ m_data = gridData;
+ if (m_control.getLayoutData() != m_data) {
+ m_control.setLayoutData(m_data);
+ }
+ }
+
+ /**
+ * Creates new {@link GridDataFactory} with new {@link GridData}.
+ */
+ public static GridDataFactory create(Control control) {
+ return new GridDataFactory(control, new GridData());
+ }
+
+ /**
+ * Creates new {@link GridDataFactory} for modifying {@link GridData} already installed in
+ * control.
+ */
+ public static GridDataFactory modify(Control control) {
+ GridData gridData;
+ {
+ Object existingLayoutData = control.getLayoutData();
+ if (existingLayoutData instanceof GridData) {
+ gridData = (GridData) existingLayoutData;
+ } else {
+ gridData = new GridData();
+ }
+ }
+ return new GridDataFactory(control, gridData);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Span
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the GridData span. The span controls how many cells are filled by the control.
+ *
+ * @param hSpan
+ * number of columns spanned by the control
+ * @param vSpan
+ * number of rows spanned by the control
+ * @return this
+ */
+ public GridDataFactory span(int hSpan, int vSpan) {
+ m_data.horizontalSpan = hSpan;
+ m_data.verticalSpan = vSpan;
+ return this;
+ }
+
+ /**
+ * Sets the GridData span. The span controls how many cells are filled by the control.
+ *
+ * @param hSpan
+ * number of columns spanned by the control
+ * @return this
+ */
+ public GridDataFactory spanH(int hSpan) {
+ m_data.horizontalSpan = hSpan;
+ return this;
+ }
+
+ /**
+ * Sets the GridData span. The span controls how many cells are filled by the control.
+ *
+ * @param vSpan
+ * number of rows spanned by the control
+ * @return this
+ */
+ public GridDataFactory spanV(int vSpan) {
+ m_data.verticalSpan = vSpan;
+ return this;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Hint
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the width and height hints. The width and height hints override the control's preferred
+ * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used.
+ *
+ * @param xHint
+ * horizontal hint (pixels), or SWT.DEFAULT to use the control's preferred size
+ * @param yHint
+ * vertical hint (pixels), or SWT.DEFAULT to use the control's preferred size
+ * @return this
+ */
+ public GridDataFactory hint(int xHint, int yHint) {
+ m_data.widthHint = xHint;
+ m_data.heightHint = yHint;
+ return this;
+ }
+
+ /**
+ * Sets hint in chars.
+ */
+ public GridDataFactory hintC(int xHintInChars, int yHintInChars) {
+ hintHC(xHintInChars);
+ hintVC(yHintInChars);
+ return this;
+ }
+
+ /**
+ * Sets the width hint.
+ *
+ * @return this
+ */
+ public GridDataFactory hintH(int xHint) {
+ m_data.widthHint = xHint;
+ return this;
+ }
+
+ /**
+ * Sets the width hint to the minimum of current hint and given <code>otherHint</code>.
+ *
+ * @return this
+ */
+ public GridDataFactory hintHMin(int otherHint) {
+ m_data.widthHint = Math.min(m_data.widthHint, otherHint);
+ return this;
+ }
+
+ /**
+ * Sets the width hint in chars.
+ *
+ * @return this
+ */
+ public GridDataFactory hintHC(int hintInChars) {
+ return hintH(m_pixelConverter.convertWidthInCharsToPixels(hintInChars));
+ }
+
+ /**
+ * Sets the width hint.
+ *
+ * @return this
+ */
+ public GridDataFactory hintHU(int hintInDLU) {
+ return hintH(m_pixelConverter.convertHorizontalDLUsToPixels(hintInDLU));
+ }
+
+ /**
+ * Sets the height hint.
+ *
+ * @return this
+ */
+ public GridDataFactory hintV(int yHint) {
+ m_data.heightHint = yHint;
+ return this;
+ }
+
+ /**
+ * Sets the height hint in chars.
+ *
+ * @return this
+ */
+ public GridDataFactory hintVC(int hintInChars) {
+ return hintV(m_pixelConverter.convertHeightInCharsToPixels(hintInChars));
+ }
+
+ /**
+ * Increments horizontal hint on given value.
+ *
+ * @return this
+ */
+ public GridDataFactory hintHAdd(int increment) {
+ return hintV(m_data.widthHint + increment);
+ }
+
+ /**
+ * Increments vertical hint on given value.
+ *
+ * @return this
+ */
+ public GridDataFactory hintVAdd(int increment) {
+ return hintV(m_data.heightHint + increment);
+ }
+
+ /**
+ * Sets the width and height hints. The width and height hints override the control's preferred
+ * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used.
+ *
+ * @param hint
+ * size (pixels) to be used instead of the control's preferred size. If the x or y values
+ * are set to SWT.DEFAULT, the control's computeSize() method will be used to obtain that
+ * dimension of the preferred size.
+ * @return this
+ */
+ public GridDataFactory hint(Point hint) {
+ m_data.widthHint = hint.x;
+ m_data.heightHint = hint.y;
+ return this;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Minimum size
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public GridDataFactory minH(int minimumWidth) {
+ m_data.minimumWidth = minimumWidth;
+ return this;
+ }
+
+ public GridDataFactory minHC(int widthInChars) {
+ return minH(m_pixelConverter.convertWidthInCharsToPixels(widthInChars));
+ }
+
+ public GridDataFactory minV(int minimumHeight) {
+ m_data.minimumHeight = minimumHeight;
+ return this;
+ }
+
+ public GridDataFactory minVC(int heightInChars) {
+ return minV(m_pixelConverter.convertHeightInCharsToPixels(heightInChars));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Alignment
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the alignment of the control within its cell.
+ *
+ * @param hAlign
+ * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+ * @param vAlign
+ * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+ * @return this
+ */
+ public GridDataFactory align(int hAlign, int vAlign) {
+ m_data.horizontalAlignment = hAlign;
+ m_data.verticalAlignment = vAlign;
+ return this;
+ }
+
+ /**
+ * Sets the horizontal and vertical alignment to GridData.FILL.
+ */
+ public GridDataFactory fill() {
+ return align(GridData.FILL, GridData.FILL);
+ }
+
+ /**
+ * Sets the horizontal alignment of the control within its cell.
+ *
+ * @param hAlign
+ * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+ * @return this
+ */
+ public GridDataFactory alignH(int hAlign) {
+ m_data.horizontalAlignment = hAlign;
+ return this;
+ }
+
+ /**
+ * Sets the horizontal alignment of the control to GridData.BEGINNING
+ *
+ * @return this
+ */
+ public GridDataFactory alignHL() {
+ return alignH(GridData.BEGINNING);
+ }
+
+ /**
+ * Sets the horizontal alignment of the control to GridData.CENTER
+ *
+ * @return this
+ */
+ public GridDataFactory alignHC() {
+ return alignH(GridData.CENTER);
+ }
+
+ /**
+ * Sets the horizontal alignment of the control to GridData.FILL
+ *
+ * @return this
+ */
+ public GridDataFactory alignHF() {
+ return alignH(GridData.FILL);
+ }
+
+ /**
+ * Sets the horizontal alignment of the control to GridData.FILL
+ *
+ * @return this
+ */
+ public GridDataFactory fillH() {
+ return alignHF();
+ }
+
+ /**
+ * Sets the horizontal alignment of the control to GridData.END
+ *
+ * @return this
+ */
+ public GridDataFactory alignHR() {
+ return alignH(GridData.END);
+ }
+
+ /**
+ * Sets the vertical alignment of the control within its cell.
+ *
+ * @param vAlign
+ * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL.
+ * @return this
+ */
+ public GridDataFactory alignV(int vAlign) {
+ m_data.verticalAlignment = vAlign;
+ return this;
+ }
+
+ /**
+ * Sets the vertical alignment of the control to GridData.BEGINNING
+ *
+ * @return this
+ */
+ public GridDataFactory alignVT() {
+ return alignV(GridData.BEGINNING);
+ }
+
+ /**
+ * Sets the vertical alignment of the control to GridData.CENTER
+ *
+ * @return this
+ */
+ public GridDataFactory alignVM() {
+ return alignV(GridData.CENTER);
+ }
+
+ /**
+ * Sets the vertical alignment of the control to GridData.FILL
+ *
+ * @return this
+ */
+ public GridDataFactory alignVF() {
+ return alignV(GridData.FILL);
+ }
+
+ /**
+ * Sets the vertical alignment of the control to GridData.FILL
+ *
+ * @return this
+ */
+ public GridDataFactory fillV() {
+ return alignVF();
+ }
+
+ /**
+ * Sets the vertical alignment of the control to GridData.END
+ *
+ * @return this
+ */
+ public GridDataFactory alignVB() {
+ return alignV(GridData.END);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Indent
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the indent of the control within the cell in pixels.
+ */
+ public GridDataFactory indentH(int hIndent) {
+ m_data.horizontalIndent = hIndent;
+ return this;
+ }
+
+ /**
+ * Sets the indent of the control within the cell in characters.
+ */
+ public GridDataFactory indentHC(int hIndent) {
+ m_data.horizontalIndent = m_pixelConverter.convertWidthInCharsToPixels(hIndent);
+ return this;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Grab
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Determines whether extra horizontal or vertical space should be allocated to this control's
+ * column when the layout resizes. If any control in the column is set to grab horizontal then the
+ * whole column will grab horizontal space. If any control in the row is set to grab vertical then
+ * the whole row will grab vertical space.
+ *
+ * @param horizontal
+ * true if the control's column should grow horizontally
+ * @param vertical
+ * true if the control's row should grow vertically
+ * @return this
+ */
+ public GridDataFactory grab(boolean horizontal, boolean vertical) {
+ m_data.grabExcessHorizontalSpace = horizontal;
+ m_data.grabExcessVerticalSpace = vertical;
+ return this;
+ }
+
+ public GridDataFactory grabH() {
+ m_data.grabExcessHorizontalSpace = true;
+ return this;
+ }
+
+ public GridDataFactory grabV() {
+ m_data.grabExcessVerticalSpace = true;
+ return this;
+ }
+
+ public GridDataFactory grab() {
+ return grab(true, true);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Exclude
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public GridDataFactory exclude(boolean value) {
+ m_data.exclude = value;
+ return this;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java
new file mode 100644
index 0000000..f6dc58e
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+
+/**
+ * GridLayoutFactory provides a convenient shorthand for creating and initializing GridLayout.
+ *
+ * @author scheglov_ke
+ */
+public final class GridLayoutFactory {
+ private final GridLayout m_layout;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private GridLayoutFactory(Composite composite, GridLayout layout) {
+ m_layout = layout;
+ composite.setLayout(m_layout);
+ }
+
+ public static GridLayoutFactory create(Composite composite) {
+ return new GridLayoutFactory(composite, new GridLayout());
+ }
+
+ public static GridLayoutFactory modify(Composite composite) {
+ Layout layout = composite.getLayout();
+ if (layout instanceof GridLayout) {
+ return new GridLayoutFactory(composite, (GridLayout) layout);
+ }
+ return create(composite);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets number of columns in {@link GridLayout}.
+ */
+ public GridLayoutFactory columns(int numColumns) {
+ m_layout.numColumns = numColumns;
+ return this;
+ }
+
+ /**
+ * Specifies whether all columns in the layout will be forced to have the same width.
+ */
+ public GridLayoutFactory equalColumns() {
+ m_layout.makeColumnsEqualWidth = true;
+ return this;
+ }
+
+ /**
+ * Sets the horizontal margins.
+ */
+ public GridLayoutFactory marginsH(int margins) {
+ m_layout.marginWidth = margins;
+ return this;
+ }
+
+ /**
+ * Sets the vertical margins.
+ */
+ public GridLayoutFactory marginsV(int margins) {
+ m_layout.marginHeight = margins;
+ return this;
+ }
+
+ /**
+ * Sets the horizontal/vertical margins.
+ */
+ public GridLayoutFactory margins(int margins) {
+ m_layout.marginWidth = m_layout.marginHeight = margins;
+ return this;
+ }
+
+ /**
+ * Sets zero horizontal and vertical margins.
+ */
+ public GridLayoutFactory noMargins() {
+ m_layout.marginWidth = m_layout.marginHeight = 0;
+ return this;
+ }
+
+ /**
+ * Sets zero horizontal and vertical spacing.
+ */
+ public GridLayoutFactory noSpacing() {
+ m_layout.horizontalSpacing = m_layout.verticalSpacing = 0;
+ return this;
+ }
+
+ /**
+ * Sets horizontal spacing.
+ */
+ public GridLayoutFactory spacingH(int spacing) {
+ m_layout.horizontalSpacing = spacing;
+ return this;
+ }
+
+ /**
+ * Sets vertical spacing.
+ */
+ public GridLayoutFactory spacingV(int spacing) {
+ m_layout.verticalSpacing = spacing;
+ return this;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java
new file mode 100644
index 0000000..d5fc39f
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+
+/**
+ * Implementation of {@link ImageDescriptor} for {@link Image} instance.
+ *
+ * @author scheglov_ke
+ * @coverage core.ui
+ */
+public final class ImageImageDescriptor extends ImageDescriptor {
+ private final Image m_Image;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public ImageImageDescriptor(Image image) {
+ m_Image = image;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // ImageDescriptor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public ImageData getImageData() {
+ return m_Image == null ? null : m_Image.getImageData();
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java
new file mode 100644
index 0000000..1e3699f
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Helper class for converting DLU and char size into pixels.
+ *
+ * Based on code from JDT UI.
+ *
+ * @author scheglov_ke
+ */
+public class PixelConverter {
+ private final FontMetrics fFontMetrics;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructors
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public PixelConverter(Control control) {
+ GC gc = new GC(control);
+ gc.setFont(control.getFont());
+ fFontMetrics = gc.getFontMetrics();
+ gc.dispose();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Conversions
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * see org.eclipse.jface.dialogs.DialogPage#convertHeightInCharsToPixels(int)
+ */
+ public int convertHeightInCharsToPixels(int chars) {
+ return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars);
+ }
+
+ /**
+ * see org.eclipse.jface.dialogs.DialogPage#convertHorizontalDLUsToPixels(int)
+ */
+ public int convertHorizontalDLUsToPixels(int dlus) {
+ return Dialog.convertHorizontalDLUsToPixels(fFontMetrics, dlus);
+ }
+
+ /**
+ * see org.eclipse.jface.dialogs.DialogPage#convertVerticalDLUsToPixels(int)
+ */
+ public int convertVerticalDLUsToPixels(int dlus) {
+ return Dialog.convertVerticalDLUsToPixels(fFontMetrics, dlus);
+ }
+
+ /**
+ * see org.eclipse.jface.dialogs.DialogPage#convertWidthInCharsToPixels(int)
+ */
+ public int convertWidthInCharsToPixels(int chars) {
+ return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars);
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java
new file mode 100644
index 0000000..126efba
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Utilities for UI.
+ *
+ * @author scheglov_ke
+ */
+public class UiUtils {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Message dialogs
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Opens standard warning dialog.
+ */
+ public static void openWarning(Shell parent, String title, String message) {
+ MessageDialog dialog =
+ new MessageDialog(parent,
+ title,
+ null,
+ message,
+ MessageDialog.WARNING,
+ new String[]{IDialogConstants.OK_LABEL},
+ 0);
+ dialog.open();
+ }
+} \ No newline at end of file
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java
new file mode 100644
index 0000000..307c2f6
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java
@@ -0,0 +1,221 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui.dialogs;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * {@link Dialog} that remembers location/size between usage sessions.
+ *
+ * @author scheglov_ke
+ * @coverage core.ui
+ */
+public abstract class ResizableDialog extends Dialog {
+ /**
+ * Key for accessing {@link Dialog} from its {@link Shell}.
+ */
+ public static final String KEY_DIALOG = "KEY_DIALOG";
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Internal constants
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static final String X = "x";
+ private static final String Y = "y";
+ private static final String WIDTH = "width";
+ private static final String HEIGHT = "height";
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Instance fields
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private final AbstractUIPlugin m_plugin;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public ResizableDialog(Shell parentShell, AbstractUIPlugin plugin) {
+ super(parentShell);
+ m_plugin = plugin;
+ setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Size
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ protected Point getInitialSize() {
+ // track the current dialog bounds
+ installDialogBoundsTracker();
+ // answer the size from the previous incarnation
+ Point defaultSize = getDefaultSize();
+ if ((getShellStyle() & SWT.RESIZE) != 0) {
+ Rectangle oldBounds = loadBounds();
+ if (oldBounds != null) {
+ Rectangle displayBounds = getShell().getDisplay().getBounds();
+ int width = Math.min(displayBounds.width, Math.max(oldBounds.width, defaultSize.x));
+ int height = Math.min(displayBounds.height, Math.max(oldBounds.height, defaultSize.y));
+ return new Point(width, height);
+ }
+ }
+ // use default size
+ return defaultSize;
+ }
+
+ /**
+ * @return the default size of dialog.
+ */
+ protected Point getDefaultSize() {
+ return super.getInitialSize();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Location
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ protected Point getInitialLocation(Point initialSize) {
+ Rectangle windowBounds;
+ {
+ Shell windowShell = m_plugin.getWorkbench().getActiveWorkbenchWindow().getShell();
+ windowBounds = windowShell.getBounds();
+ }
+ // answer the location from the previous incarnation
+ Rectangle bounds = loadBounds();
+ if (bounds != null) {
+ int x = bounds.x;
+ int y = bounds.y;
+ int maxX = windowBounds.x + windowBounds.width - initialSize.x;
+ int maxY = windowBounds.y + windowBounds.height - initialSize.y;
+ if (x > maxX) {
+ x = maxX;
+ }
+ if (y > maxY) {
+ y = maxY;
+ }
+ if (x < windowBounds.x) {
+ x = windowBounds.x;
+ }
+ if (y < windowBounds.y) {
+ y = windowBounds.y;
+ }
+ return new Point(x, y);
+ }
+ // default location - centered on workbench window
+ int x = windowBounds.x + (windowBounds.width - initialSize.x) / 2;
+ int y = windowBounds.y + (windowBounds.height - initialSize.y) / 2;
+ return new Point(x, y);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Bounds
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Loads bounds from {@link IDialogSettings}.
+ */
+ private Rectangle loadBounds() {
+ IDialogSettings settings = getDialogSettings();
+ try {
+ return new Rectangle(settings.getInt(X),
+ settings.getInt(Y),
+ settings.getInt(WIDTH),
+ settings.getInt(HEIGHT));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Saves bounds to {@link IDialogSettings}.
+ */
+ private void saveBounds(Rectangle bounds) {
+ IDialogSettings settings = getDialogSettings();
+ settings.put(X, bounds.x);
+ settings.put(Y, bounds.y);
+ settings.put(WIDTH, bounds.width);
+ settings.put(HEIGHT, bounds.height);
+ }
+
+ /**
+ * @return the {@link IDialogSettings} for this dialog with this type.
+ */
+ protected IDialogSettings getDialogSettings() {
+ IDialogSettings settings = m_plugin.getDialogSettings();
+ String sectionName = getDialogSettingsSectionName();
+ if (settings.getSection(sectionName) == null) {
+ return settings.addNewSection(sectionName);
+ }
+ return settings.getSection(sectionName);
+ }
+
+ /**
+ * @return the name of section for dialog specific bounds. By default uses name of {@link Class},
+ * but if same dialog is used for displaying different content, then may be overridden.
+ */
+ protected String getDialogSettingsSectionName() {
+ return getClass().getName();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Size tracking
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected Rectangle cachedBounds;
+
+ private void installDialogBoundsTracker() {
+ getShell().addControlListener(new ControlListener() {
+ public void controlMoved(ControlEvent e) {
+ cachedBounds = getShell().getBounds();
+ }
+
+ public void controlResized(ControlEvent e) {
+ cachedBounds = getShell().getBounds();
+ }
+ });
+ }
+
+ @Override
+ public boolean close() {
+ boolean shellMaximized = getShell().getMaximized();
+ boolean closed = super.close();
+ if (closed && !shellMaximized && cachedBounds != null) {
+ saveBounds(cachedBounds);
+ }
+ return closed;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Shell
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setData(KEY_DIALOG, this);
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java
new file mode 100644
index 0000000..745bcc3
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui.dialogs;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
+import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.List;
+
+/**
+ * The dialog for editing array of {@link String}'s.
+ *
+ * @author scheglov_ke
+ * @coverage core.ui
+ */
+public class StringsDialog extends TextDialog {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public StringsDialog(Shell parentShell,
+ AbstractUIPlugin plugin,
+ String titleText,
+ String headerText,
+ String footerText) {
+ super(parentShell, plugin, titleText, headerText, footerText);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Items
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the items to edit.
+ */
+ public void setItems(String[] items) {
+ setText(Joiner.on('\n').join(items));
+ }
+
+ /**
+ * @return the edited items.
+ */
+ public String[] getItems() {
+ return ExecutionUtils.runObjectLog(new RunnableObjectEx<String[]>() {
+ @Override
+ public String[] runObject() throws Exception {
+ List<String> strings = Lists.newArrayList();
+ BufferedReader br = new BufferedReader(new StringReader(getText()));
+ while (true) {
+ String s = br.readLine();
+ if (s == null) {
+ break;
+ }
+ strings.add(s);
+ }
+ return strings.toArray(new String[strings.size()]);
+ }
+ }, new String[0]);
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java
new file mode 100644
index 0000000..320fa32
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils.ui.dialogs;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.eclipse.wb.internal.core.utils.ui.GridDataFactory;
+import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory;
+
+/**
+ * The dialog for editing multiline text.
+ *
+ * @author scheglov_ke
+ * @coverage core.ui
+ */
+public class TextDialog extends ResizableDialog {
+ private final String m_titleText;
+ private final String m_headerText;
+ private final String m_footerText;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public TextDialog(Shell parentShell,
+ AbstractUIPlugin plugin,
+ String titleText,
+ String headerText,
+ String footerText) {
+ super(parentShell, plugin);
+ m_titleText = titleText;
+ m_headerText = headerText;
+ m_footerText = footerText;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Text
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private String m_text;
+
+ /**
+ * Sets the text to edit.
+ */
+ public final void setText(String text) {
+ m_text = text;
+ }
+
+ /**
+ * @return the edited text.
+ */
+ public final String getText() {
+ return m_text;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // GUI
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected Text m_textWidget;
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite area = (Composite) super.createDialogArea(parent);
+ GridLayoutFactory.create(area);
+ // header
+ new Label(area, SWT.NONE).setText(m_headerText);
+ // Text widget
+ {
+ m_textWidget = new Text(area, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+ GridDataFactory.create(m_textWidget).grab().fill().hintVC(10);
+ m_textWidget.setText(m_text);
+ // handle Ctrl+Enter as OK
+ m_textWidget.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.stateMask == SWT.CTRL && e.keyCode == SWT.CR) {
+ okPressed();
+ }
+ }
+ });
+ }
+ // footer
+ new Label(area, SWT.NONE).setText(m_footerText);
+ //
+ return area;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(m_titleText);
+ }
+
+ @Override
+ protected void okPressed() {
+ m_text = m_textWidget.getText();
+ super.okPressed();
+ }
+}