OSDN Git Service

Merge "Add support for multiple instrumentation test result listeners."
authorBrett Chabot <brettchabot@android.com>
Wed, 10 Mar 2010 20:57:57 +0000 (12:57 -0800)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Wed, 10 Mar 2010 20:57:57 +0000 (12:57 -0800)
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/PreCompilerBuilder.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutReloadMonitor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/FolderDecorator.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectState.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectResources.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java

index f52e9c4..508a86f 100644 (file)
@@ -987,7 +987,7 @@ public class AdtPlugin extends AbstractUIPlugin {
                                     // project that have been resolved before the sdk was loaded
                                     // will have a ProjectState where the IAndroidTarget is null
                                     // so we load the target now that the SDK is loaded.
-                                    sdk.loadTarget(Sdk.getProject(iProject));
+                                    sdk.loadTarget(Sdk.getProjectState(iProject));
                                     list.add(javaProject);
                                 }
                             }
index 6ecde37..2b35806 100644 (file)
@@ -275,7 +275,7 @@ public class ApkBuilder extends BaseBuilder {
 
         try {
             // get the project info
-            ProjectState projectState = Sdk.getProject(project);
+            ProjectState projectState = Sdk.getProjectState(project);
 
             // get the libraries
             libProjects = projectState.getLibraryProjects();
@@ -409,7 +409,7 @@ public class ApkBuilder extends BaseBuilder {
             }
 
             // get the APK configs for the project.
-            ProjectState state = Sdk.getProject(project);
+            ProjectState state = Sdk.getProjectState(project);
             Set<Entry<String, String>> apkfilters = null;
             if (state != null) {
                 ApkSettings apkSettings = state.getApkSettings();
index 0764346..46ee68f 100644 (file)
@@ -219,7 +219,7 @@ public class PreCompilerBuilder extends BaseBuilder {
             mDerivedProgressMonitor.reset();
 
             // get the project info
-            ProjectState projectState = Sdk.getProject(project);
+            ProjectState projectState = Sdk.getProjectState(project);
 
             // this can happen if the project has no default.properties.
             if (projectState == null) {
index c3c1a71..f36d001 100644 (file)
@@ -228,7 +228,7 @@ public final class LayoutReloadMonitor {
 
                 // check if the project is a library, and if it is search for what other
                 // project depends on this one (directly or not)
-                ProjectState state = Sdk.getProject(project);
+                ProjectState state = Sdk.getProjectState(project);
                 if (state != null && state.isLibrary()) {
                     Set<ProjectState> mainProjects = Sdk.getMainProjectsFor(project);
                     for (ProjectState mainProject : mainProjects) {
index b03f041..aef8692 100644 (file)
@@ -162,7 +162,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
                 boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED;
 
                 // check if the project has a valid target.
-                ProjectState state = Sdk.getProject(iProject);
+                ProjectState state = Sdk.getProjectState(iProject);
                 if (state == null) {
                     // looks like the project state (default.properties) couldn't be read!
                     markerMessage = String.format(
@@ -515,10 +515,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
             // project that have been resolved before the sdk was loaded
             // will have a ProjectState where the IAndroidTarget is null
             // so we load the target now that the SDK is loaded.
-            currentSdk.loadTarget(Sdk.getProject(iProject));
-
-            // get the target from the project and its paths
-            IAndroidTarget target = currentSdk.getTarget(javaProject.getProject());
+            IAndroidTarget target = currentSdk.loadTarget(Sdk.getProjectState(iProject));
             if (target == null) {
                 // this is really not supposed to happen. This would mean there are cached paths,
                 // but default.properties was deleted. Keep the project in the list to force
index 5f4e22e..2a37476 100644 (file)
@@ -18,6 +18,8 @@ package com.android.ide.eclipse.adt.internal.project;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryState;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.sdklib.SdkConstants;
 
 import org.eclipse.core.resources.IFolder;
@@ -35,7 +37,7 @@ import org.eclipse.jface.viewers.ILightweightLabelDecorator;
  * This is used to add android icons in some special folders in the package explorer.
  */
 public class FolderDecorator implements ILightweightLabelDecorator {
-    
+
     private ImageDescriptor mDescriptor;
 
     public FolderDecorator() {
@@ -45,7 +47,7 @@ public class FolderDecorator implements ILightweightLabelDecorator {
     public void decorate(Object element, IDecoration decoration) {
         if (element instanceof IFolder) {
             IFolder folder = (IFolder)element;
-            
+
             // get the project and make sure this is an android project
             IProject project = folder.getProject();
 
@@ -62,6 +64,12 @@ public class FolderDecorator implements ILightweightLabelDecorator {
                             doDecoration(decoration, " [Generated Java Files]");
                       } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) {
                           doDecoration(decoration, null);
+                      } else if (folder.isLinked()) {
+                          ProjectState state = Sdk.getProjectState(project);
+                          LibraryState lib = state.getLibrary(folder.getName());
+                          if (lib != null) {
+                              doDecoration(decoration, " [Android Library]");
+                          }
                         }
                     }
                 }
@@ -71,21 +79,13 @@ public class FolderDecorator implements ILightweightLabelDecorator {
             }
         }
     }
-    
+
     public void doDecoration(IDecoration decoration, String suffix) {
         decoration.addOverlay(mDescriptor, IDecoration.TOP_LEFT);
 
         if (suffix != null) {
             decoration.addSuffix(suffix);
-
-            // this is broken as it changes the color of the whole text, not only of the decoration.
-            // TODO: figure out how to change the color of the decoration only.
-//            ITheme theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme();
-//            ColorRegistry registry = theme.getColorRegistry();
-//            decoration.setForegroundColor(
-//                    registry.get("org.eclipse.jdt.ui.ColoredLabels.decorations")); //$NON-NLS-1$
         }
-
     }
 
     public boolean isLabelProperty(Object element, String property) {
index 348d2d6..ecedafb 100644 (file)
@@ -30,6 +30,7 @@ import org.eclipse.core.runtime.Status;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Centralized state for Android Eclipse project.
@@ -42,7 +43,7 @@ public final class ProjectState {
     /**
      * A class that represents a library linked to a project.
      * <p/>It does not represent the library uniquely. Instead the {@link LibraryState} is linked
-     * to the main project which is accessible through {@link #getMainProject()}.
+     * to the main project which is accessible through {@link #getMainProjectState()}.
      * <p/>If a library is used by two different projects, then there will be two different
      * instances of {@link LibraryState} for the library.
      *
@@ -50,7 +51,7 @@ public final class ProjectState {
      */
     public final class LibraryState {
         private String mRelativePath;
-        private IProject mProject;
+        private ProjectState mProjectState;
         private String mPath;
 
         private LibraryState(String relativePath) {
@@ -60,18 +61,18 @@ public final class ProjectState {
         /**
          * Returns the {@link ProjectState} of the main project using this library.
          */
-        public ProjectState getMainProject() {
+        public ProjectState getMainProjectState() {
             return ProjectState.this;
         }
 
         /**
-         * Closes the library. This resets the IProject from this object ({@link #getProject()} will
+         * Closes the library. This resets the IProject from this object ({@link #getProjectState()} will
          * return <code>null</code>), and updates the main project data so that the library
          * {@link IProject} object does not show up in the return value of
          * {@link ProjectState#getLibraryProjects()}.
          */
         public void close() {
-            mProject = null;
+            mProjectState = null;
             mPath = null;
 
             updateLibraries();
@@ -81,9 +82,9 @@ public final class ProjectState {
             mRelativePath = relativePath;
         }
 
-        private void setProject(IProject project) {
-            mProject = project;
-            mPath = project.getLocation().toOSString();
+        private void setProject(ProjectState project) {
+            mProjectState = project;
+            mPath = project.getProject().getLocation().toOSString();
 
             updateLibraries();
         }
@@ -97,11 +98,11 @@ public final class ProjectState {
         }
 
         /**
-         * Returns the {@link IProject} item for the library. This can be null if the project
+         * Returns the {@link ProjectState} item for the library. This can be null if the project
          * is not actually opened in Eclipse.
          */
-        public IProject getProject() {
-            return mProject;
+        public ProjectState getProjectState() {
+            return mProjectState;
         }
 
         /**
@@ -114,10 +115,36 @@ public final class ProjectState {
         public String getProjectLocation() {
             return mPath;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof LibraryState) {
+                // the only thing that's always non-null is the relative path.
+                LibraryState objState = (LibraryState)obj;
+                return mRelativePath.equals(objState.mRelativePath) &&
+                        getMainProjectState().equals(objState.getMainProjectState());
+            } else if (obj instanceof ProjectState || obj instanceof IProject) {
+                return mProjectState != null && mProjectState.equals(obj);
+            } else if (obj instanceof String) {
+                return mRelativePath.equals(obj);
+            }
+
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return mRelativePath.hashCode();
+        }
     }
 
     private final IProject mProject;
     private final ProjectProperties mProperties;
+    /**
+     * list of libraries. Access to this list must be protected by
+     * <code>synchronized(mLibraries)</code>, but it is important that such code do not call
+     * out to other classes (especially those protected by {@link Sdk#getLock()}.)
+     */
     private final ArrayList<LibraryState> mLibraries = new ArrayList<LibraryState>();
     private IAndroidTarget mTarget;
     private ApkSettings mApkSettings;
@@ -131,16 +158,18 @@ public final class ProjectState {
         mApkSettings = ApkConfigurationHelper.getSettings(properties);
 
         // load the libraries
-        int index = 1;
-        while (true) {
-            String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
-            String rootPath = mProperties.getProperty(propName);
+        synchronized (mLibraries) {
+            int index = 1;
+            while (true) {
+                String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
+                String rootPath = mProperties.getProperty(propName);
+
+                if (rootPath == null) {
+                    break;
+                }
 
-            if (rootPath == null) {
-                break;
+                mLibraries.add(new LibraryState(convertPath(rootPath)));
             }
-
-            mLibraries.add(new LibraryState(convertPath(rootPath)));
         }
     }
 
@@ -180,14 +209,77 @@ public final class ProjectState {
         return mTarget;
     }
 
+    public static class LibraryDifference {
+        public List<LibraryState> removed = new ArrayList<LibraryState>();
+        public boolean added = false;
+
+        public boolean hasDiff() {
+            return removed.size() > 0 || added;
+        }
+    }
+
     /**
      * Reloads the content of the properties.
      * <p/>This also reset the reference to the target as it may have changed.
      * <p/>This should be followed by a call to {@link Sdk#loadTarget(ProjectState)}.
+     *
+     * @return an instance of {@link LibraryDifference} describing the change in libraries.
      */
-    public void reloadProperties() {
+    public LibraryDifference reloadProperties() {
         mTarget = null;
         mProperties.reload();
+
+        // compare/reload the libraries.
+
+        // if the order change it won't impact the java part, so instead try to detect removed/added
+        // libraries.
+
+        LibraryDifference diff = new LibraryDifference();
+
+        synchronized (mLibraries) {
+            List<LibraryState> oldLibraries = new ArrayList<LibraryState>(mLibraries);
+            mLibraries.clear();
+
+            // load the libraries
+            int index = 1;
+            while (true) {
+                String propName = ProjectProperties.PROPERTY_LIB_REF + Integer.toString(index++);
+                String rootPath = mProperties.getProperty(propName);
+
+                if (rootPath == null) {
+                    break;
+                }
+
+                // search for a library with the same path (not exact same string, but going
+                // to the same folder).
+                String convertedPath = convertPath(rootPath);
+                boolean found = false;
+                for (int i = 0 ; i < oldLibraries.size(); i++) {
+                    LibraryState libState = oldLibraries.get(i);
+                    if (libState.equals(convertedPath)) {
+                        // it's a match. move it back to mLibraries and remove it from the
+                        // old library list.
+                        found = true;
+                        mLibraries.add(libState);
+                        oldLibraries.remove(i);
+                        break;
+                    }
+                }
+
+                if (found == false) {
+                    diff.added = true;
+                    mLibraries.add(new LibraryState(convertedPath));
+                }
+            }
+
+            // whatever's left in oldLibraries is removed.
+            diff.removed.addAll(oldLibraries);
+
+            // update the library with what IProjet are known at the time.
+            updateLibraries();
+        }
+
+        return diff;
     }
 
     public void setApkSettings(ApkSettings apkSettings) {
@@ -221,9 +313,11 @@ public final class ProjectState {
      * Returns whether the project is missing some required libraries.
      */
     public boolean isMissingLibraries() {
-        for (LibraryState state : mLibraries) {
-            if (state.getProject() == null) {
-                return true;
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState() == null) {
+                    return true;
+                }
             }
         }
 
@@ -240,46 +334,63 @@ public final class ProjectState {
      * @see #needs(IProject)
      */
     public LibraryState getLibrary(IProject library) {
-        for (LibraryState state : mLibraries) {
-            if (state.getProject() == library) {
-                return state;
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState().equals(library)) {
+                    return state;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public LibraryState getLibrary(String name) {
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState().getProject().getName().equals(name)) {
+                    return state;
+                }
             }
         }
 
         return null;
     }
 
+
     /**
      * Returns whether a given library project is needed by the receiver.
      * <p/>If the library is needed, this finds the matching {@link LibraryState}, initializes it
      * so that it contains the library's {@link IProject} object (so that
-     * {@link LibraryState#getProject()} does not return null) and then returns it.
+     * {@link LibraryState#getProjectState()} does not return null) and then returns it.
      *
      * @param libraryProject the library project to check.
      * @return a non null object if the project is a library dependency,
      * <code>null</code> otherwise.
      *
-     * @see LibraryState#getProject()
+     * @see LibraryState#getProjectState()
      */
-    public LibraryState needs(IProject libraryProject) {
+    public LibraryState needs(ProjectState libraryProject) {
         // compute current location
         File projectFile = new File(mProject.getLocation().toOSString());
 
         // get the location of the library.
-        File libraryFile = new File(libraryProject.getLocation().toOSString());
+        File libraryFile = new File(libraryProject.getProject().getLocation().toOSString());
 
         // loop on all libraries and check if the path match
-        for (LibraryState state : mLibraries) {
-            if (state.getProject() == null) {
-                File library = new File(projectFile, state.getRelativePath());
-                try {
-                    File absPath = library.getCanonicalFile();
-                    if (absPath.equals(libraryFile)) {
-                        state.setProject(libraryProject);
-                        return state;
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState() == null) {
+                    File library = new File(projectFile, state.getRelativePath());
+                    try {
+                        File absPath = library.getCanonicalFile();
+                        if (absPath.equals(libraryFile)) {
+                            state.setProject(libraryProject);
+                            return state;
+                        }
+                    } catch (IOException e) {
+                        // ignore this library
                     }
-                } catch (IOException e) {
-                    // ignore this library
                 }
             }
         }
@@ -301,51 +412,53 @@ public final class ProjectState {
      *
      * @param oldRelativePath the old library path relative to this project
      * @param newRelativePath the new library path relative to this project
-     * @param newLibraryProject the new {@link IProject} object.
+     * @param newLibraryState the new {@link ProjectState} object.
      * @return a non null object if the project depends on the library.
      *
-     * @see LibraryState#getProject()
+     * @see LibraryState#getProjectState()
      */
     public LibraryState updateLibrary(String oldRelativePath, String newRelativePath,
-            IProject newLibraryProject) {
+            ProjectState newLibraryState) {
         // compute current location
         File projectFile = new File(mProject.getLocation().toOSString());
 
         // loop on all libraries and check if the path matches
-        for (LibraryState state : mLibraries) {
-            if (state.getProject() == null) {
-                try {
-                    // oldRelativePath may not be the same exact string as the
-                    // one in the project properties (trailing separator could be different
-                    // for instance).
-                    // Use java.io.File to deal with this and also do a platform-dependent
-                    // path comparison
-                    File library1 = new File(projectFile, oldRelativePath);
-                    File library2 = new File(projectFile, state.getRelativePath());
-                    if (library1.getCanonicalPath().equals(library2.getCanonicalPath())) {
-                        // save the exact property string to replace.
-                        String oldProperty = state.getRelativePath();
-
-                        // then update the LibraryPath.
-                        state.setRelativePath(newRelativePath);
-                        state.setProject(newLibraryProject);
-
-                        // update the default.properties file
-                        IStatus status = replaceLibraryProperty(oldProperty, newRelativePath);
-                        if (status != null) {
-                            if (status.getSeverity() != IStatus.OK) {
-                                // log the error somehow.
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState() == null) {
+                    try {
+                        // oldRelativePath may not be the same exact string as the
+                        // one in the project properties (trailing separator could be different
+                        // for instance).
+                        // Use java.io.File to deal with this and also do a platform-dependent
+                        // path comparison
+                        File library1 = new File(projectFile, oldRelativePath);
+                        File library2 = new File(projectFile, state.getRelativePath());
+                        if (library1.getCanonicalPath().equals(library2.getCanonicalPath())) {
+                            // save the exact property string to replace.
+                            String oldProperty = state.getRelativePath();
+
+                            // then update the LibraryPath.
+                            state.setRelativePath(newRelativePath);
+                            state.setProject(newLibraryState);
+
+                            // update the default.properties file
+                            IStatus status = replaceLibraryProperty(oldProperty, newRelativePath);
+                            if (status != null) {
+                                if (status.getSeverity() != IStatus.OK) {
+                                    // log the error somehow.
+                                }
+                            } else {
+                                // This should not happen since the library wouldn't be here in the
+                                // first place
                             }
-                        } else {
-                            // This should not happen since the library wouldn't be here in the
-                            // first place
-                        }
 
-                        // return the LibraryState object.
-                        return state;
+                            // return the LibraryState object.
+                            return state;
+                        }
+                    } catch (IOException e) {
+                        // ignore this library
                     }
-                } catch (IOException e) {
-                    // ignore this library
                 }
             }
         }
@@ -382,9 +495,11 @@ public final class ProjectState {
 
     private void updateLibraries() {
         ArrayList<IProject> list = new ArrayList<IProject>();
-        for (LibraryState state : mLibraries) {
-            if (state.getProject() != null) {
-                list.add(state.getProject());
+        synchronized (mLibraries) {
+            for (LibraryState state : mLibraries) {
+                if (state.getProjectState() != null) {
+                    list.add(state.getProjectState().getProject());
+                }
             }
         }
 
@@ -397,4 +512,20 @@ public final class ProjectState {
     private String convertPath(String path) {
         return path.replaceAll("/", File.separator); //$NON-NLS-1$
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof ProjectState) {
+            return mProject.equals(((ProjectState) obj).mProject);
+        } else if (obj instanceof IProject) {
+            return mProject.equals(obj);
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mProject.hashCode();
+    }
 }
index ebbf7ef..b568f31 100644 (file)
@@ -382,7 +382,7 @@ public class ProjectResources implements IResourceRepository {
         // if the project contains libraries, we need to add the libraries resources here
         // so that they are accessible to the layout rendering.
         if (mProject != null) {
-            ProjectState state = Sdk.getProject(mProject);
+            ProjectState state = Sdk.getProjectState(mProject);
             if (state != null) {
                 IProject[] libraries = state.getLibraryProjects();
 
index 4519350..bc4f14b 100644 (file)
@@ -21,6 +21,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.project.AndroidClasspathContainerInitializer;
 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.ide.eclipse.adt.internal.project.ProjectState;
+import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryDifference;
 import com.android.ide.eclipse.adt.internal.project.ProjectState.LibraryState;
 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor;
 import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener;
@@ -52,12 +53,14 @@ import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.progress.IJobRunnable;
 
 import java.io.File;
 import java.io.IOException;
@@ -87,7 +90,7 @@ public final class Sdk  {
 
     /**
      * Map associating {@link IProject} and their state {@link ProjectState}.
-     * <p/>This <b>MUST NOT</b> be accessed directly. Instead use {@link #getProject(IProject)}.
+     * <p/>This <b>MUST NOT</b> be accessed directly. Instead use {@link #getProjectState(IProject)}.
      */
     private final static HashMap<IProject, ProjectState> sProjectStateMap =
             new HashMap<IProject, ProjectState>();
@@ -296,7 +299,7 @@ public final class Sdk  {
 
         synchronized (sLock) {
             // check if there's already a state?
-            ProjectState state = getProject(project);
+            ProjectState state = getProjectState(project);
 
             ProjectProperties properties = null;
 
@@ -337,7 +340,7 @@ public final class Sdk  {
         synchronized (sLock) {
             boolean resolveProject = false;
 
-            ProjectState state = getProject(project);
+            ProjectState state = getProjectState(project);
             if (state == null) {
                 return;
             }
@@ -411,7 +414,7 @@ public final class Sdk  {
      * @param project the request project
      * @return the ProjectState for the project.
      */
-    public static ProjectState getProject(IProject project) {
+    public static ProjectState getProjectState(IProject project) {
         if (project == null) {
             return null;
         }
@@ -455,7 +458,7 @@ public final class Sdk  {
             return null;
         }
 
-        ProjectState state = getProject(project);
+        ProjectState state = getProjectState(project);
         if (state != null) {
             return state.getTarget();
         }
@@ -468,12 +471,16 @@ public final class Sdk  {
      * <p/>This method will get the target hash string from the project properties, and resolve
      * it to an {@link IAndroidTarget} object and store it inside the {@link ProjectState}.
      * @param state the state representing the project to load.
+     * @return the target that was loaded.
      */
-    public void loadTarget(ProjectState state) {
+    public IAndroidTarget loadTarget(ProjectState state) {
+        IAndroidTarget target = null;
         String hash = state.getTargetHashString();
         if (hash != null) {
-            state.setTarget(getTargetFromHashString(hash));
+            state.setTarget(target = getTargetFromHashString(hash));
         }
+
+        return target;
     }
 
     /**
@@ -815,7 +822,7 @@ public final class Sdk  {
 
                             // edit the project to remove the linked source folder.
                             // this also calls LibraryState.close();
-                            unlinkLibrary(projectState, project);
+                            unlinkLibrary(projectState, project, true /*doInJob*/);
                         }
                     }
 
@@ -840,7 +847,7 @@ public final class Sdk  {
         }
 
         private void onProjectOpened(IProject openedProject, boolean recompile) {
-            ProjectState openedState = getProject(openedProject);
+            ProjectState openedState = getProjectState(openedProject);
             if (openedState != null) {
                 // find dependencies, if any
                 if (openedState.isMissingLibraries()) {
@@ -852,12 +859,12 @@ public final class Sdk  {
                     synchronized (sLock) {
                         for (ProjectState projectState : sProjectStateMap.values()) {
                             if (projectState != openedState) {
-                                LibraryState libState = openedState.needs(
-                                        projectState.getProject());
+                                LibraryState libState = openedState.needs(projectState);
 
                                 if (libState != null) {
                                     foundLibrary = true;
-                                    linkProjectAndLibrary(openedState, libState, null);
+                                    linkProjectAndLibrary(openedState, libState, null,
+                                            true /*doInJob*/);
                                 }
                             }
                         }
@@ -877,9 +884,10 @@ public final class Sdk  {
                     synchronized (sLock) {
                         for (ProjectState projectState : sProjectStateMap.values()) {
                             if (projectState != openedState && projectState.isMissingLibraries()) {
-                                LibraryState libState = projectState.needs(openedProject);
+                                LibraryState libState = projectState.needs(openedState);
                                 if (libState != null) {
-                                    linkProjectAndLibrary(projectState, libState, null);
+                                    linkProjectAndLibrary(projectState, libState, null,
+                                            true /*doInJob*/);
 
                                     // force a recompile of the main project through a job
                                     // (tree is locked)
@@ -898,7 +906,7 @@ public final class Sdk  {
             // a project was renamed.
             // if the project is a library, look for any project that depended on it
             // and update it. (default.properties and linked source folder)
-            ProjectState renamedState = getProject(project);
+            ProjectState renamedState = getProjectState(project);
             if (renamedState.isLibrary()) {
                 // remove the variable
                 disposeLibraryProject(from.lastSegment());
@@ -916,9 +924,10 @@ public final class Sdk  {
                             // update the library for the main project.
                             LibraryState libState = projectState.updateLibrary(
                                     oldRelativePath.toString(), newRelativePath.toString(),
-                                    project);
+                                    renamedState);
                             if (libState != null) {
-                                linkProjectAndLibrary(projectState, libState, from);
+                                linkProjectAndLibrary(projectState, libState, from,
+                                        true /*doInJob*/);
 
                                 // force a recompile of the main project through a job
                                 // (tree is locked)
@@ -947,15 +956,51 @@ public final class Sdk  {
                             // reload the content of the default.properties file and update
                             // the target.
                             IProject iProject = file.getProject();
-                            ProjectState state = Sdk.getProject(iProject);
-                            state.reloadProperties();
-                            loadTarget(state);
-
-                            IJavaProject javaProject = BaseProjectHelper.getJavaProject(
-                                    file.getProject());
-                            if (javaProject != null) {
-                                AndroidClasspathContainerInitializer.updateProjects(
-                                        new IJavaProject[] { javaProject });
+                            ProjectState state = Sdk.getProjectState(iProject);
+
+                            // get the current target
+                            IAndroidTarget oldTarget = state.getTarget();
+
+                            LibraryDifference diff = state.reloadProperties();
+
+                            // load the (possibly new) target.
+                            IAndroidTarget newTarget = loadTarget(state);
+
+                            // reload the libraries if needed
+                            if (diff.hasDiff()) {
+                                for (LibraryState removedState : diff.removed) {
+                                    unlinkLibrary(state,
+                                            removedState.getProjectState().getProject(),
+                                            false /*doInJob*/);
+                                }
+
+                                if (diff.added) {
+                                    synchronized (sLock) {
+                                        for (ProjectState projectState : sProjectStateMap.values()) {
+                                            if (projectState != state) {
+                                                LibraryState libState = state.needs(projectState);
+
+                                                if (libState != null) {
+                                                    linkProjectAndLibrary(state, libState, null,
+                                                            false /*doInJob*/);
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+
+                                // need to force a full recompile.
+                                iProject.build( IncrementalProjectBuilder.FULL_BUILD, monitor);
+                            }
+
+                            // apply the new target if needed.
+                            if (newTarget != oldTarget) {
+                                IJavaProject javaProject = BaseProjectHelper.getJavaProject(
+                                        file.getProject());
+                                if (javaProject != null) {
+                                    AndroidClasspathContainerInitializer.updateProjects(
+                                            new IJavaProject[] { javaProject });
+                                }
                             }
                         } catch (CoreException e) {
                             // This can't happen as it's only for closed project (or non existing)
@@ -1014,23 +1059,25 @@ public final class Sdk  {
 
     /**
      * Links a project and a library so that the project can use the library code and resources.
-     * <p/>This is done in a job to be sure that the workspace is not locked for resource
-     * modification.
+     * <p/>This can be done in a job in case the workspace is not locked for resource
+     * modification. See <var>doInJob</var>.
+     *
      * @param projectState the {@link ProjectState} for the main project
      * @param libraryState the {@link LibraryState} for the library project.
      * @param previousLibraryPath an optional old library path that needs to be removed at the
      * same time. Can be <code>null</code> in which case no libraries are removed.
+     * @param doInJob whether the action must be done in a new {@link Job}.
      */
     private void linkProjectAndLibrary(
             final ProjectState projectState,
             final LibraryState libraryState,
-            final IPath previousLibraryPath) {
-        Job job = new Job("Android Library link creation") { //$NON-NLS-1$
-            @Override
-            protected IStatus run(IProgressMonitor monitor) {
+            final IPath previousLibraryPath,
+            boolean doInJob) {
+        final IJobRunnable jobRunnable = new IJobRunnable() {
+            public IStatus run(IProgressMonitor monitor) {
                 try {
                     IProject project = projectState.getProject();
-                    IProject library = libraryState.getProject();
+                    IProject library = libraryState.getProjectState().getProject();
 
                     // add the library to the list of dynamic references
                     IProjectDescription projectDescription = project.getDescription();
@@ -1113,22 +1160,35 @@ public final class Sdk  {
                 }
             }
         };
-        job.setPriority(Job.BUILD);
-        job.schedule();
+
+        if (doInJob) {
+            Job job = new Job("Android Library link creation") { //$NON-NLS-1$
+                @Override
+                protected IStatus run(IProgressMonitor monitor) {
+                    return jobRunnable.run(monitor);
+                }
+            };
+            job.setPriority(Job.BUILD);
+            job.schedule();
+        } else {
+            jobRunnable.run(new NullProgressMonitor());
+        }
     }
 
     /**
      * Unlinks a project and a library. This removes the linked folder from the main project, and
      * removes it from the build path. Finally, this calls {@link LibraryState#close()}.
-     * <p/>This is done in a job to be sure that the workspace is not locked for resource
-     * modification.
+     * <p/>This can be done in a job in case the workspace is not locked for resource
+     * modification. See <var>doInJob</var>.
+     *
      * @param projectState the {@link ProjectState} for the main project
      * @param libraryProject the library project that needs to be removed
+     * @param doInJob whether the action must be done in a new {@link Job}.
      */
-    private void unlinkLibrary(final ProjectState projectState, final IProject libraryProject) {
-        Job job = new Job("Android Library unlinking") { //$NON-NLS-1$
-            @Override
-            protected IStatus run(IProgressMonitor monitor) {
+    private void unlinkLibrary(final ProjectState projectState, final IProject libraryProject,
+            boolean doInJob) {
+        final IJobRunnable jobRunnable = new IJobRunnable() {
+            public IStatus run(IProgressMonitor monitor) {
                 try {
                     IProject project = projectState.getProject();
 
@@ -1187,8 +1247,19 @@ public final class Sdk  {
             }
         };
 
-        job.setPriority(Job.BUILD);
-        job.schedule();
+        if (doInJob) {
+            Job job = new Job("Android Library unlinking") { //$NON-NLS-1$
+                @Override
+                protected IStatus run(IProgressMonitor monitor) {
+                    return jobRunnable.run(monitor);
+                }
+            };
+
+            job.setPriority(Job.BUILD);
+            job.schedule();
+        } else {
+            jobRunnable.run(new NullProgressMonitor());
+        }
     }
 
     /**
index 42476bd..43c62ba 100644 (file)
@@ -151,7 +151,7 @@ final class KeyCheckPage extends ExportWizardPage {
         if ((mProjectDataChanged & DATA_PROJECT) != 0) {
             // reset the destination from the content of the project
             IProject project = mWizard.getProject();
-            ProjectState state = Sdk.getProject(project);
+            ProjectState state = Sdk.getProjectState(project);
             if (state != null) {
                 mApkSettings = state.getApkSettings();
             }