| /******************************************************************************* |
| * 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; |
| } |
| } |
| } |