blob: 8147c029b86afe232d6fc7e399e56611c21edf34 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 BSI Business Systems Integration AG.
* 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:
* Daniel Wiehl (BSI Business Systems Integration AG) - initial API and implementation
******************************************************************************/
/**
*
*/
package org.eclipse.scout.sdk.ws.jaxws.resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.scout.commons.CompareUtility;
import org.eclipse.scout.sdk.ws.jaxws.JaxWsSdk;
public class ManagedResource {
/**
* modification stamp to indicate that API changed resource to omit JDT Resource Changed notification.
* Invoke {@link ManagedResource#setModificationStamp(long)} with this code prior to write resource to disk. It is
* important to reset the modification stamp after having written to disk.
*/
public static final int API_MODIFICATION_STAMP = Integer.MAX_VALUE;
protected long m_modificationStamp;
private IProject m_project;
protected IFile m_file;
protected Object m_fileLock;
private Object m_registrationLock;
private IResourceChangeListener m_jdtResourceChangedListener;
private List<P_ResourceListenerEntry> m_resourceListeners;
public ManagedResource(IProject project) {
m_project = project;
m_fileLock = new Object();
m_registrationLock = new Object();
m_resourceListeners = new ArrayList<P_ResourceListenerEntry>();
m_jdtResourceChangedListener = new P_JdtResourceChangedListener();
}
private void attachJdtResourceChangeListener() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(m_jdtResourceChangedListener);
}
private void detachJdtResourceChangeListener() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(m_jdtResourceChangedListener);
}
/**
* To receive notifications when this resource change.
*
* @param listener
*/
public void addResourceListener(IResourceListener listener) {
addResourceListener(null, null, listener);
}
/**
* To receive notifications when this resource change.
*
* @param element
* To only receive notification that origin of the given element within the resource.
* @param listener
*/
public void addResourceListener(String element, IResourceListener listener) {
addResourceListener(element, null, listener);
}
/**
* To receive notifications when this resource change.
*
* @param event
* the event to be interested in receiving notifications. This is <em>bitwise OR</em>'ing together event
* constants defined in {@link IResourceListener}.
* @param listener
*/
public void addResourceListener(Integer event, IResourceListener listener) {
addResourceListener(null, event, listener);
}
/**
* To receive notifications when this resource change.
*
* @param element
* To only receive notification that origin of the given element within the resource.
* @param event
* To only receive notification of the given event. This is <em>bitwise OR</em>'ing together event
* constants defined in {@link IResourceListener}.
* @param listener
*/
public void addResourceListener(String element, Integer event, IResourceListener listener) {
P_ResourceListenerEntry entry = new P_ResourceListenerEntry();
entry.setElement(element);
entry.setEvent(event);
entry.setListener(listener);
synchronized (m_registrationLock) {
if (m_resourceListeners.size() == 0) {
attachJdtResourceChangeListener();
}
m_resourceListeners.add(entry);
}
}
public void removeResourceListener(IResourceListener listener) {
synchronized (m_registrationLock) {
for (P_ResourceListenerEntry entry : m_resourceListeners.toArray(new P_ResourceListenerEntry[m_resourceListeners.size()])) {
if (entry.getListener() == listener) {
m_resourceListeners.remove(entry);
}
}
if (m_resourceListeners.size() == 0) {
detachJdtResourceChangeListener();
}
}
}
public IFile getFile() {
synchronized (m_fileLock) {
return m_file;
}
}
public boolean isSameFile(IFile file) {
if (file == null || m_file == null) {
return false;
}
return file.getProjectRelativePath().equals(m_file.getProjectRelativePath());
}
public void setFile(IFile file) {
synchronized (m_fileLock) {
m_modificationStamp = IResource.NULL_STAMP;
m_file = file;
}
}
public boolean existsFile() {
return m_file != null && m_file.exists();
}
protected long getModificationStamp() {
return m_modificationStamp;
}
protected void setModificationStamp(long modificationStamp) {
m_modificationStamp = modificationStamp;
}
public void touch() {
m_modificationStamp = IResource.NULL_STAMP;
}
/**
* To notify listeners about a change.
*
* @param element
* the element that changed (defined in {@link IResourceListener})
* @param event
* the events that describes the change. This is <em>bitwise OR</em>'ing together events described in
* {@link IResourceListener}
*/
protected void notifyResourceListeners(String element, Integer event) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
Set<IResourceListener> listenersProcessed = new HashSet<IResourceListener>();
for (P_ResourceListenerEntry entry : m_resourceListeners.toArray(new P_ResourceListenerEntry[m_resourceListeners.size()])) {
if (!listenersProcessed.contains(entry.getListener()) &&
(entry.getElement() == null || entry.getElement().equals(element)) &&
(entry.getEvent() == null || (entry.getEvent() & event) > 0)) {
try {
entry.getListener().changed(element, event);
}
catch (Throwable e) {
// failsafe
JaxWsSdk.logError("Error occured while notifying listener about change", e);
}
finally {
listenersProcessed.add(entry.getListener());
}
}
}
}
private class P_JdtResourceChangedListener implements IResourceChangeListener {
@Override
public void resourceChanged(IResourceChangeEvent event) {
if (m_file == null) {
return;
}
// there is only interest in POST_CHANGE events
if (event.getType() != IResourceChangeEvent.POST_CHANGE) {
return;
}
try {
IResourceDelta rootDelta = event.getDelta();
rootDelta.accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta.getKind() == IResourceDelta.REMOVED || delta.getKind() == IResourceDelta.REPLACED || delta.getKind() == IResourceDelta.ADDED || (delta.getKind() == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.CONTENT) != 0)) { // it is crucial to exclude marker update events
IResource candidate = delta.getResource();
if (candidate.getType() != IResource.FILE) {
return true;
}
if (candidate.getProject() != m_project) {
return false;
}
if (CompareUtility.equals(candidate, m_file)) {
// only notify if modification stamp of resource is different to current stamp.
// This is to exclude workspace modifications caused by API code
if (m_modificationStamp != API_MODIFICATION_STAMP && (m_modificationStamp == IResource.NULL_STAMP || m_modificationStamp != m_file.getModificationStamp())) {
notifyResourceListeners(IResourceListener.ELEMENT_FILE, IResourceListener.EVENT_UNKNOWN);
}
return false;
}
}
return true;
}
});
}
catch (Exception e) {
JaxWsSdk.logError("Unexpected error occured while intercepting 'Resource Change' event.", e);
}
}
}
private class P_ResourceListenerEntry {
private IResourceListener m_listener;
/**
* the source the listener is interested in to receive notifications
*/
private String m_element;
/**
* the event the listener is interested in to receive notifications
*/
private Integer m_event;
public IResourceListener getListener() {
return m_listener;
}
public void setListener(IResourceListener listener) {
m_listener = listener;
}
public String getElement() {
return m_element;
}
public void setElement(String element) {
m_element = element;
}
public Integer getEvent() {
return m_event;
}
public void setEvent(Integer event) {
m_event = event;
}
}
}