1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ide.eclipse.adt.internal.resources.manager; 18 19 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 20 21 import org.eclipse.core.resources.IFile; 22 import org.eclipse.core.resources.IFolder; 23 import org.eclipse.core.resources.IMarkerDelta; 24 import org.eclipse.core.resources.IProject; 25 import org.eclipse.core.resources.IResource; 26 import org.eclipse.core.resources.IResourceChangeEvent; 27 import org.eclipse.core.resources.IResourceChangeListener; 28 import org.eclipse.core.resources.IResourceDelta; 29 import org.eclipse.core.resources.IResourceDeltaVisitor; 30 import org.eclipse.core.resources.IWorkspace; 31 import org.eclipse.core.resources.IWorkspaceRoot; 32 import org.eclipse.core.runtime.CoreException; 33 import org.eclipse.jdt.core.IJavaModel; 34 import org.eclipse.jdt.core.IJavaProject; 35 import org.eclipse.jdt.core.JavaCore; 36 37 import java.util.ArrayList; 38 39 /** 40 * Resource Monitor for the whole editor plugin. Other, more simple, listeners can register to 41 * that one. 42 */ 43 public class ResourceMonitor implements IResourceChangeListener { 44 45 private final static ResourceMonitor sThis = new ResourceMonitor(); 46 47 /** 48 * Classes which implement this interface provide a method that deals 49 * with file change events. 50 */ 51 public interface IFileListener { 52 /** 53 * Sent when a file changed. 54 * @param file The file that changed. 55 * @param markerDeltas The marker deltas for the file. 56 * @param kind The change kind. This is equivalent to 57 * {@link IResourceDelta#accept(IResourceDeltaVisitor)} 58 */ fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind)59 public void fileChanged(IFile file, IMarkerDelta[] markerDeltas, int kind); 60 } 61 62 /** 63 * Classes which implements this interface provide methods dealing with project events. 64 */ 65 public interface IProjectListener { 66 /** 67 * Sent for each opened android project at the time the listener is put in place. 68 * @param project the opened project. 69 */ projectOpenedWithWorkspace(IProject project)70 public void projectOpenedWithWorkspace(IProject project); 71 /** 72 * Sent when a project is opened. 73 * @param project the project being opened. 74 */ projectOpened(IProject project)75 public void projectOpened(IProject project); 76 /** 77 * Sent when a project is closed. 78 * @param project the project being closed. 79 */ projectClosed(IProject project)80 public void projectClosed(IProject project); 81 /** 82 * Sent when a project is deleted. 83 * @param project the project about to be deleted. 84 */ projectDeleted(IProject project)85 public void projectDeleted(IProject project); 86 } 87 88 /** 89 * Classes which implement this interface provide a method that deals 90 * with folder change events 91 */ 92 public interface IFolderListener { 93 /** 94 * Sent when a folder changed. 95 * @param folder The file that was changed 96 * @param kind The change kind. This is equivalent to {@link IResourceDelta#getKind()} 97 */ folderChanged(IFolder folder, int kind)98 public void folderChanged(IFolder folder, int kind); 99 } 100 101 /** 102 * Interface for a listener to be notified when resource change event starts and ends. 103 */ 104 public interface IResourceEventListener { resourceChangeEventStart()105 public void resourceChangeEventStart(); resourceChangeEventEnd()106 public void resourceChangeEventEnd(); 107 } 108 109 /** 110 * Base listener bundle to associate a listener to an event mask. 111 */ 112 private static class ListenerBundle { 113 /** Mask value to accept all events */ 114 public final static int MASK_NONE = -1; 115 116 /** 117 * Event mask. Values accepted are IResourceDelta.### 118 * @see IResourceDelta#ADDED 119 * @see IResourceDelta#REMOVED 120 * @see IResourceDelta#CHANGED 121 * @see IResourceDelta#ADDED_PHANTOM 122 * @see IResourceDelta#REMOVED_PHANTOM 123 * */ 124 int kindMask; 125 } 126 127 /** 128 * Listener bundle for file event. 129 */ 130 private static class FileListenerBundle extends ListenerBundle { 131 132 /** The file listener */ 133 IFileListener listener; 134 } 135 136 /** 137 * Listener bundle for folder event. 138 */ 139 private static class FolderListenerBundle extends ListenerBundle { 140 /** The file listener */ 141 IFolderListener listener; 142 } 143 144 private final ArrayList<FileListenerBundle> mFileListeners = 145 new ArrayList<FileListenerBundle>(); 146 147 private final ArrayList<FolderListenerBundle> mFolderListeners = 148 new ArrayList<FolderListenerBundle>(); 149 150 private final ArrayList<IProjectListener> mProjectListeners = new ArrayList<IProjectListener>(); 151 152 private final ArrayList<IResourceEventListener> mEventListeners = 153 new ArrayList<IResourceEventListener>(); 154 155 private IWorkspace mWorkspace; 156 157 /** 158 * Delta visitor for resource changes. 159 */ 160 private final class DeltaVisitor implements IResourceDeltaVisitor { 161 visit(IResourceDelta delta)162 public boolean visit(IResourceDelta delta) { 163 IResource r = delta.getResource(); 164 int type = r.getType(); 165 if (type == IResource.FILE) { 166 int kind = delta.getKind(); 167 // notify the listeners. 168 for (FileListenerBundle bundle : mFileListeners) { 169 if (bundle.kindMask == ListenerBundle.MASK_NONE 170 || (bundle.kindMask & kind) != 0) { 171 bundle.listener.fileChanged((IFile)r, delta.getMarkerDeltas(), kind); 172 } 173 } 174 return false; 175 } else if (type == IResource.FOLDER) { 176 int kind = delta.getKind(); 177 // notify the listeners. 178 for (FolderListenerBundle bundle : mFolderListeners) { 179 if (bundle.kindMask == ListenerBundle.MASK_NONE 180 || (bundle.kindMask & kind) != 0) { 181 bundle.listener.folderChanged((IFolder)r, kind); 182 } 183 } 184 return true; 185 } else if (type == IResource.PROJECT) { 186 int flags = delta.getFlags(); 187 188 if (flags == IResourceDelta.OPEN) { 189 // the project is opening or closing. 190 IProject project = (IProject)r; 191 192 if (project.isOpen()) { 193 // notify the listeners. 194 for (IProjectListener pl : mProjectListeners) { 195 pl.projectOpened(project); 196 } 197 } else { 198 // notify the listeners. 199 for (IProjectListener pl : mProjectListeners) { 200 pl.projectClosed(project); 201 } 202 } 203 } 204 } 205 206 return true; 207 } 208 } 209 getMonitor()210 public static ResourceMonitor getMonitor() { 211 return sThis; 212 } 213 214 215 /** 216 * Starts the resource monitoring. 217 * @param ws The current workspace. 218 * @return The monitor object. 219 */ startMonitoring(IWorkspace ws)220 public static ResourceMonitor startMonitoring(IWorkspace ws) { 221 if (sThis != null) { 222 ws.addResourceChangeListener(sThis, 223 IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE); 224 sThis.mWorkspace = ws; 225 } 226 return sThis; 227 } 228 229 /** 230 * Stops the resource monitoring. 231 * @param ws The current workspace. 232 */ stopMonitoring(IWorkspace ws)233 public static void stopMonitoring(IWorkspace ws) { 234 if (sThis != null) { 235 ws.removeResourceChangeListener(sThis); 236 237 sThis.mFileListeners.clear(); 238 sThis.mProjectListeners.clear(); 239 } 240 } 241 242 /** 243 * Adds a file listener. 244 * @param listener The listener to receive the events. 245 * @param kindMask The event mask to filter out specific events. 246 * {@link ListenerBundle#MASK_NONE} will forward all events. 247 */ addFileListener(IFileListener listener, int kindMask)248 public synchronized void addFileListener(IFileListener listener, int kindMask) { 249 FileListenerBundle bundle = new FileListenerBundle(); 250 bundle.listener = listener; 251 bundle.kindMask = kindMask; 252 253 mFileListeners.add(bundle); 254 } 255 256 /** 257 * Removes an existing file listener. 258 * @param listener the listener to remove. 259 */ removeFileListener(IFileListener listener)260 public synchronized void removeFileListener(IFileListener listener) { 261 for (int i = 0 ; i < mFileListeners.size() ; i++) { 262 FileListenerBundle bundle = mFileListeners.get(i); 263 if (bundle.listener == listener) { 264 mFileListeners.remove(i); 265 return; 266 } 267 } 268 } 269 270 /** 271 * Adds a folder listener. 272 * @param listener The listener to receive the events. 273 * @param kindMask The event mask to filter out specific events. 274 * {@link ListenerBundle#MASK_NONE} will forward all events. 275 */ addFolderListener(IFolderListener listener, int kindMask)276 public synchronized void addFolderListener(IFolderListener listener, int kindMask) { 277 FolderListenerBundle bundle = new FolderListenerBundle(); 278 bundle.listener = listener; 279 bundle.kindMask = kindMask; 280 281 mFolderListeners.add(bundle); 282 } 283 284 /** 285 * Removes an existing folder listener. 286 * @param listener the listener to remove. 287 */ removeFolderListener(IFolderListener listener)288 public synchronized void removeFolderListener(IFolderListener listener) { 289 for (int i = 0 ; i < mFolderListeners.size() ; i++) { 290 FolderListenerBundle bundle = mFolderListeners.get(i); 291 if (bundle.listener == listener) { 292 mFolderListeners.remove(i); 293 return; 294 } 295 } 296 } 297 298 /** 299 * Adds a project listener. 300 * @param listener The listener to receive the events. 301 */ addProjectListener(IProjectListener listener)302 public synchronized void addProjectListener(IProjectListener listener) { 303 mProjectListeners.add(listener); 304 305 // we need to look at the opened projects and give them to the listener. 306 307 // get the list of opened android projects. 308 IWorkspaceRoot workspaceRoot = mWorkspace.getRoot(); 309 IJavaModel javaModel = JavaCore.create(workspaceRoot); 310 IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(javaModel); 311 312 for (IJavaProject androidProject : androidProjects) { 313 listener.projectOpenedWithWorkspace(androidProject.getProject()); 314 } 315 } 316 317 /** 318 * Removes an existing project listener. 319 * @param listener the listener to remove. 320 */ removeProjectListener(IProjectListener listener)321 public synchronized void removeProjectListener(IProjectListener listener) { 322 mProjectListeners.remove(listener); 323 } 324 325 /** 326 * Adds a resource event listener. 327 * @param listener The listener to receive the events. 328 */ addResourceEventListener(IResourceEventListener listener)329 public synchronized void addResourceEventListener(IResourceEventListener listener) { 330 mEventListeners.add(listener); 331 } 332 333 /** 334 * Removes an existing Resource Event listener. 335 * @param listener the listener to remove. 336 */ removeResourceEventListener(IResourceEventListener listener)337 public synchronized void removeResourceEventListener(IResourceEventListener listener) { 338 mEventListeners.remove(listener); 339 } 340 341 /** 342 * Processes the workspace resource change events. 343 */ resourceChanged(IResourceChangeEvent event)344 public void resourceChanged(IResourceChangeEvent event) { 345 // notify the event listeners of a start. 346 for (IResourceEventListener listener : mEventListeners) { 347 listener.resourceChangeEventStart(); 348 } 349 350 if (event.getType() == IResourceChangeEvent.PRE_DELETE) { 351 // a project is being deleted. Lets get the project object and remove 352 // its compiled resource list. 353 IResource r = event.getResource(); 354 IProject project = r.getProject(); 355 356 // notify the listeners. 357 for (IProjectListener pl : mProjectListeners) { 358 pl.projectDeleted(project); 359 } 360 } else { 361 // this a regular resource change. We get the delta and go through it with a visitor. 362 IResourceDelta delta = event.getDelta(); 363 364 DeltaVisitor visitor = new DeltaVisitor(); 365 try { 366 delta.accept(visitor); 367 } catch (CoreException e) { 368 } 369 } 370 371 // we're done, notify the event listeners. 372 for (IResourceEventListener listener : mEventListeners) { 373 listener.resourceChangeEventEnd(); 374 } 375 } 376 377 } 378