1 /* 2 * Copyright (C) 2010 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; 18 19 import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController; 20 import com.android.ide.eclipse.adt.internal.project.ProjectHelper; 21 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor; 22 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener; 23 import com.android.ide.eclipse.ddms.IDebuggerConnector; 24 25 import org.eclipse.core.resources.IProject; 26 import org.eclipse.core.runtime.IPath; 27 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.Map; 31 import java.util.Map.Entry; 32 import java.util.Set; 33 34 /** 35 * Implementation of the com.android.ide.ddms.debuggerConnector extension point. 36 */ 37 public class DebuggerConnector implements IDebuggerConnector { 38 private WorkspaceAppCache mWorkspaceAppCache; 39 40 public DebuggerConnector() { 41 mWorkspaceAppCache = new WorkspaceAppCache(); 42 GlobalProjectMonitor.getMonitor().addProjectListener(mWorkspaceAppCache); 43 } 44 45 @Override 46 public boolean connectDebugger(String appName, int appPort, int selectedPort) { 47 // search for an android project matching the process name 48 IProject project = ProjectHelper.findAndroidProjectByAppName(appName); 49 if (project != null) { 50 AndroidLaunchController.debugRunningApp(project, appPort); 51 return true; 52 } 53 54 return false; 55 } 56 57 /** {@inheritDoc} */ 58 @Override 59 public boolean isWorkspaceApp(String appName) { 60 return mWorkspaceAppCache.isWorkspaceApp(appName); 61 } 62 63 /** 64 * A cache of Android application name to workspace project name mappings. 65 * Users can query whether an application is present in the workspace using 66 * {@link #isWorkspaceApp(String)}. The cache listens to workspace changes 67 * (project open/close/delete etc), and keeps its internal mappings up-to-date.<br> 68 * This class is designed with the following assumptions: 69 * <ul> 70 * <li> There are a significant number of calls to {@link #isWorkspaceApp(String)}, with 71 * a large number of possible application names as arguments. </li> 72 * <li> The number of projects actually present in the workspace, and the 73 * number of user initiated project changes (project open/close/delete/rename) are both 74 * relatively small. </li> 75 * </ul> 76 */ 77 static class WorkspaceAppCache implements IProjectListener { 78 /** Apps known to be not in the workspace. */ 79 private Set<String> mAppsNotInWorkspace; 80 81 /** Mapping of application name to project name for apps present in the workspace. */ 82 private Map<String, String> mAppsInWorkspace; 83 84 public WorkspaceAppCache() { 85 mAppsNotInWorkspace = new HashSet<String>(); 86 mAppsInWorkspace = new HashMap<String, String>(); 87 } 88 89 public boolean isWorkspaceApp(String appName) { 90 if (mAppsNotInWorkspace.contains(appName)) { 91 return false; 92 } 93 94 String projectName = mAppsInWorkspace.get(appName); 95 if (projectName == null) { 96 IProject p = ProjectHelper.findAndroidProjectByAppName(appName); 97 if (p == null) { 98 mAppsNotInWorkspace.add(appName); 99 } else { 100 projectName = p.getName(); 101 mAppsInWorkspace.put(appName, projectName); 102 } 103 } 104 return projectName != null; 105 } 106 107 /** {@inheritDoc} */ 108 @Override 109 public void projectRenamed(IProject project, IPath from) { 110 // when a project is renamed, ideally we should just update the current 111 // known mapping of app name -> project name. However, the projectRenamed 112 // callback is called only after projectDeleted() and projectOpened() are called, 113 // so there is really nothing we can do here.. 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 public void projectOpenedWithWorkspace(IProject project) { 119 // don't do anything as the cache is lazily initialized 120 } 121 122 @Override 123 public void allProjectsOpenedWithWorkspace() { 124 // don't do anything as the cache is lazily initialized 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public void projectOpened(IProject project) { 130 // A newly opened project could contribute some Android application. 131 // So we invalidate the set of apps that are known to be not in the workspace, as 132 // that set could be out of date now. 133 mAppsNotInWorkspace.clear(); 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public void projectDeleted(IProject project) { 139 // Deletion is effectively the same as closing 140 projectClosed(project); 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public void projectClosed(IProject project) { 146 // When a project is closed, remove all mappings contributed by the project. 147 Map<String, String> updatedCache = new HashMap<String, String>(); 148 149 String name = project.getName(); 150 for (Entry<String, String> e : mAppsInWorkspace.entrySet()) { 151 if (!e.getValue().equals(name)) { 152 updatedCache.put(e.getKey(), e.getValue()); 153 } 154 } 155 156 mAppsInWorkspace = updatedCache; 157 } 158 } 159 } 160