blob: 0cb8f23a52fec358e38fdc91076419f7943988ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.jpa.ui;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jpt.common.ui.internal.JptUIPlugin;
import org.eclipse.jpt.common.utility.internal.AbstractBooleanReference;
import org.eclipse.jpt.jpa.core.JpaProjectManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.osgi.framework.BundleContext;
/**
* Dali JPA UI plug-in.
* <p>
* Provisional API: This interface is part of an interim API that is still
* under development and expected to change significantly before reaching
* stability. It is available at this early stage to solicit feedback from
* pioneering adopters on the understanding that any code that uses this API
* will almost certainly be broken (repeatedly) as the API evolves.
*/
@SuppressWarnings("nls")
public class JptJpaUiPlugin
extends JptUIPlugin
{
/**
* @see #focusIn(Control)
*/
private final AsyncEventListenerFlag asyncEventListenerFlag = new AsyncEventListenerFlag();
private final Listener focusListener;
// ********** constants **********
/**
* The plug-in identifier of JPA UI support (value {@value}).
*/
public static final String PLUGIN_ID = "org.eclipse.jpt.jpa.ui";
public static final String PLUGIN_ID_ = PLUGIN_ID + '.';
private static final String DALI_UI_KEY = PLUGIN_ID;
private static final Object DALI_UI_DATA = new Object();
// ********** Preference keys **********
/**
* The preference key used to retrieve the case used for JPQL identifiers.
* @deprecated - Use JpaJpqlPreferencesManager instead
*/
@Deprecated
public static final String JPQL_IDENTIFIER_CASE_PREF_KEY = PLUGIN_ID + ".jpqlIdentifier.case";
@Deprecated
public static final String JPQL_IDENTIFIER_LOWERCASE_PREF_VALUE = "lowercase";
@Deprecated
public static final String JPQL_IDENTIFIER_UPPERCASE_PREF_VALUE = "uppercase";
@Deprecated
public static final String JPQL_IDENTIFIER_MATCH_FIRST_CHARACTER_CASE_PREF_KEY = PLUGIN_ID + ".jpqlIdentifier.matchFirstCharacterCase";
// ********** singleton **********
private static JptJpaUiPlugin INSTANCE;
/**
* Returns the singleton JPT UI plug-in.
*/
public static JptJpaUiPlugin instance() {
return INSTANCE;
}
// ********** logging **********
/**
* Log the specified message.
*/
public static void log(String msg) {
INSTANCE.logError(msg);
}
/**
* Log the specified exception or error.
*/
public static void log(Throwable throwable) {
INSTANCE.logError(throwable);
}
/**
* Log the specified message and exception or error.
*/
public static void log(String msg, Throwable throwable) {
INSTANCE.logError(msg, throwable);
}
// ********** images **********
/**
* Return an image descriptor for the specified <code>.gif<code>
* file in the icons folder.
*/
public static ImageDescriptor getImageDescriptor(String key) {
if ( ! key.startsWith("icons/")) {
key = "icons/" + key;
}
if ( ! key.endsWith(".gif")) {
key = key + ".gif";
}
return imageDescriptorFromPlugin(PLUGIN_ID, key);
}
/**
* Return an image for the specified <code>.gif<code>
* file in the icons folder.
*/
//TODO we are using the ImageRegistry here and storing all our icons for the life of the plugin,
//which means until the workspace is closed. This is better than before where we constantly
//created new images. Bug 306437 is about cleaning this up and using Local Resource Managers
//on our views so that closing the JPA perspective would mean our icons are disposed.
public static Image getImage(String key) {
ImageRegistry imageRegistry = instance().getImageRegistry();
Image image = imageRegistry.get(key);
if (image == null) {
imageRegistry.put(key, getImageDescriptor(key));
image = imageRegistry.get(key);
}
return image;
}
// ********** construction **********
public JptJpaUiPlugin() {
super();
this.focusListener = this.buildFocusListener();
if (INSTANCE != null) {
throw new IllegalStateException();
}
INSTANCE = this;
}
/**
* We are registered to receive only {@link SWT#FocusIn} events
*/
private Listener buildFocusListener() {
return new Listener() {
public void handleEvent(Event event) {
JptJpaUiPlugin.this.focusIn((Control) event.widget);
}
};
}
// ********** focus handling **********
/**
* This method is called whenever a {@link SWT#FocusIn} event is generated.
* <p>
* If the control gaining focus is part of one the Dali composites
* (typically the JPA Details View), we deactivate the Dali Java change
* listener so we ignore any changes to the Java source code that probably
* originated from Dali. This means we will miss any changes to the Java
* source code that is caused by non-UI activity; but, we hope, these
* changes are unrelated to JPA annotations etc.
* <p>
* If the control gaining focus is <em>not</em> part of one of the Dali
* composites, we start listening to the Java change events again. This
* method is called whenever a non-Dali UI control gains the UI focus. When
* this happens we activate the Dali Java change listener so that we begin
* to keep the Dali model synchronized with the Java source code.
*/
/* CU private */ void focusIn(Control control) {
this.asyncEventListenerFlag.setValue(this.controlIsNonDali(control));
}
/**
* Return whether the specified control is <em><b>neither</b></em>
* a Dali UI component <em><b>nor</b></em> contained by a Dali UI component.
*/
private boolean controlIsNonDali(Control control) {
return ! this.controlIsDali(control);
}
/**
* Return whether the specified control, or any of its ancestors, is a Dali
* UI component.
*
* @see #controlAffectsJavaSource(Control)
*/
private boolean controlIsDali(Control control) {
while (control != null) {
if (control.getData(DALI_UI_KEY) == DALI_UI_DATA) {
return true;
}
control = control.getParent();
}
return false;
}
/**
* Tag the specified control so that whenever it (or any of its descendants)
* has the focus, the Dali model ignores any Java change events. This method
* is to be called when the control is first constructed.
*
* @see #controlIsDali(Control)
*/
public void controlAffectsJavaSource(Control control) {
control.setData(DALI_UI_KEY, DALI_UI_DATA);
}
// ********** plug-in implementation **********
/**
* Register our SWT listener with the display so we receive notification
* of every "focus in" event.
*/
@Override
public synchronized void start(BundleContext context) throws Exception {
super.start(context);
this.getJpaProjectManager().addAsyncEventListenerFlag(this.asyncEventListenerFlag);
Display.getDefault().addFilter(SWT.FocusIn, this.focusListener);
}
private JpaProjectManager getJpaProjectManager() {
return (JpaProjectManager) ResourcesPlugin.getWorkspace().getAdapter(JpaProjectManager.class);
}
/**
* Unregister our SWT listener with the display.
*/
@Override
public synchronized void stop(BundleContext context) throws Exception {
try {
Display.getDefault().removeFilter(SWT.FocusIn, this.focusListener);
this.getJpaProjectManager().removeAsyncEventListenerFlag(this.asyncEventListenerFlag);
} finally {
super.stop(context);
}
}
/**
* This flag's value depends on the current thread. If the current thread is
* the background Java Reconciler thread, the flag's value is determined by
* the current UI focus (i.e. whether the focus is somewhere other than a
* Dali view); otherwise the flag's value is <code>true</code>.
* In other words, if a Dali view has the focus and a Java event is fired
* on the Java Reconciler thread, the event is ignored; if the event is
* fired on some other thread (typically synchronously on the Main thread),
* the event is forwarded to the JPA projects.
*/
/* CU private */ static class AsyncEventListenerFlag
extends AbstractBooleanReference
{
private volatile boolean value = true;
@SuppressWarnings("restriction")
private static final String JAVA_RECONCILER_THREAD_NAME = org.eclipse.jdt.internal.ui.text.JavaReconciler.class.getName();
AsyncEventListenerFlag() {
super();
}
public boolean getValue() {
if (Thread.currentThread().getName().equals(JAVA_RECONCILER_THREAD_NAME)) {
return this.value;
}
return true;
}
public boolean setValue(boolean value) {
boolean old = this.value;
this.value = value;
return old;
}
}
}