diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java | 1620 |
1 files changed, 0 insertions, 1620 deletions
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 deleted file mode 100644 index 7ff06fc40..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ /dev/null @@ -1,1620 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Eclipse Public License, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.eclipse.org/org/documents/epl-v10.php - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.ide.eclipse.adt.internal.sdk; - -import static com.android.SdkConstants.DOT_XML; -import static com.android.SdkConstants.EXT_JAR; -import static com.android.SdkConstants.FD_RES; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ddmlib.IDevice; -import com.android.ide.common.rendering.LayoutLibrary; -import com.android.ide.common.sdk.LoadStatus; -import com.android.ide.eclipse.adt.AdtConstants; -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.internal.build.DexWrapper; -import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; -import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; -import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; -import com.android.ide.eclipse.adt.internal.project.LibraryClasspathContainerInitializer; -import com.android.ide.eclipse.adt.internal.project.ProjectHelper; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IFileListener; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IProjectListener; -import com.android.ide.eclipse.adt.internal.resources.manager.GlobalProjectMonitor.IResourceEventListener; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryDifference; -import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState; -import com.android.io.StreamException; -import com.android.prefs.AndroidLocation.AndroidLocationException; -import com.android.sdklib.AndroidVersion; -import com.android.sdklib.BuildToolInfo; -import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkManager; -import com.android.sdklib.devices.DeviceManager; -import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.internal.project.ProjectProperties.PropertyType; -import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy; -import com.android.sdklib.repository.FullRevision; -import com.android.utils.ILogger; -import com.google.common.collect.Maps; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IMarkerDelta; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceDelta; -import org.eclipse.core.resources.IncrementalProjectBuilder; -import org.eclipse.core.resources.ResourcesPlugin; -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.QualifiedName; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.ui.IEditorDescriptor; -import org.eclipse.ui.IEditorInput; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IEditorReference; -import org.eclipse.ui.IFileEditorInput; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchPartSite; -import org.eclipse.ui.IWorkbenchWindow; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.ide.IDE; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Central point to load, manipulate and deal with the Android SDK. Only one SDK can be used - * at the same time. - * - * To start using an SDK, call {@link #loadSdk(String)} which returns the instance of - * the Sdk object. - * - * To get the list of platforms or add-ons present in the SDK, call {@link #getTargets()}. - */ -public final class Sdk { - private final static boolean DEBUG = false; - - private final static Object LOCK = new Object(); - - private static Sdk sCurrentSdk = null; - - /** - * Map associating {@link IProject} and their state {@link ProjectState}. - * <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>(); - - /** - * Data bundled using during the load of Target data. - * <p/>This contains the {@link LoadStatus} and a list of projects that attempted - * to compile before the loading was finished. Those projects will be recompiled - * at the end of the loading. - */ - private final static class TargetLoadBundle { - LoadStatus status; - final HashSet<IJavaProject> projectsToReload = new HashSet<IJavaProject>(); - } - - private final SdkManager mManager; - private final Map<String, DexWrapper> mDexWrappers = Maps.newHashMap(); - private final AvdManager mAvdManager; - private final DeviceManager mDeviceManager; - - /** 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 its {@link TargetLoadBundle}. */ - private final HashMap<IAndroidTarget, TargetLoadBundle> mTargetDataStatusMap = - new HashMap<IAndroidTarget, TargetLoadBundle>(); - - /** - * If true the target data will never load anymore. The only way to reload them is to - * completely reload the SDK with {@link #loadSdk(String)} - */ - private boolean mDontLoadTargetData = false; - - private final String mDocBaseUrl; - - /** - * Classes implementing this interface will receive notification when targets are changed. - */ - public interface ITargetChangeListener { - /** - * Sent when project has its target changed. - */ - void onProjectTargetChange(IProject changedProject); - - /** - * Called when the targets are loaded (either the SDK finished loading when Eclipse starts, - * or the SDK is changed). - */ - 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(); - - @Override - public void onProjectTargetChange(IProject changedProject) { - if (changedProject != null && changedProject.equals(getProject())) { - reload(); - } - } - - @Override - public void onTargetLoaded(IAndroidTarget target) { - IProject project = getProject(); - if (target != null && target.equals(Sdk.getCurrent().getTarget(project))) { - reload(); - } - } - - @Override - public void onSdkLoaded() { - // do nothing; - } - } - - /** - * Returns the lock object used to synchronize all operations dealing with SDK, targets and - * projects. - */ - @NonNull - public static final Object getLock() { - return LOCK; - } - - /** - * 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. - */ - @Nullable - public static Sdk loadSdk(String sdkLocation) { - synchronized (LOCK) { - if (sCurrentSdk != null) { - sCurrentSdk.dispose(); - sCurrentSdk = null; - } - - final AtomicBoolean hasWarning = new AtomicBoolean(); - final AtomicBoolean hasError = new AtomicBoolean(); - final ArrayList<String> logMessages = new ArrayList<String>(); - ILogger log = new ILogger() { - @Override - public void error(@Nullable Throwable throwable, @Nullable String errorFormat, - Object... arg) { - hasError.set(true); - if (errorFormat != null) { - logMessages.add(String.format("Error: " + errorFormat, arg)); - } - - if (throwable != null) { - logMessages.add(throwable.getMessage()); - } - } - - @Override - public void warning(@NonNull String warningFormat, Object... arg) { - hasWarning.set(true); - logMessages.add(String.format("Warning: " + warningFormat, arg)); - } - - @Override - public void info(@NonNull String msgFormat, Object... arg) { - logMessages.add(String.format(msgFormat, arg)); - } - - @Override - public void verbose(@NonNull String msgFormat, Object... arg) { - info(msgFormat, arg); - } - }; - - // get an SdkManager object for the location - SdkManager manager = SdkManager.createManager(sdkLocation, log); - try { - if (manager == null) { - hasError.set(true); - } else { - // create the AVD Manager - AvdManager avdManager = null; - try { - avdManager = AvdManager.getInstance(manager.getLocalSdk(), log); - } catch (AndroidLocationException e) { - log.error(e, "Error parsing the AVDs"); - } - sCurrentSdk = new Sdk(manager, avdManager); - return sCurrentSdk; - } - } finally { - if (hasError.get() || hasWarning.get()) { - StringBuilder sb = new StringBuilder( - String.format("%s when loading the SDK:\n", - hasError.get() ? "Error" : "Warning")); - for (String msg : logMessages) { - sb.append('\n'); - sb.append(msg); - } - if (hasError.get()) { - AdtPlugin.printErrorToConsole("Android SDK", sb.toString()); - AdtPlugin.displayError("Android SDK", sb.toString()); - } else { - AdtPlugin.printToConsole("Android SDK", sb.toString()); - } - } - } - return null; - } - } - - /** - * Returns the current {@link Sdk} object. - */ - @Nullable - public static Sdk getCurrent() { - synchronized (LOCK) { - return sCurrentSdk; - } - } - - /** - * Returns the location of the current SDK as an OS path string. - * Guaranteed to be terminated by a platform-specific path separator. - * <p/> - * Due to {@link File} canonicalization, this MAY differ from the string used to initialize - * the SDK path. - * - * @return The SDK OS path or null if no SDK is setup. - * @deprecated Consider using {@link #getSdkFileLocation()} instead. - * @see #getSdkFileLocation() - */ - @Deprecated - @Nullable - public String getSdkOsLocation() { - String path = mManager == null ? null : mManager.getLocation(); - if (path != null) { - // For backward compatibility make sure it ends with a separator. - // This used to be the case when the SDK Manager was created from a String path - // but now that a File is internally used the trailing dir separator is lost. - if (path.length() > 0 && !path.endsWith(File.separator)) { - path = path + File.separator; - } - } - return path; - } - - /** - * Returns the location of the current SDK as a {@link File} or null. - * - * @return The SDK OS path or null if no SDK is setup. - */ - @Nullable - public File getSdkFileLocation() { - if (mManager == null || mManager.getLocalSdk() == null) { - return null; - } - return mManager.getLocalSdk().getLocation(); - } - - /** - * Returns a <em>new</em> {@link SdkManager} that can parse the SDK located - * at the current {@link #getSdkOsLocation()}. - * <p/> - * Implementation detail: The {@link Sdk} has its own internal manager with - * a custom logger which is not designed to be useful for outsiders. Callers - * who need their own {@link SdkManager} for parsing will often want to control - * the logger for their own need. - * <p/> - * This is just a convenient method equivalent to writing: - * <pre>SdkManager.createManager(Sdk.getCurrent().getSdkLocation(), log);</pre> - * - * @param log The logger for the {@link SdkManager}. - * @return A new {@link SdkManager} parsing the same location. - */ - public @Nullable SdkManager getNewSdkManager(@NonNull ILogger log) { - return SdkManager.createManager(getSdkOsLocation(), log); - } - - /** - * Returns the URL to the local documentation. - * Can return null if no documentation is found in the current SDK. - * - * @return A file:// URL on the local documentation folder if it exists or null. - */ - @Nullable - public String getDocumentationBaseUrl() { - return mDocBaseUrl; - } - - /** - * Returns the list of targets that are available in the SDK. - */ - public IAndroidTarget[] getTargets() { - return mManager.getTargets(); - } - - /** - * Queries the underlying SDK Manager to check whether the platforms or addons - * directories have changed on-disk. Does not reload the SDK. - * <p/> - * This is a quick test based on the presence of the directories, their timestamps - * and a quick checksum of the source.properties files. It's possible to have - * false positives (e.g. if a file is manually modified in a platform) or false - * negatives (e.g. if a platform data file is changed manually in a 2nd level - * directory without altering the source.properties.) - */ - public boolean haveTargetsChanged() { - return mManager.hasChanged(); - } - - /** - * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}. - * - * @param hash the {@link IAndroidTarget} hash string. - * @return The matching {@link IAndroidTarget} or null. - */ - @Nullable - public IAndroidTarget getTargetFromHashString(@NonNull String hash) { - return mManager.getTargetFromHashString(hash); - } - - @Nullable - public BuildToolInfo getBuildToolInfo(@Nullable String buildToolVersion) { - if (buildToolVersion != null) { - try { - return mManager.getBuildTool(FullRevision.parseRevision(buildToolVersion)); - } catch (Exception e) { - // ignore, return null below. - } - } - - return null; - } - - @Nullable - public BuildToolInfo getLatestBuildTool() { - return mManager.getLatestBuildTool(); - } - - /** - * Initializes a new project with a target. This creates the <code>project.properties</code> - * file. - * @param project the project to initialize - * @param target the project's target. - * @throws IOException if creating the file failed in any way. - * @throws StreamException if processing the project property file fails - */ - public void initProject(@Nullable IProject project, @Nullable IAndroidTarget target) - throws IOException, StreamException { - if (project == null || target == null) { - return; - } - - synchronized (LOCK) { - // check if there's already a state? - ProjectState state = getProjectState(project); - - ProjectPropertiesWorkingCopy properties = null; - - if (state != null) { - properties = state.getProperties().makeWorkingCopy(); - } - - if (properties == null) { - IPath location = project.getLocation(); - if (location == null) { // can return null when the project is being deleted. - // do nothing and return null; - return; - } - - properties = ProjectProperties.create(location.toOSString(), PropertyType.PROJECT); - } - - // save the target hash string in the project persistent property - properties.setProperty(ProjectProperties.PROPERTY_TARGET, target.hashString()); - properties.save(); - } - } - - /** - * Returns the {@link ProjectState} object associated with a given project. - * <p/> - * This method is the only way to properly get the project's {@link ProjectState} - * If the project has not yet been loaded, then it is loaded. - * <p/>Because this methods deals with projects, it's not linked to an actual {@link Sdk} - * objects, and therefore is static. - * <p/>The value returned by {@link ProjectState#getTarget()} will change as {@link Sdk} objects - * are replaced. - * @param project the request project - * @return the ProjectState for the project. - */ - @Nullable - @SuppressWarnings("deprecation") - public static ProjectState getProjectState(IProject project) { - if (project == null) { - return null; - } - - synchronized (LOCK) { - ProjectState state = sProjectStateMap.get(project); - if (state == null) { - // load the project.properties from the project folder. - IPath location = project.getLocation(); - if (location == null) { // can return null when the project is being deleted. - // do nothing and return null; - return null; - } - - String projectLocation = location.toOSString(); - - ProjectProperties properties = ProjectProperties.load(projectLocation, - PropertyType.PROJECT); - if (properties == null) { - // legacy support: look for default.properties and rename it if needed. - properties = ProjectProperties.load(projectLocation, - PropertyType.LEGACY_DEFAULT); - - if (properties == null) { - AdtPlugin.log(IStatus.ERROR, - "Failed to load properties file for project '%s'", - project.getName()); - return null; - } else { - //legacy mode. - // get a working copy with the new type "project" - ProjectPropertiesWorkingCopy wc = properties.makeWorkingCopy( - PropertyType.PROJECT); - // and save it - try { - wc.save(); - - // delete the old file. - ProjectProperties.delete(projectLocation, PropertyType.LEGACY_DEFAULT); - - // make sure to use the new properties - properties = ProjectProperties.load(projectLocation, - PropertyType.PROJECT); - } catch (Exception e) { - AdtPlugin.log(IStatus.ERROR, - "Failed to rename properties file to %1$s for project '%s2$'", - PropertyType.PROJECT.getFilename(), project.getName()); - } - } - } - - state = new ProjectState(project, properties); - sProjectStateMap.put(project, state); - - // try to resolve the target - if (AdtPlugin.getDefault().getSdkLoadStatus() == LoadStatus.LOADED) { - sCurrentSdk.loadTargetAndBuildTools(state); - } - } - - return state; - } - } - - /** - * Returns the {@link IAndroidTarget} object associated with the given {@link IProject}. - */ - @Nullable - public IAndroidTarget getTarget(IProject project) { - if (project == null) { - return null; - } - - ProjectState state = getProjectState(project); - if (state != null) { - return state.getTarget(); - } - - return null; - } - - /** - * Loads the {@link IAndroidTarget} and BuildTools for a given project. - * <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. - */ - @Nullable - public IAndroidTarget loadTargetAndBuildTools(ProjectState state) { - IAndroidTarget target = null; - if (state != null) { - String hash = state.getTargetHashString(); - if (hash != null) { - state.setTarget(target = getTargetFromHashString(hash)); - } - - String markerMessage = null; - String buildToolInfoVersion = state.getBuildToolInfoVersion(); - if (buildToolInfoVersion != null) { - BuildToolInfo buildToolsInfo = getBuildToolInfo(buildToolInfoVersion); - - if (buildToolsInfo != null) { - state.setBuildToolInfo(buildToolsInfo); - } else { - markerMessage = String.format("Unable to resolve %s property value '%s'", - ProjectProperties.PROPERTY_BUILD_TOOLS, - buildToolInfoVersion); - } - } else { - // this is ok, we'll use the latest one automatically. - state.setBuildToolInfo(null); - } - - handleBuildToolsMarker(state.getProject(), markerMessage); - } - - return target; - } - - /** - * Adds or edit a build tools marker from the given project. This is done through a Job. - * @param project the project - * @param markerMessage the message. if null the marker is removed. - */ - private void handleBuildToolsMarker(final IProject project, final String markerMessage) { - Job markerJob = new Job("Android SDK: Build Tools Marker") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - if (project.isAccessible()) { - // always delete existing marker first - project.deleteMarkers(AdtConstants.MARKER_BUILD_TOOLS, true, - IResource.DEPTH_ZERO); - - // add the new one if needed. - if (markerMessage != null) { - BaseProjectHelper.markProject(project, - AdtConstants.MARKER_BUILD_TOOLS, - markerMessage, IMarker.SEVERITY_ERROR, - IMarker.PRIORITY_HIGH); - } - } - } catch (CoreException e2) { - AdtPlugin.log(e2, null); - // Don't return e2.getStatus(); the job control will then produce - // a popup with this error, which isn't very interesting for the - // user. - } - - return Status.OK_STATUS; - } - }; - - // build jobs are run after other interactive jobs - markerJob.setPriority(Job.BUILD); - markerJob.setRule(ResourcesPlugin.getWorkspace().getRoot()); - markerJob.schedule(); - } - - /** - * 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)}. - * <p/>An optional project as second parameter can be given to be recompiled once the target - * data is finished loading. - * <p/>The return value is non-null only if the target data has already been loaded (and in this - * case is the status of the load operation) - * @param target the target to load. - * @param project an optional project to be recompiled when the target data is loaded. - * If the target is already loaded, nothing happens. - * @return The load status if the target data is already loaded. - */ - @NonNull - public LoadStatus checkAndLoadTargetData(final IAndroidTarget target, IJavaProject project) { - boolean loadData = false; - - synchronized (LOCK) { - if (mDontLoadTargetData) { - return LoadStatus.FAILED; - } - - TargetLoadBundle bundle = mTargetDataStatusMap.get(target); - if (bundle == null) { - bundle = new TargetLoadBundle(); - mTargetDataStatusMap.put(target,bundle); - - // set status to loading - bundle.status = LoadStatus.LOADING; - - // add project to bundle - if (project != null) { - bundle.projectsToReload.add(project); - } - - // and set the flag to start the loading below - loadData = true; - } else if (bundle.status == LoadStatus.LOADING) { - // add project to bundle - if (project != null) { - bundle.projectsToReload.add(project); - } - - return bundle.status; - } else if (bundle.status == LoadStatus.LOADED || bundle.status == LoadStatus.FAILED) { - return bundle.status; - } - } - - if (loadData) { - Job job = new Job(String.format("Loading data for %1$s", target.getFullName())) { - @Override - protected IStatus run(IProgressMonitor monitor) { - AdtPlugin plugin = AdtPlugin.getDefault(); - try { - IStatus status = new AndroidTargetParser(target).run(monitor); - - IJavaProject[] javaProjectArray = null; - - synchronized (LOCK) { - TargetLoadBundle bundle = mTargetDataStatusMap.get(target); - - if (status.getCode() != IStatus.OK) { - bundle.status = LoadStatus.FAILED; - bundle.projectsToReload.clear(); - } else { - bundle.status = LoadStatus.LOADED; - - // Prepare the array of project to recompile. - // The call is done outside of the synchronized block. - javaProjectArray = bundle.projectsToReload.toArray( - new IJavaProject[bundle.projectsToReload.size()]); - - // and update the UI of the editors that depend on the target data. - plugin.updateTargetListeners(target); - } - } - - if (javaProjectArray != null) { - ProjectHelper.updateProjects(javaProjectArray); - } - - return status; - } catch (Throwable t) { - synchronized (LOCK) { - TargetLoadBundle bundle = mTargetDataStatusMap.get(target); - bundle.status = LoadStatus.FAILED; - } - - AdtPlugin.log(t, "Exception in checkAndLoadTargetData."); //$NON-NLS-1$ - String message = String.format("Parsing Data for %1$s failed", target.hashString()); - if (t instanceof UnsupportedClassVersionError) { - message = "To use this platform, run Eclipse with JDK 7 or later. (" + message + ")"; - } - return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, message, t); - } - } - }; - job.setPriority(Job.BUILD); // build jobs are run after other interactive jobs - job.setRule(ResourcesPlugin.getWorkspace().getRoot()); - job.schedule(); - } - - // The only way to go through here is when the loading starts through the Job. - // Therefore the current status of the target is LOADING. - return LoadStatus.LOADING; - } - - /** - * Return the {@link AndroidTargetData} for a given {@link IAndroidTarget}. - */ - @Nullable - public AndroidTargetData getTargetData(IAndroidTarget target) { - synchronized (LOCK) { - return mTargetDataMap.get(target); - } - } - - /** - * Return the {@link AndroidTargetData} for a given {@link IProject}. - */ - @Nullable - public AndroidTargetData getTargetData(IProject project) { - synchronized (LOCK) { - IAndroidTarget target = getTarget(project); - if (target != null) { - return getTargetData(target); - } - } - - return null; - } - - /** - * Returns a {@link DexWrapper} object to be used to execute dx commands. If dx.jar was not - * loaded properly, then this will return <code>null</code>. - */ - @Nullable - public DexWrapper getDexWrapper(@Nullable BuildToolInfo buildToolInfo) { - if (buildToolInfo == null) { - return null; - } - synchronized (LOCK) { - String dexLocation = buildToolInfo.getPath(BuildToolInfo.PathId.DX_JAR); - DexWrapper dexWrapper = mDexWrappers.get(dexLocation); - - if (dexWrapper == null) { - // load DX. - dexWrapper = new DexWrapper(); - IStatus res = dexWrapper.loadDex(dexLocation); - if (res != Status.OK_STATUS) { - AdtPlugin.log(null, res.getMessage()); - dexWrapper = null; - } else { - mDexWrappers.put(dexLocation, dexWrapper); - } - } - - return dexWrapper; - } - } - - public void unloadDexWrappers() { - synchronized (LOCK) { - for (DexWrapper wrapper : mDexWrappers.values()) { - wrapper.unload(); - } - mDexWrappers.clear(); - } - } - - /** - * Returns the {@link AvdManager}. If the AvdManager failed to parse the AVD folder, this could - * be <code>null</code>. - */ - @Nullable - public AvdManager getAvdManager() { - return mAvdManager; - } - - @Nullable - public static AndroidVersion getDeviceVersion(@NonNull IDevice device) { - try { - Map<String, String> props = device.getProperties(); - String apiLevel = props.get(IDevice.PROP_BUILD_API_LEVEL); - if (apiLevel == null) { - return null; - } - - return new AndroidVersion(Integer.parseInt(apiLevel), - props.get((IDevice.PROP_BUILD_CODENAME))); - } catch (NumberFormatException e) { - return null; - } - } - - @NonNull - public DeviceManager getDeviceManager() { - return mDeviceManager; - } - - /** - * Returns a list of {@link ProjectState} representing projects depending, directly or - * indirectly on a given library project. - * @param project the library project. - * @return a possibly empty list of ProjectState. - */ - @NonNull - public static Set<ProjectState> getMainProjectsFor(IProject project) { - synchronized (LOCK) { - // first get the project directly depending on this. - Set<ProjectState> list = new HashSet<ProjectState>(); - - // loop on all project and see if ProjectState.getLibrary returns a non null - // project. - for (Entry<IProject, ProjectState> entry : sProjectStateMap.entrySet()) { - if (project != entry.getKey()) { - LibraryState library = entry.getValue().getLibrary(project); - if (library != null) { - list.add(entry.getValue()); - } - } - } - - // now look for projects depending on the projects directly depending on the library. - HashSet<ProjectState> result = new HashSet<ProjectState>(list); - for (ProjectState p : list) { - if (p.isLibrary()) { - Set<ProjectState> set = getMainProjectsFor(p.getProject()); - result.addAll(set); - } - } - - return result; - } - } - - /** - * Unload the SDK's target data. - * - * If <var>preventReload</var>, this effect is final until the SDK instance is changed - * through {@link #loadSdk(String)}. - * - * The goal is to unload the targets to be able to replace existing targets with new ones, - * before calling {@link #loadSdk(String)} to fully reload the SDK. - * - * @param preventReload prevent the data from being loaded again for the remaining live of - * this {@link Sdk} instance. - */ - public void unloadTargetData(boolean preventReload) { - synchronized (LOCK) { - mDontLoadTargetData = preventReload; - - // dispose of the target data. - for (AndroidTargetData data : mTargetDataMap.values()) { - data.dispose(); - } - - mTargetDataMap.clear(); - } - } - - private Sdk(SdkManager manager, AvdManager avdManager) { - mManager = manager; - mAvdManager = avdManager; - - // listen to projects closing - GlobalProjectMonitor monitor = GlobalProjectMonitor.getMonitor(); - // need to register the resource event listener first because the project listener - // is called back during registration with project opened in the workspace. - monitor.addResourceEventListener(mResourceEventListener); - monitor.addProjectListener(mProjectListener); - monitor.addFileListener(mFileListener, - IResourceDelta.CHANGED | IResourceDelta.ADDED | IResourceDelta.REMOVED); - - // pre-compute some paths - mDocBaseUrl = getDocumentationBaseUrl(manager.getLocation() + - SdkConstants.OS_SDK_DOCS_FOLDER); - - mDeviceManager = DeviceManager.createInstance(manager.getLocalSdk().getLocation(), - AdtPlugin.getDefault()); - - // update whatever ProjectState is already present with new IAndroidTarget objects. - synchronized (LOCK) { - for (Entry<IProject, ProjectState> entry: sProjectStateMap.entrySet()) { - loadTargetAndBuildTools(entry.getValue()); - } - } - } - - /** - * Cleans and unloads the SDK. - */ - private void dispose() { - GlobalProjectMonitor monitor = GlobalProjectMonitor.getMonitor(); - monitor.removeProjectListener(mProjectListener); - monitor.removeFileListener(mFileListener); - monitor.removeResourceEventListener(mResourceEventListener); - - // the IAndroidTarget objects are now obsolete so update the project states. - synchronized (LOCK) { - for (Entry<IProject, ProjectState> entry: sProjectStateMap.entrySet()) { - entry.getValue().setTarget(null); - } - - // dispose of the target data. - for (AndroidTargetData data : mTargetDataMap.values()) { - data.dispose(); - } - - mTargetDataMap.clear(); - } - } - - void setTargetData(IAndroidTarget target, AndroidTargetData data) { - synchronized (LOCK) { - mTargetDataMap.put(target, data); - } - } - - /** - * Returns the URL to the local documentation. - * Can return null if no documentation is found in the current SDK. - * - * @param osDocsPath Path to the documentation folder in the current SDK. - * The folder may not actually exist. - * @return A file:// URL on the local documentation folder if it exists or null. - */ - private String getDocumentationBaseUrl(String osDocsPath) { - File f = new File(osDocsPath); - - if (f.isDirectory()) { - try { - // Note: to create a file:// URL, one would typically use something like - // f.toURI().toURL().toString(). However this generates a broken path on - // Windows, namely "C:\\foo" is converted to "file:/C:/foo" instead of - // "file:///C:/foo" (i.e. there should be 3 / after "file:"). So we'll - // do the correct thing manually. - - String path = f.getAbsolutePath(); - if (File.separatorChar != '/') { - path = path.replace(File.separatorChar, '/'); - } - - // For some reason the URL class doesn't add the mandatory "//" after - // the "file:" protocol name, so it has to be hacked into the path. - URL url = new URL("file", null, "//" + path); //$NON-NLS-1$ //$NON-NLS-2$ - String result = url.toString(); - return result; - } catch (MalformedURLException e) { - // ignore malformed URLs - } - } - - return null; - } - - /** - * Delegate listener for project changes. - */ - private IProjectListener mProjectListener = new IProjectListener() { - @Override - public void projectClosed(IProject project) { - onProjectRemoved(project, false /*deleted*/); - } - - @Override - public void projectDeleted(IProject project) { - onProjectRemoved(project, true /*deleted*/); - } - - private void onProjectRemoved(IProject removedProject, boolean deleted) { - if (DEBUG) { - System.out.println(">>> CLOSED: " + removedProject.getName()); - } - - // get the target project - synchronized (LOCK) { - // Don't use getProject() as it could create the ProjectState if it's not - // there yet and this is not what we want. We want the current object. - // Therefore, direct access to the map. - ProjectState removedState = sProjectStateMap.get(removedProject); - if (removedState != null) { - // 1. clear the layout lib cache associated with this project - IAndroidTarget target = removedState.getTarget(); - if (target != null) { - // get the bridge for the target, and clear the cache for this project. - AndroidTargetData data = mTargetDataMap.get(target); - if (data != null) { - LayoutLibrary layoutLib = data.getLayoutLibrary(); - if (layoutLib != null && layoutLib.getStatus() == LoadStatus.LOADED) { - layoutLib.clearCaches(removedProject); - } - } - } - - // 2. if the project is a library, make sure to update the - // LibraryState for any project referencing it. - // Also, record the updated projects that are libraries, to update - // projects that depend on them. - for (ProjectState projectState : sProjectStateMap.values()) { - LibraryState libState = projectState.getLibrary(removedProject); - if (libState != null) { - // Close the library right away. - // This remove links between the LibraryState and the projectState. - // This is because in case of a rename of a project, projectClosed and - // projectOpened will be called before any other job is run, so we - // need to make sure projectOpened is closed with the main project - // state up to date. - libState.close(); - - // record that this project changed, and in case it's a library - // that its parents need to be updated as well. - markProject(projectState, projectState.isLibrary()); - } - } - - // now remove the project for the project map. - sProjectStateMap.remove(removedProject); - } - } - - if (DEBUG) { - System.out.println("<<<"); - } - } - - @Override - public void projectOpened(IProject project) { - onProjectOpened(project); - } - - @Override - public void projectOpenedWithWorkspace(IProject project) { - // no need to force recompilation when projects are opened with the workspace. - onProjectOpened(project); - } - - @Override - public void allProjectsOpenedWithWorkspace() { - // Correct currently open editors - fixOpenLegacyEditors(); - } - - private void onProjectOpened(final IProject openedProject) { - - ProjectState openedState = getProjectState(openedProject); - if (openedState != null) { - if (DEBUG) { - System.out.println(">>> OPENED: " + openedProject.getName()); - } - - synchronized (LOCK) { - final boolean isLibrary = openedState.isLibrary(); - final boolean hasLibraries = openedState.hasLibraries(); - - if (isLibrary || hasLibraries) { - boolean foundLibraries = false; - // loop on all the existing project and update them based on this new - // project - for (ProjectState projectState : sProjectStateMap.values()) { - if (projectState != openedState) { - // If the project has libraries, check if this project - // is a reference. - if (hasLibraries) { - // ProjectState#needs() both checks if this is a missing library - // and updates LibraryState to contains the new values. - // This must always be called. - LibraryState libState = openedState.needs(projectState); - - if (libState != null) { - // found a library! Add the main project to the list of - // modified project - foundLibraries = true; - } - } - - // if the project is a library check if the other project depend - // on it. - if (isLibrary) { - // ProjectState#needs() both checks if this is a missing library - // and updates LibraryState to contains the new values. - // This must always be called. - LibraryState libState = projectState.needs(openedState); - - if (libState != null) { - // There's a dependency! Add the project to the list of - // modified project, but also to a list of projects - // that saw one of its dependencies resolved. - markProject(projectState, projectState.isLibrary()); - } - } - } - } - - // if the project has a libraries and we found at least one, we add - // the project to the list of modified project. - // Since we already went through the parent, no need to update them. - if (foundLibraries) { - markProject(openedState, false /*updateParents*/); - } - } - } - - // Correct file editor associations. - fixEditorAssociations(openedProject); - - // Fix classpath entries in a job since the workspace might be locked now. - Job fixCpeJob = new Job("Adjusting Android Project Classpath") { - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - ProjectHelper.fixProjectClasspathEntries( - JavaCore.create(openedProject)); - } catch (JavaModelException e) { - AdtPlugin.log(e, "error fixing classpath entries"); - // Don't return e2.getStatus(); the job control will then produce - // a popup with this error, which isn't very interesting for the - // user. - } - - return Status.OK_STATUS; - } - }; - - // build jobs are run after other interactive jobs - fixCpeJob.setPriority(Job.BUILD); - fixCpeJob.setRule(ResourcesPlugin.getWorkspace().getRoot()); - fixCpeJob.schedule(); - - - if (DEBUG) { - System.out.println("<<<"); - } - } - } - - @Override - public void projectRenamed(IProject project, IPath from) { - // we don't actually care about this anymore. - } - }; - - /** - * Delegate listener for file changes. - */ - private IFileListener mFileListener = new IFileListener() { - @Override - public void fileChanged(final @NonNull IFile file, @NonNull IMarkerDelta[] markerDeltas, - int kind, @Nullable String extension, int flags, boolean isAndroidPRoject) { - if (!isAndroidPRoject) { - return; - } - - if (SdkConstants.FN_PROJECT_PROPERTIES.equals(file.getName()) && - file.getParent() == file.getProject()) { - try { - // reload the content of the project.properties file and update - // the target. - IProject iProject = file.getProject(); - - ProjectState state = Sdk.getProjectState(iProject); - - // get the current target and build tools - IAndroidTarget oldTarget = state.getTarget(); - boolean oldRsSupportMode = state.getRenderScriptSupportMode(); - - // get the current library flag - boolean wasLibrary = state.isLibrary(); - - LibraryDifference diff = state.reloadProperties(); - - // load the (possibly new) target. - IAndroidTarget newTarget = loadTargetAndBuildTools(state); - - // reload the libraries if needed - if (diff.hasDiff()) { - if (diff.added) { - synchronized (LOCK) { - for (ProjectState projectState : sProjectStateMap.values()) { - if (projectState != state) { - // need to call needs to do the libraryState link, - // but no need to look at the result, as we'll compare - // the result of getFullLibraryProjects() - // this is easier to due to indirect dependencies. - state.needs(projectState); - } - } - } - } - - markProject(state, wasLibrary || state.isLibrary()); - } - - // apply the new target if needed. - if (newTarget != oldTarget || - oldRsSupportMode != state.getRenderScriptSupportMode()) { - IJavaProject javaProject = BaseProjectHelper.getJavaProject( - file.getProject()); - if (javaProject != null) { - ProjectHelper.updateProject(javaProject); - } - - // update the editors to reload with the new target - AdtPlugin.getDefault().updateTargetListeners(iProject); - } - } catch (CoreException e) { - // This can't happen as it's only for closed project (or non existing) - // but in that case we can't get a fileChanged on this file. - } - } else if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) { - // check if it's an add/remove on a jar files inside libs - if (EXT_JAR.equals(extension) && - file.getProjectRelativePath().segmentCount() == 2 && - file.getParent().getName().equals(SdkConstants.FD_NATIVE_LIBS)) { - // need to update the project and whatever depend on it. - - processJarFileChange(file); - } - } - } - - private void processJarFileChange(final IFile file) { - try { - IProject iProject = file.getProject(); - - if (iProject.hasNature(AdtConstants.NATURE_DEFAULT) == false) { - return; - } - - List<IJavaProject> projectList = new ArrayList<IJavaProject>(); - IJavaProject javaProject = BaseProjectHelper.getJavaProject(iProject); - if (javaProject != null) { - projectList.add(javaProject); - } - - ProjectState state = Sdk.getProjectState(iProject); - - if (state != null) { - Collection<ProjectState> parents = state.getFullParentProjects(); - for (ProjectState s : parents) { - javaProject = BaseProjectHelper.getJavaProject(s.getProject()); - if (javaProject != null) { - projectList.add(javaProject); - } - } - - ProjectHelper.updateProjects( - projectList.toArray(new IJavaProject[projectList.size()])); - } - } catch (CoreException e) { - // This can't happen as it's only for closed project (or non existing) - // but in that case we can't get a fileChanged on this file. - } - } - }; - - /** List of modified projects. This is filled in - * {@link IProjectListener#projectOpened(IProject)}, - * {@link IProjectListener#projectOpenedWithWorkspace(IProject)}, - * {@link IProjectListener#projectClosed(IProject)}, and - * {@link IProjectListener#projectDeleted(IProject)} and processed in - * {@link IResourceEventListener#resourceChangeEventEnd()}. - */ - private final List<ProjectState> mModifiedProjects = new ArrayList<ProjectState>(); - private final List<ProjectState> mModifiedChildProjects = new ArrayList<ProjectState>(); - - private void markProject(ProjectState projectState, boolean updateParents) { - if (mModifiedProjects.contains(projectState) == false) { - if (DEBUG) { - System.out.println("\tMARKED: " + projectState.getProject().getName()); - } - mModifiedProjects.add(projectState); - } - - // if the project is resolved also add it to this list. - if (updateParents) { - if (mModifiedChildProjects.contains(projectState) == false) { - if (DEBUG) { - System.out.println("\tMARKED(child): " + projectState.getProject().getName()); - } - mModifiedChildProjects.add(projectState); - } - } - } - - /** - * Delegate listener for resource changes. This is called before and after any calls to the - * project and file listeners (for a given resource change event). - */ - private IResourceEventListener mResourceEventListener = new IResourceEventListener() { - @Override - public void resourceChangeEventStart() { - mModifiedProjects.clear(); - mModifiedChildProjects.clear(); - } - - @Override - public void resourceChangeEventEnd() { - if (mModifiedProjects.size() == 0) { - return; - } - - // first make sure all the parents are updated - updateParentProjects(); - - // for all modified projects, update their library list - // and gather their IProject - final List<IJavaProject> projectList = new ArrayList<IJavaProject>(); - for (ProjectState state : mModifiedProjects) { - state.updateFullLibraryList(); - projectList.add(JavaCore.create(state.getProject())); - } - - Job job = new Job("Android Library Update") { //$NON-NLS-1$ - @Override - protected IStatus run(IProgressMonitor monitor) { - LibraryClasspathContainerInitializer.updateProjects( - projectList.toArray(new IJavaProject[projectList.size()])); - - for (IJavaProject javaProject : projectList) { - try { - javaProject.getProject().build(IncrementalProjectBuilder.FULL_BUILD, - monitor); - } catch (CoreException e) { - // pass - } - } - return Status.OK_STATUS; - } - }; - job.setPriority(Job.BUILD); - job.setRule(ResourcesPlugin.getWorkspace().getRoot()); - job.schedule(); - } - }; - - /** - * Updates all existing projects with a given list of new/updated libraries. - * This loops through all opened projects and check if they depend on any of the given - * library project, and if they do, they are linked together. - */ - private void updateParentProjects() { - if (mModifiedChildProjects.size() == 0) { - return; - } - - ArrayList<ProjectState> childProjects = new ArrayList<ProjectState>(mModifiedChildProjects); - mModifiedChildProjects.clear(); - synchronized (LOCK) { - // for each project for which we must update its parent, we loop on the parent - // projects and adds them to the list of modified projects. If they are themselves - // libraries, we add them too. - for (ProjectState state : childProjects) { - if (DEBUG) { - System.out.println(">>> Updating parents of " + state.getProject().getName()); - } - List<ProjectState> parents = state.getParentProjects(); - for (ProjectState parent : parents) { - markProject(parent, parent.isLibrary()); - } - if (DEBUG) { - System.out.println("<<<"); - } - } - } - - // done, but there may be parents that are also libraries. Need to update their parents. - updateParentProjects(); - } - - /** - * Fix editor associations for the given project, if not already done. - * <p/> - * Eclipse has a per-file setting for which editor should be used for each file - * (see {@link IDE#setDefaultEditor(IFile, String)}). - * We're using this flag to pick between the various XML editors (layout, drawable, etc) - * since they all have the same file name extension. - * <p/> - * Unfortunately, the file setting can be "wrong" for two reasons: - * <ol> - * <li> The editor type was added <b>after</b> a file had been seen by the IDE. - * For example, we added new editors for animations and for drawables around - * ADT 12, but any file seen by ADT in earlier versions will continue to use - * the vanilla Eclipse XML editor instead. - * <li> A bug in ADT 14 and ADT 15 (see issue 21124) meant that files created in new - * folders would end up with wrong editor associations. Even though that bug - * is fixed in ADT 16, the fix only affects new files, it cannot retroactively - * fix editor associations that were set incorrectly by ADT 14 or 15. - * </ol> - * <p/> - * This method attempts to fix the editor bindings retroactively by scanning all the - * resource XML files and resetting the editor associations. - * Since this is a potentially slow operation, this is only done "once"; we use a - * persistent project property to avoid looking repeatedly. In the future if we add - * additional editors, we can rev the scanned version value. - */ - private void fixEditorAssociations(final IProject project) { - QualifiedName KEY = new QualifiedName(AdtPlugin.PLUGIN_ID, "editorbinding"); //$NON-NLS-1$ - - try { - String value = project.getPersistentProperty(KEY); - int currentVersion = 0; - if (value != null) { - try { - currentVersion = Integer.parseInt(value); - } catch (Exception ingore) { - } - } - - // The target version we're comparing to. This must be incremented each time - // we change the processing here so that a new version of the plugin would - // try to fix existing user projects. - final int targetVersion = 2; - - if (currentVersion >= targetVersion) { - return; - } - - // Set to specific version such that we can rev the version in the future - // to trigger further scanning - project.setPersistentProperty(KEY, Integer.toString(targetVersion)); - - // Now update the actual editor associations. - Job job = new Job("Update Android editor bindings") { //$NON-NLS-1$ - @Override - protected IStatus run(IProgressMonitor monitor) { - try { - for (IResource folderResource : project.getFolder(FD_RES).members()) { - if (folderResource instanceof IFolder) { - IFolder folder = (IFolder) folderResource; - - for (IResource resource : folder.members()) { - if (resource instanceof IFile && - resource.getName().endsWith(DOT_XML)) { - fixXmlFile((IFile) resource); - } - } - } - } - - // TODO change AndroidManifest.xml ID too - - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - - return Status.OK_STATUS; - } - - /** - * Attempt to fix the editor ID for the given /res XML file. - */ - private void fixXmlFile(final IFile file) { - // Fix the default editor ID for this resource. - // This has no effect on currently open editors. - IEditorDescriptor desc = IDE.getDefaultEditor(file); - - if (desc == null || !CommonXmlEditor.ID.equals(desc.getId())) { - IDE.setDefaultEditor(file, CommonXmlEditor.ID); - } - } - }; - job.setPriority(Job.BUILD); - job.schedule(); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - } - - /** - * Tries to fix all currently open Android legacy editors. - * <p/> - * If an editor is found to match one of the legacy ids, we'll try to close it. - * If that succeeds, we try to reopen it using the new common editor ID. - * <p/> - * This method must be run from the UI thread. - */ - private void fixOpenLegacyEditors() { - - AdtPlugin adt = AdtPlugin.getDefault(); - if (adt == null) { - return; - } - - final IPreferenceStore store = adt.getPreferenceStore(); - int currentValue = store.getInt(AdtPrefs.PREFS_FIX_LEGACY_EDITORS); - // The target version we're comparing to. This must be incremented each time - // we change the processing here so that a new version of the plugin would - // try to fix existing editors. - final int targetValue = 1; - - if (currentValue >= targetValue) { - return; - } - - // To be able to close and open editors we need to make sure this is done - // in the UI thread, which this isn't invoked from. - PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - HashSet<String> legacyIds = - new HashSet<String>(Arrays.asList(CommonXmlEditor.LEGACY_EDITOR_IDS)); - - for (IWorkbenchWindow win : PlatformUI.getWorkbench().getWorkbenchWindows()) { - for (IWorkbenchPage page : win.getPages()) { - for (IEditorReference ref : page.getEditorReferences()) { - try { - IEditorInput input = ref.getEditorInput(); - if (input instanceof IFileEditorInput) { - IFile file = ((IFileEditorInput)input).getFile(); - IEditorPart part = ref.getEditor(true /*restore*/); - if (part != null) { - IWorkbenchPartSite site = part.getSite(); - if (site != null) { - String id = site.getId(); - if (legacyIds.contains(id)) { - // This editor matches one of legacy editor IDs. - fixEditor(page, part, input, file, id); - } - } - } - } - } catch (Exception e) { - // ignore - } - } - } - } - - // Remember that we managed to do fix all editors - store.setValue(AdtPrefs.PREFS_FIX_LEGACY_EDITORS, targetValue); - } - - private void fixEditor( - IWorkbenchPage page, - IEditorPart part, - IEditorInput input, - IFile file, - String id) { - IDE.setDefaultEditor(file, CommonXmlEditor.ID); - - boolean ok = page.closeEditor(part, true /*save*/); - - AdtPlugin.log(IStatus.INFO, - "Closed legacy editor ID %s for %s: %s", //$NON-NLS-1$ - id, - file.getFullPath(), - ok ? "Success" : "Failed");//$NON-NLS-1$ //$NON-NLS-2$ - - if (ok) { - // Try to reopen it with the new ID - try { - page.openEditor(input, CommonXmlEditor.ID); - } catch (PartInitException e) { - AdtPlugin.log(e, - "Failed to reopen %s", //$NON-NLS-1$ - file.getFullPath()); - } - } - } - }); - } -} |