diff options
author | Xavier Ducrohet <xav@android.com> | 2009-12-07 18:41:34 -0800 |
---|---|---|
committer | Xavier Ducrohet <xav@android.com> | 2009-12-07 18:56:29 -0800 |
commit | 2fb27a8d7f3c4636c775377cd09fa6b00c077440 (patch) | |
tree | 23dee375f6d5551882ea48accc54ee63428a391f /eclipse | |
parent | 977214b91d3c58ebddc55f4ac8efda1d7de01a0f (diff) | |
download | sdk-2fb27a8d7f3c4636c775377cd09fa6b00c077440.zip sdk-2fb27a8d7f3c4636c775377cd09fa6b00c077440.tar.gz sdk-2fb27a8d7f3c4636c775377cd09fa6b00c077440.tar.bz2 |
Load the SDK target data dynamically when a project requires it.
BUG 2303254
Change-Id: Iba88d526f50218bb57c41109ca1777bad441bb00
Diffstat (limited to 'eclipse')
14 files changed, 286 insertions, 149 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index 5d8b6c2..a081b82 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -39,7 +39,6 @@ import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceMonitor.IFileListener; -import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser; import com.android.ide.eclipse.adt.internal.sdk.LoadStatus; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; @@ -856,7 +855,7 @@ public class AdtPlugin extends AbstractUIPlugin { } /** - * Returns whether the Sdk has been loaded. + * Returns whether the {@link IAndroidTarget}s have been loaded from the SDK. */ public final LoadStatus getSdkLoadStatus() { synchronized (getSdkLockObject()) { @@ -1024,27 +1023,6 @@ public class AdtPlugin extends AbstractUIPlugin { if (sdk != null) { - progress.setTaskName(Messages.AdtPlugin_Parsing_Resources); - - final IAndroidTarget[] targets = sdk.getTargets(); - final int n = targets.length; - if (n > 0) { - // load the rest of the targets. - // TODO: make this on-demand. - int w = 60 / n; - for (IAndroidTarget target : targets) { - SubMonitor p2 = progress.newChild(w); - IStatus status = new AndroidTargetParser(target).run(p2); - if (status.getCode() != IStatus.OK) { - synchronized (getSdkLockObject()) { - mSdkIsLoaded = LoadStatus.FAILED; - mPostLoadProjectsToResolve.clear(); - } - return status; - } - } - } - ArrayList<IJavaProject> list = new ArrayList<IJavaProject>(); synchronized (getSdkLockObject()) { mSdkIsLoaded = LoadStatus.LOADED; @@ -1077,24 +1055,30 @@ public class AdtPlugin extends AbstractUIPlugin { } progress.worked(10); + } else { + // SDK failed to Load! + // Sdk#loadSdk() has already displayed an error. + synchronized (getSdkLockObject()) { + mSdkIsLoaded = LoadStatus.FAILED; + } } // Notify resource changed listeners progress.setTaskName("Refresh UI"); progress.setWorkRemaining(mTargetChangeListeners.size()); - // Clone the list before iterating, to avoid Concurrent Modification + // Clone the list before iterating, to avoid ConcurrentModification // exceptions final List<ITargetChangeListener> listeners = - (List<ITargetChangeListener>)mTargetChangeListeners.clone(); + (List<ITargetChangeListener>)mTargetChangeListeners.clone(); final SubMonitor progress2 = progress; AdtPlugin.getDisplay().syncExec(new Runnable() { public void run() { for (ITargetChangeListener listener : listeners) { try { - listener.onTargetsLoaded(); + listener.onSdkLoaded(); } catch (Exception e) { - AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ + AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ } finally { progress2.worked(1); } @@ -1353,11 +1337,11 @@ public class AdtPlugin extends AbstractUIPlugin { } /** - * Updates all the {@link ITargetChangeListener} that a target has changed for a given project. + * Updates all the {@link ITargetChangeListener}s that a target has changed for a given project. * <p/>Only editors related to that project should reload. */ @SuppressWarnings("unchecked") - public void updateTargetListener(final IProject project) { + public void updateTargetListeners(final IProject project) { final List<ITargetChangeListener> listeners = (List<ITargetChangeListener>)mTargetChangeListeners.clone(); @@ -1374,6 +1358,28 @@ public class AdtPlugin extends AbstractUIPlugin { }); } + /** + * Updates all the {@link ITargetChangeListener}s that a target data was loaded. + * <p/>Only editors related to a project using this target should reload. + */ + @SuppressWarnings("unchecked") + public void updateTargetListeners(final IAndroidTarget target) { + final List<ITargetChangeListener> listeners = + (List<ITargetChangeListener>)mTargetChangeListeners.clone(); + + AdtPlugin.getDisplay().asyncExec(new Runnable() { + public void run() { + for (ITargetChangeListener listener : listeners) { + try { + listener.onTargetLoaded(target); + } catch (Exception e) { + AdtPlugin.log(e, "Failed to update a TargetChangeListener."); //$NON-NLS-1$ + } + } + } + }); + } + public static synchronized OutputStream getErrorStream() { return sPlugin.mAndroidConsoleErrorStream; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/Messages.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/Messages.java index 33046dc..3288ddb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/Messages.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/Messages.java @@ -14,8 +14,6 @@ public class Messages extends NLS { public static String AdtPlugin_Failed_To_Start_s; - public static String AdtPlugin_Parsing_Resources; - public static String Console_Data_Project_Tag; public static String Console_Date_Tag; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ResourceManagerBuilder.java index a277e87..bc850a0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ResourceManagerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ResourceManagerBuilder.java @@ -64,7 +64,7 @@ public class ResourceManagerBuilder extends BaseBuilder { // Clear the project of the generic markers BaseBuilder.removeMarkersFromProject(project, AdtConstants.MARKER_ADT); - + // check for existing target marker, in which case we abort. // (this means: no SDK, no target, or unresolvable target.) abortOnBadSetup(project); @@ -86,7 +86,7 @@ public class ResourceManagerBuilder extends BaseBuilder { BaseProjectHelper.addMarker(project, AdtConstants.MARKER_ADT, errorMessage, IMarker.SEVERITY_ERROR); AdtPlugin.printErrorToConsole(project, errorMessage); - + // interrupt the build. The next builders will not run. stopBuild(errorMessage); } @@ -110,11 +110,11 @@ public class ResourceManagerBuilder extends BaseBuilder { // This interrupts the build. The next builders will not run. stopBuild("Project has no target"); } - + // check the 'gen' source folder is present boolean hasGenSrcFolder = false; // whether the project has a 'gen' source folder setup IJavaProject javaProject = JavaCore.create(project); - + IClasspathEntry[] classpaths = javaProject.readRawClasspath(); if (classpaths != null) { for (IClasspathEntry e : classpaths) { @@ -132,7 +132,7 @@ public class ResourceManagerBuilder extends BaseBuilder { boolean genFolderPresent = false; // whether the gen folder actually exists IResource resource = project.findMember(SdkConstants.FD_GEN_SOURCES); genFolderPresent = resource != null && resource.exists(); - + if (hasGenSrcFolder == false && genFolderPresent) { // No source folder setup for 'gen' in the project, but there's already a // 'gen' resource (file or folder). @@ -178,7 +178,7 @@ public class ResourceManagerBuilder extends BaseBuilder { new SubProgressMonitor(monitor, 10)); genFolder.setDerived(true); } - + // add it to the source folder list, if needed only (or it will throw) if (hasGenSrcFolder == false) { IClasspathEntry[] entries = javaProject.getRawClasspath(); @@ -186,7 +186,7 @@ public class ResourceManagerBuilder extends BaseBuilder { JavaCore.newSourceEntry(genFolder.getFullPath())); javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10)); } - + // refresh the whole project project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 10)); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java index 336b46a..e48a0c4 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidEditor.java @@ -20,7 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IFile; @@ -100,7 +100,7 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang private XmlModelStateListener mXmlModelStateListener; /** Listener to update the root node if the target of the file is changed because of a * SDK location change or a project target change */ - private ITargetChangeListener mTargetListener; + private TargetChangeListener mTargetListener; /** * Creates a form editor. @@ -109,14 +109,14 @@ public abstract class AndroidEditor extends FormEditor implements IResourceChang super(); ResourcesPlugin.getWorkspace().addResourceChangeListener(this); - mTargetListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - if (changedProject == getProject()) { - onTargetsLoaded(); - } + mTargetListener = new TargetChangeListener() { + @Override + public IProject getProject() { + return AndroidEditor.this.getProject(); } - public void onTargetsLoaded() { + @Override + public void reload() { commitPages(false /* onSave */); // recreate the ui root node always diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java index 302d3db..083ff55 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java @@ -33,7 +33,7 @@ import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener;
import com.android.layoutlib.api.ILayoutBridge;
import com.android.layoutlib.api.ILayoutLog;
import com.android.layoutlib.api.ILayoutResult;
@@ -546,15 +546,14 @@ public class GraphicalEditorPart extends EditorPart implements IGraphicalLayoutE /**
* Listens to target changed in the current project, to trigger a new layout rendering.
*/
- private class TargetListener implements ITargetChangeListener {
-
- public void onProjectTargetChange(IProject changedProject) {
- if (changedProject == getLayoutEditor().getProject()) {
- onTargetsLoaded();
- }
+ private class TargetListener extends TargetChangeListener {
+ @Override
+ public IProject getProject() {
+ return getLayoutEditor().getProject();
}
- public void onTargetsLoaded() {
+ @Override
+ public void reload() {
// because the SDK changed we must reset the configured framework resource.
mConfiguredFrameworkRes = null;
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java index 1d9054c..7e4311f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java @@ -41,6 +41,7 @@ import com.android.ide.eclipse.adt.internal.sdk.LoadStatus; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge; import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; import com.android.layoutlib.api.ILayoutBridge; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.ILayoutResult; @@ -143,14 +144,14 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** Listener to update the root node if the target of the file is changed because of a * SDK location change or a project target change */ - private ITargetChangeListener mTargetListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - if (changedProject == getLayoutEditor().getProject()) { - onTargetsLoaded(); - } - } + private ITargetChangeListener mTargetListener = new TargetChangeListener() { + @Override + public IProject getProject() { + return getLayoutEditor().getProject(); + }; - public void onTargetsLoaded() { + @Override + public void reload() { // because the SDK changed we must reset the configured framework resource. mConfiguredFrameworkRes = null; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java index 1641284..ca8d9f3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/ui/tree/UiTreeBlock.java @@ -26,6 +26,7 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.IUiUpdateListener; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; import org.eclipse.core.resources.IProject; import org.eclipse.jface.action.Action; @@ -299,14 +300,18 @@ public final class UiTreeBlock extends MasterDetailsBlock implements ICommitXml /** Listener to update the root node if the target of the file is changed because of a * SDK location change or a project target change */ - final ITargetChangeListener targetListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - if (changedProject == mEditor.getProject()) { - onTargetsLoaded(); + final ITargetChangeListener targetListener = new TargetChangeListener() { + @Override + public IProject getProject() { + if (mEditor != null) { + return mEditor.getProject(); } + + return null; } - public void onTargetsLoaded() { + @Override + public void reload() { // If a details part has been created, we need to "refresh" it too. if (mDetailsPart != null) { // The details part does not directly expose access to its internal diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java index 6f4772b..a884c05 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java @@ -204,11 +204,7 @@ public class AndroidPreferencePage extends FieldEditorPreferencePage implements } private class TargetChangedListener implements ITargetChangeListener { - public void onProjectTargetChange(IProject changedProject) { - // do nothing. - } - - public void onTargetsLoaded() { + public void onSdkLoaded() { if (mTargetSelector != null) { // We may not have an sdk if the sdk path pref is empty or not valid. Sdk sdk = Sdk.getCurrent(); @@ -217,6 +213,14 @@ public class AndroidPreferencePage extends FieldEditorPreferencePage implements mTargetSelector.setTargets(targets); } } + + public void onProjectTargetChange(IProject changedProject) { + // do nothing. + } + + public void onTargetLoaded(IAndroidTarget target) { + // do nothing. + } } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java index 44956bc..cc32b5c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.java @@ -68,13 +68,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit private final static String PROPERTY_TARGET_NAME = "androidTargetCache"; //$NON-NLS-1$ private final static String CACHE_VERSION = "01"; //$NON-NLS-1$ private final static String CACHE_VERSION_SEP = CACHE_VERSION + PATH_SEPARATOR; - + private final static int CACHE_INDEX_JAR = 0; private final static int CACHE_INDEX_SRC = 1; private final static int CACHE_INDEX_DOCS_URI = 2; private final static int CACHE_INDEX_OPT_DOCS_URI = 3; private final static int CACHE_INDEX_ADD_ON_START = CACHE_INDEX_OPT_DOCS_URI; - + public AndroidClasspathContainerInitializer() { // pass } @@ -111,7 +111,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit public static boolean checkPath(IPath path) { return CONTAINER_ID.equals(path.toString()); } - + /** * Updates the {@link IJavaProject} objects with new android framework container. This forces * JDT to recompile them. @@ -120,11 +120,11 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit */ public static boolean updateProjects(IJavaProject[] androidProjects) { try { - // Allocate a new AndroidClasspathContainer, and associate it to the android framework + // Allocate a new AndroidClasspathContainer, and associate it to the android framework // container id for each projects. // By providing a new association between a container id and a IClasspathContainer, // this forces the JDT to query the IClasspathContainer for new IClasspathEntry (with - // IClasspathContainer#getClasspathEntries()), and therefore force recompilation of + // IClasspathContainer#getClasspathEntries()), and therefore force recompilation of // the projects. int projectCount = androidProjects.length; @@ -137,7 +137,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit JavaCore.setClasspathContainer( new Path(CONTAINER_ID), androidProjects, containers, new NullProgressMonitor()); - + return true; } catch (JavaModelException e) { return false; @@ -154,15 +154,15 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit String markerMessage = null; boolean outputToConsole = true; - + try { AdtPlugin plugin = AdtPlugin.getDefault(); - + // get the lock object for project manipulation during SDK load. Object lock = plugin.getSdkLockObject(); synchronized (lock) { boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED; - + // check if the project has a valid target. IAndroidTarget target = null; if (sdkIsLoaded) { @@ -171,7 +171,9 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // if we are loaded and the target is non null, we create a valid ClassPathContainer if (sdkIsLoaded && target != null) { - + // first make sure the target has loaded its data + Sdk.getCurrent().checkAndLoadTargetData(target); + String targetName = target.getClasspathName(); return new AndroidClasspathContainer( @@ -202,13 +204,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // loaded and therefore we can't get the target yet. // We check if there is a cache of the needed information. AndroidClasspathContainer container = getContainerFromCache(iProject); - + if (container == null) { - // either the cache was wrong (ie folder does not exists anymore), or + // either the cache was wrong (ie folder does not exists anymore), or // there was no cache. In this case we need to make sure the project // is resolved again after the SDK is loaded. plugin.setProjectToResolve(javaProject); - + markerMessage = String.format( "Unable to resolve target '%s' until the SDK is loaded.", hashString); @@ -221,13 +223,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // we created a container from the cache, so we register the project // to be checked for cache validity once the SDK is loaded plugin.setProjectToCheck(javaProject); - + // and return the container return container; } - + } - + // return a dummy container to replace the one we may have had before. // It'll be replaced by the real when if/when the target is resolved if/when the // SDK finishes loading. @@ -255,7 +257,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit if (outputToConsole) { AdtPlugin.printErrorToConsole(iProject, markerMessage); } - + try { BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, markerMessage, -1, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH); @@ -325,19 +327,19 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit * The method also stores the paths used to create the entries in the project persistent * properties. A new {@link AndroidClasspathContainer} can be created from the stored path * using the {@link #getContainerFromCache(IProject)} method. - * @param project + * @param project * @param target The target that contains the libraries. - * @param targetName + * @param targetName */ private static IClasspathEntry[] createClasspathEntries(IProject project, IAndroidTarget target, String targetName) { - + // get the path from the target String[] paths = getTargetPaths(target); - + // create the classpath entry from the paths IClasspathEntry[] entries = createClasspathEntriesFromPaths(paths); - + // paths now contains all the path required to recreate the IClasspathEntry with no // target info. We encode them in a single string, with each path separated by // OS path separator. @@ -346,14 +348,14 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit sb.append(PATH_SEPARATOR); sb.append(p); } - + // store this in a project persistent property ProjectHelper.saveStringProperty(project, PROPERTY_CONTAINER_CACHE, sb.toString()); ProjectHelper.saveStringProperty(project, PROPERTY_TARGET_NAME, targetName); return entries; } - + /** * Generates an {@link AndroidClasspathContainer} from the project cache, if possible. */ @@ -364,14 +366,14 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit if (cache == null || targetNameCache == null) { return null; } - + // the first 2 chars must match CACHE_VERSION. The 3rd char is the normal separator. if (cache.startsWith(CACHE_VERSION_SEP) == false) { return null; } - + cache = cache.substring(CACHE_VERSION_SEP.length()); - + // the cache contains multiple paths, separated by a character guaranteed to not be in // the path (\u001C). // The first 3 are for android.jar (jar, source, doc), the rest are for the optional @@ -381,7 +383,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit if (paths.length < 3 || paths.length == 4) { return null; } - + // now we check the paths actually exist. // There's an exception: If the source folder for android.jar does not exist, this is // not a problem, so we skip it. @@ -392,10 +394,10 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit new File(new URI(paths[CACHE_INDEX_DOCS_URI])).exists() == false) { return null; } - + // check the path for the add-ons, if they exist. if (paths.length > CACHE_INDEX_ADD_ON_START) { - + // check the docs path separately from the rest of the paths as it's a URI. if (new File(new URI(paths[CACHE_INDEX_OPT_DOCS_URI])).exists() == false) { return null; @@ -421,14 +423,14 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit return new AndroidClasspathContainer(entries, new Path(CONTAINER_ID), targetNameCache); } - + /** * Generates an array of {@link IClasspathEntry} from a set of paths. * @see #getTargetPaths(IAndroidTarget) */ private static IClasspathEntry[] createClasspathEntriesFromPaths(String[] paths) { ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>(); - + // First, we create the IClasspathEntry for the framework. // now add the android framework to the class path. // create the path object. @@ -439,7 +441,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute( IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, paths[CACHE_INDEX_DOCS_URI]); - + // create the access rule to restrict access to classes in com.android.internal IAccessRule accessRule = JavaCore.newAccessRule( new Path("com/android/internal/**"), //$NON-NLS-1$ @@ -454,7 +456,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit ); list.add(frameworkClasspathEntry); - + // now deal with optional libraries if (paths.length >= 5) { String docPath = paths[CACHE_INDEX_OPT_DOCS_URI]; @@ -470,7 +472,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit docPath) }; } - + IClasspathEntry entry = JavaCore.newLibraryEntry( jarPath, null, // source attachment path @@ -482,7 +484,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit list.add(entry); } } - + return list.toArray(new IClasspathEntry[list.size()]); } @@ -495,7 +497,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit projectLoop: while (i < projects.size()) { IJavaProject javaProject = projects.get(i); IProject iProject = javaProject.getProject(); - + // check if the project is opened if (iProject.isOpen() == false) { // remove from the list @@ -514,9 +516,9 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit i++; continue; } - + String[] targetPaths = getTargetPaths(target); - + // now get the cached paths String cache = ProjectHelper.loadStringProperty(iProject, PROPERTY_CONTAINER_CACHE); if (cache == null) { @@ -524,23 +526,23 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit i++; continue; } - + String[] cachedPaths = cache.split(Pattern.quote(PATH_SEPARATOR)); if (cachedPaths.length < 3 || cachedPaths.length == 4) { // paths length is wrong. simply resolve the project again i++; continue; } - + // Now we compare the paths. The first 4 can be compared directly. // because of case sensitiveness we need to use File objects - + if (targetPaths.length != cachedPaths.length) { // different paths, force resolve again. i++; continue; } - + // compare the main paths (android.jar, main sources, main javadoc) if (new File(targetPaths[CACHE_INDEX_JAR]).equals( new File(cachedPaths[CACHE_INDEX_JAR])) == false || @@ -552,7 +554,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit i++; continue; } - + if (cachedPaths.length > CACHE_INDEX_OPT_DOCS_URI) { // compare optional libraries javadoc if (new File(targetPaths[CACHE_INDEX_OPT_DOCS_URI]).equals( @@ -561,7 +563,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit i++; continue; } - + // testing the optional jar files is a little bit trickier. // The order is not guaranteed to be identical. // From a previous test, we do know however that there is the same number. @@ -569,7 +571,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // lists manually. targetLoop: for (int tpi = 4 ; tpi < targetPaths.length; tpi++) { String targetPath = targetPaths[tpi]; - + // look for a match in the other array for (int cpi = 4 ; cpi < cachedPaths.length; cpi++) { if (new File(targetPath).equals(new File(cachedPaths[cpi]))) { @@ -577,7 +579,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit continue targetLoop; } } - + // if we stop here, we haven't found a match, which means there's a // discrepancy in the libraries. We force a resolve. i++; @@ -590,7 +592,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit projects.remove(i); } } - + /** * Returns the paths necessary to create the {@link IClasspathEntry} for this targets. * <p/>The paths are always in the same order. @@ -608,13 +610,13 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit */ private static String[] getTargetPaths(IAndroidTarget target) { ArrayList<String> paths = new ArrayList<String>(); - + // first, we get the path for android.jar // The order is: android.jar, source folder, docs folder paths.add(target.getPath(IAndroidTarget.ANDROID_JAR)); paths.add(target.getPath(IAndroidTarget.SOURCES)); paths.add(AdtPlugin.getUrlDoc()); - + // now deal with optional libraries. IOptionalLibrary[] libraries = target.getOptionalLibraries(); if (libraries != null) { @@ -626,7 +628,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit // we add an empty string, to always have the same count. paths.add(""); } - + // because different libraries could use the same jar file, we make sure we add // each jar file only once. HashSet<String> visitedJars = new HashSet<String>(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index e40a495..3fcd2a9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -73,12 +73,21 @@ public class Sdk implements IProjectListener, IFileListener { private final SdkManager mManager; private final AvdManager mAvdManager; + /** Map associating an {@link IAndroidTarget} to an {@link AndroidTargetData} */ + private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap = + new HashMap<IAndroidTarget, AndroidTargetData>(); + /** Map associating an {@link IAndroidTarget} and the {@link LoadStatus} of its + * {@link AndroidTargetData}. */ + private final HashMap<IAndroidTarget, LoadStatus> mTargetDataStatusMap = + new HashMap<IAndroidTarget, LoadStatus>(); + + /** Map associating {@link IProject} and their resolved {@link IAndroidTarget}. */ private final HashMap<IProject, IAndroidTarget> mProjectTargetMap = new HashMap<IProject, IAndroidTarget>(); - private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap = - new HashMap<IAndroidTarget, AndroidTargetData>(); + /** Map associating {@link IProject} and their APK creation settings ({@link ApkSettings}). */ private final HashMap<IProject, ApkSettings> mApkSettingsMap = new HashMap<IProject, ApkSettings>(); + private final String mDocBaseUrl; private final LayoutDeviceManager mLayoutDeviceManager = new LayoutDeviceManager(); @@ -96,11 +105,54 @@ public class Sdk implements IProjectListener, IFileListener { * Called when the targets are loaded (either the SDK finished loading when Eclipse starts, * or the SDK is changed). */ - void onTargetsLoaded(); + void onTargetLoaded(IAndroidTarget target); + + /** + * Called when the base content of the SDK is parsed. + */ + void onSdkLoaded(); + } + + /** + * Basic abstract implementation of the ITargetChangeListener for the case where both + * {@link #onProjectTargetChange(IProject)} and {@link #onTargetLoaded(IAndroidTarget)} + * use the same code based on a simple test requiring to know the current IProject. + */ + public static abstract class TargetChangeListener implements ITargetChangeListener { + /** + * Returns the {@link IProject} associated with the listener. + */ + public abstract IProject getProject(); + + /** + * Called when the listener needs to take action on the event. This is only called + * if {@link #getProject()} and the {@link IAndroidTarget} associated with the project + * match the values received in {@link #onProjectTargetChange(IProject)} and + * {@link #onTargetLoaded(IAndroidTarget)}. + */ + public abstract void reload(); + + public void onProjectTargetChange(IProject changedProject) { + if (changedProject != null && changedProject.equals(getProject())) { + reload(); + } + } + + public void onTargetLoaded(IAndroidTarget target) { + IProject project = getProject(); + if (target != null && target.equals(Sdk.getCurrent().getTarget(project))) { + reload(); + } + } + + public void onSdkLoaded() { + // do nothing; + } } /** * Loads an SDK and returns an {@link Sdk} object if success. + * <p/>If the SDK failed to load, it displays an error to the user. * @param sdkLocation the OS path to the SDK. */ public static Sdk loadSdk(String sdkLocation) { @@ -271,7 +323,7 @@ public class Sdk implements IProjectListener, IFileListener { // finally, update the opened editors. if (resolveProject) { - AdtPlugin.getDefault().updateTargetListener(project); + AdtPlugin.getDefault().updateTargetListeners(project); } } } @@ -280,6 +332,9 @@ public class Sdk implements IProjectListener, IFileListener { * Returns the {@link IAndroidTarget} object associated with the given {@link IProject}. */ public IAndroidTarget getTarget(IProject project) { + if (project == null) { + return null; + } synchronized (AdtPlugin.getDefault().getSdkLockObject()) { IAndroidTarget target = mProjectTargetMap.get(project); if (target == null) { @@ -376,6 +431,68 @@ public class Sdk implements IProjectListener, IFileListener { project.getName()); } } + + /** + * Checks and loads (if needed) the data for a given target. + * <p/> The data is loaded in a separate {@link Job}, and opened editors will be notified + * through their implementation of {@link ITargetChangeListener#onTargetLoaded(IAndroidTarget)}. + * @param target the target to load. + */ + public void checkAndLoadTargetData(final IAndroidTarget target) { + boolean loadData = false; + + synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + LoadStatus status = mTargetDataStatusMap.get(target); + if (status == null) { + // set status to loading + mTargetDataStatusMap.put(target, LoadStatus.LOADING); + // and set the flag to start the loading below + loadData = true; + } + } + + if (loadData) { + Job job = new Job(String.format("Loading data for %1$s", target.getFullName())) { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + IStatus status = new AndroidTargetParser(target).run(monitor); + AdtPlugin plugin = AdtPlugin.getDefault(); + synchronized (plugin.getSdkLockObject()) { + if (status.getCode() != IStatus.OK) { + mTargetDataStatusMap.put(target, LoadStatus.FAILED); + } else { + mTargetDataStatusMap.put(target, LoadStatus.LOADED); + plugin.updateTargetListeners(target); + } + } + return status; + } catch (Throwable t) { + AdtPlugin.log(t, "Exception in checkAndLoadTargetData."); //$NON-NLS-1$ + return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, + String.format( + "Parsing Data for %1$s failed", //$NON-NLS-1$ + target.hashString()), + t); + } + } + }; + job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs + job.schedule(); + } + } + + /** + * Returns the {@link LoadStatus} for the data of a given {@link IAndroidTarget}. + * @param target the target that is queried. + * @return the status or <code>null</code> if the data was not loaded. + */ + public LoadStatus getTargetDataLoadStatus(IAndroidTarget target) { + synchronized (AdtPlugin.getDefault().getSdkLockObject()) { + return mTargetDataStatusMap.get(target); + } + } + /** * Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}. */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java index 8d5cf27..bd212cb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectCreationPage.java @@ -525,11 +525,7 @@ public class NewProjectCreationPage extends WizardPage { mSdkTargetSelector = new SdkTargetSelector(group, null); mSdkTargetChangeListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - // Ignore - } - - public void onTargetsLoaded() { + public void onSdkLoaded() { // Update the sdk target selector with the new targets // get the targets from the sdk @@ -544,12 +540,20 @@ public class NewProjectCreationPage extends WizardPage { mSdkTargetSelector.setSelection(targets[0]); } } + + public void onProjectTargetChange(IProject changedProject) { + // Ignore + } + + public void onTargetLoaded(IAndroidTarget target) { + // Ignore + } }; AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); // Invoke it once to initialize the targets - mSdkTargetChangeListener.onTargetsLoaded(); + mSdkTargetChangeListener.onSdkLoaded(); mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java index 94fd99d..28855a2 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewTestProjectCreationPage.java @@ -557,11 +557,7 @@ public class NewTestProjectCreationPage extends WizardPage { mSdkTargetSelector = new SdkTargetSelector(group, null); mSdkTargetChangeListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - // Ignore - } - - public void onTargetsLoaded() { + public void onSdkLoaded() { // Update the sdk target selector with the new targets // get the targets from the sdk @@ -576,12 +572,20 @@ public class NewTestProjectCreationPage extends WizardPage { mSdkTargetSelector.setSelection(targets[0]); } } + + public void onProjectTargetChange(IProject changedProject) { + // Ignore + } + + public void onTargetLoaded(IAndroidTarget target) { + // Ignore + } }; AdtPlugin.getDefault().addTargetListener(mSdkTargetChangeListener); // Invoke it once to initialize the targets - mSdkTargetChangeListener.onTargetsLoaded(); + mSdkTargetChangeListener.onSdkLoaded(); mSdkTargetSelector.setSelectionListener(new SelectionAdapter() { @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java index 1e3c0df..4cedff2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileCreationPage.java @@ -30,7 +30,7 @@ import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQua import com.android.ide.eclipse.adt.internal.resources.manager.ResourceFolderType; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.Sdk; -import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; +import com.android.ide.eclipse.adt.internal.sdk.Sdk.TargetChangeListener; import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector; import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState; import com.android.sdklib.IAndroidTarget; @@ -292,7 +292,7 @@ class NewXmlFileCreationPage extends WizardPage { private boolean mInternalTypeUpdate; private boolean mInternalConfigSelectorUpdate; private ProjectChooserHelper mProjectChooserHelper; - private ITargetChangeListener mSdkTargetChangeListener; + private TargetChangeListener mSdkTargetChangeListener; private TypeInfo mCurrentTypeInfo; @@ -349,16 +349,14 @@ class NewXmlFileCreationPage extends WizardPage { } private void installTargetChangeListener() { - mSdkTargetChangeListener = new ITargetChangeListener() { - public void onProjectTargetChange(IProject changedProject) { - // If this is the current project, force it to reload its data - if (changedProject != null && changedProject == mProject) { - changeProject(mProject); - } + mSdkTargetChangeListener = new TargetChangeListener() { + @Override + public IProject getProject() { + return mProject; } - public void onTargetsLoaded() { - // Reload the current project, if any, in case its target has changed. + @Override + public void reload() { if (mProject != null) { changeProject(mProject); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties index 982bd84..5e38035 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/messages.properties @@ -11,7 +11,6 @@ VersionCheck_Unable_To_Parse_Version_s=Unable to parse sdk build version: %1$s VersionCheck_Plugin_Too_Old=This Android SDK requires Android Developer Toolkit version %1$d.%2$d.%3$d or above.\n\nCurrent version is %4$s.\n\nPlease update ADT to the latest version. AdtPlugin_Failed_To_Start_s=Failed to start %1$s AdtPlugin_Android_SDK_Content_Loader=Android SDK Content Loader -AdtPlugin_Parsing_Resources=Parsing Resources AdtPlugin_Android_SDK_Resource_Parser=Android SDK Resource Parser AdtPlugin_Failed_To_Parse_s=Failed to parse: Console_Date_Tag=[%1$tF %1$tT] |