Home | History | Annotate | Download | only in adt
      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