diff options
34 files changed, 1243 insertions, 1213 deletions
diff --git a/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java b/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java index 6901c98..84b96a5 100644 --- a/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java +++ b/draw9patch/src/com/android/draw9patch/ui/ImageEditorPanel.java @@ -478,6 +478,14 @@ class ImageEditorPanel extends JPanel { start = rect.x; } } + } else { + int start = -1; + for (Rectangle rect : patches) { + if (rect.x > start) { + horizontalPatchesSum += rect.width; + start = rect.x; + } + } } verticalPatchesSum = 0; @@ -489,6 +497,14 @@ class ImageEditorPanel extends JPanel { start = rect.y; } } + } else { + int start = -1; + for (Rectangle rect : patches) { + if (rect.y > start) { + verticalPatchesSum += rect.height; + start = rect.y; + } + } } setSize(size); @@ -528,8 +544,7 @@ class ImageEditorPanel extends JPanel { x = 0; y = 0; - if (patches.size() == 0 || horizontalPatches.size() == 0 || - verticalPatches.size() == 0) { + if (patches.size() == 0) { g.drawImage(image, 0, 0, scaledWidth, scaledHeight, null); g2.dispose(); return; @@ -1028,7 +1043,15 @@ class ImageEditorPanel extends JPanel { horizontalPatches = getRectangles(left.first, top.second); verticalPatches = getRectangles(left.second, top.first); } else { - horizontalPatches = verticalPatches = new ArrayList<Rectangle>(0); + if (top.first.size() > 0) { + horizontalPatches = new ArrayList<Rectangle>(0); + verticalPatches = getVerticalRectangles(top.first); + } else if (left.first.size() > 0) { + horizontalPatches = getHorizontalRectangles(left.first); + verticalPatches = new ArrayList<Rectangle>(0); + } else { + horizontalPatches = verticalPatches = new ArrayList<Rectangle>(0); + } } row = GraphicsUtilities.getPixels(image, 0, height - 1, width, 1, row); @@ -1041,6 +1064,28 @@ class ImageEditorPanel extends JPanel { verticalPadding = getPadding(left.first); } + private List<Rectangle> getVerticalRectangles(List<Pair<Integer>> topPairs) { + List<Rectangle> rectangles = new ArrayList<Rectangle>(); + for (Pair<Integer> top : topPairs) { + int x = top.first; + int width = top.second - top.first; + + rectangles.add(new Rectangle(x, 1, width, image.getHeight() - 2)); + } + return rectangles; + } + + private List<Rectangle> getHorizontalRectangles(List<Pair<Integer>> leftPairs) { + List<Rectangle> rectangles = new ArrayList<Rectangle>(); + for (Pair<Integer> left : leftPairs) { + int y = left.first; + int height = left.second - left.first; + + rectangles.add(new Rectangle(1, y, image.getWidth() - 2, height)); + } + return rectangles; + } + private Pair<Integer> getPadding(List<Pair<Integer>> pairs) { if (pairs.size() == 0) { return new Pair<Integer>(0, 0); @@ -1063,7 +1108,7 @@ class ImageEditorPanel extends JPanel { for (Pair<Integer> left : leftPairs) { int y = left.first; int height = left.second - left.first; - for (Pair<Integer> top: topPairs) { + for (Pair<Integer> top : topPairs) { int x = top.first; int width = top.second - top.first; @@ -1108,6 +1153,7 @@ class ImageEditorPanel extends JPanel { startWithPatch[0] = true; fixed.clear(); } + return new Pair<List<Pair<Integer>>>(fixed, patches); } diff --git a/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java b/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java index a62884f..f14cd77 100644 --- a/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java +++ b/draw9patch/src/com/android/draw9patch/ui/ImageTransferHandler.java @@ -36,25 +36,48 @@ class ImageTransferHandler extends TransferHandler { @Override public boolean importData(JComponent component, Transferable transferable) { try { - Object data = transferable.getTransferData(DataFlavor.javaFileListFlavor); - //noinspection unchecked - final File file = ((List<File>) data).get(0); - mainFrame.open(file).execute(); + for (DataFlavor flavor : transferable.getTransferDataFlavors()) { + if (flavor.isFlavorJavaFileListType()) { + Object data = transferable.getTransferData(DataFlavor.javaFileListFlavor); + //noinspection unchecked + final File file = ((List<File>) data).get(0); + mainFrame.open(file).execute(); + return true; + } else if (flavor.isFlavorTextType()) { + if (flavor.getRepresentationClass() == String.class) { + String mime = flavor.getMimeType(); + DataFlavor flave = new DataFlavor(mime); + Object data = transferable.getTransferData(flave); + final String path = convertPath(data.toString()); + mainFrame.open(new File(path)).execute(); + return true; + } + } + } } catch (UnsupportedFlavorException e) { - return false; + // Ignore } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); } - return true; + return false; + } + + private static String convertPath(String path) { + if (path.startsWith("file://")) path = path.substring("file://".length()); + if (path.indexOf('\n') != -1) path = path.substring(0, path.indexOf('\n')); + if (path.indexOf('\r') != -1) path = path.substring(0, path.indexOf('\r')); + return path; } @Override public boolean canImport(JComponent component, DataFlavor[] dataFlavors) { for (DataFlavor flavor : dataFlavors) { - if (flavor.isFlavorJavaFileListType()) { + if (flavor.isFlavorJavaFileListType() || flavor.isFlavorTextType()) { return true; } } diff --git a/eclipse/features/com.android.ide.eclipse.adt/feature.xml b/eclipse/features/com.android.ide.eclipse.adt/feature.xml index b9e2c7f..97bc8b1 100644 --- a/eclipse/features/com.android.ide.eclipse.adt/feature.xml +++ b/eclipse/features/com.android.ide.eclipse.adt/feature.xml @@ -134,6 +134,7 @@ This Agreement is governed by the laws of the State of New York and the intellec <import plugin="org.eclipse.wst.sse.ui"/> <import plugin="org.eclipse.wst.xml.core"/> <import plugin="org.eclipse.wst.xml.ui"/> + <import plugin="org.eclipse.jdt.junit"/> </requires> <plugin diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath index c3c8c10..a24fc87 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/.classpath +++ b/eclipse/plugins/com.android.ide.eclipse.adt/.classpath @@ -10,7 +10,7 @@ <classpathentry kind="lib" path="layoutlib_api.jar"/> <classpathentry kind="lib" path="layoutlib_utils.jar"/> <classpathentry kind="lib" path="ninepatch.jar"/> - <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> - <classpathentry combineaccessrules="false" kind="src" path="/SdkUiLib"/> + <classpathentry kind="lib" path="sdklib.jar" sourcepath="/SdkLib"/> + <classpathentry kind="lib" path="sdkuilib.jar" sourcepath="/SdkUiLib"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF index a464d5c..3750f66 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.adt/META-INF/MANIFEST.MF @@ -39,10 +39,12 @@ Require-Bundle: com.android.ide.eclipse.ddms, org.eclipse.wst.sse.core, org.eclipse.wst.sse.ui, org.eclipse.wst.xml.core, - org.eclipse.wst.xml.ui + org.eclipse.wst.xml.ui, + org.eclipse.jdt.junit Eclipse-LazyStart: true Export-Package: com.android.ide.eclipse.adt, com.android.ide.eclipse.adt.build;x-friends:="com.android.ide.eclipse.tests", + com.android.ide.eclipse.adt.launch;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.project;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.project.internal;x-friends:="com.android.ide.eclipse.tests", com.android.ide.eclipse.adt.sdk;x-friends:="com.android.ide.eclipse.tests", diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index d6c9ac1..4dfcc07 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -111,7 +111,7 @@ <extension point="org.eclipse.debug.core.launchConfigurationTypes"> <launchConfigurationType - delegate="com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate" + delegate="com.android.ide.eclipse.adt.launch.LaunchConfigDelegate" delegateDescription="The Android Application Launcher supports running and debugging remote Android applications on devices or emulators." delegateName="Android Launcher" id="com.android.ide.eclipse.adt.debug.LaunchConfigType" @@ -132,7 +132,7 @@ <extension point="org.eclipse.debug.ui.launchConfigurationTabGroups"> <launchConfigurationTabGroup - class="com.android.ide.eclipse.adt.debug.ui.LaunchConfigTabGroup" + class="com.android.ide.eclipse.adt.launch.LaunchConfigTabGroup" description="Android Application" id="com.android.ide.eclipse.adt.debug.LaunchConfigTabGroup" type="com.android.ide.eclipse.adt.debug.LaunchConfigType"/> @@ -140,8 +140,8 @@ <extension point="org.eclipse.debug.ui.launchShortcuts"> <shortcut - category="com.android.ide.eclipse.adt.debug.LaunchConfigType" - class="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut" + category="com.android.ide.eclipse.adt.launch.LaunchConfigType" + class="com.android.ide.eclipse.adt.launch.LaunchShortcut" icon="icons/android.png" id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut" label="Android Application" @@ -220,12 +220,6 @@ value="com.android.ide.eclipse.adt.AndroidNature"> </filter> <action - class="com.android.ide.eclipse.adt.project.CreateAidlImportAction" - enablesFor="1" - id="com.android.ide.eclipse.adt.project.CreateAidlImportAction" - label="Create Aidl preprocess file for Parcelable classes" - menubarPath="com.android.ide.eclipse.adt.AndroidTools/group1"/> - <action class="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction" enablesFor="1" id="com.android.ide.eclipse.adt.project.NewXmlFileWizardAction" @@ -345,24 +339,24 @@ name="Debug Android Application" description="Debug Android Application" categoryId="org.eclipse.debug.ui.category.run" - id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.debug"> + id="com.android.ide.eclipse.adt.launch.LaunchShortcut.debug"> </command> <command name="Run Android Application" description="Run Android Application" categoryId="org.eclipse.debug.ui.category.run" - id="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.run"> + id="com.android.ide.eclipse.adt.launch.LaunchShortcut.run"> </command> <keyBinding keySequence="M3+M2+A D" contextId="org.eclipse.ui.globalScope" - commandId="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.debug" + commandId="com.android.ide.eclipse.adt.launch.LaunchShortcut.debug" keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration"> </keyBinding> <keyBinding keySequence="M3+M2+A R" contextId="org.eclipse.ui.globalScope" - commandId="com.android.ide.eclipse.adt.debug.launching.LaunchShortcut.run" + commandId="com.android.ide.eclipse.adt.launch.LaunchShortcut.run" keyConfigurationId="org.eclipse.ui.defaultAcceleratorConfiguration"> </keyBinding> </extension> @@ -499,4 +493,15 @@ </action> </actionSet> </extension> + <extension + point="org.eclipse.debug.core.launchDelegates"> + <launchDelegate + delegate="com.android.ide.eclipse.adt.launch.JUnitLaunchConfigDelegate" + delegateDescription="Removes the Android JAR from the Bootstrap Classpath" + id="com.android.ide.eclipse.adt.launch.JUnitLaunchConfigDelegate.launchAndroidJunit" + modes="run,debug" + name="Android JUnit" + type="org.eclipse.jdt.junit.launchconfig"> + </launchDelegate> + </extension> </plugin> 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 61be3e5..48a21d1 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 @@ -20,7 +20,7 @@ import com.android.ddmuilib.StackTracePanel; import com.android.ddmuilib.StackTracePanel.ISourceRevealer; import com.android.ddmuilib.console.DdmConsole; import com.android.ddmuilib.console.IDdmConsole; -import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController; +import com.android.ide.eclipse.adt.launch.AndroidLaunchController; import com.android.ide.eclipse.adt.preferences.BuildPreferencePage; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.project.export.ExportWizard; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java index e2e9728..c3d5ba6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java @@ -20,15 +20,14 @@ import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.LoadStatus; -import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.XmlErrorHandler; import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener; -import com.android.sdklib.IAndroidTarget; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -37,8 +36,10 @@ 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.Status; +import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -892,25 +893,19 @@ abstract class BaseBuilder extends IncrementalProjectBuilder { stopBuild("SDK is not loaded yet"); } - // check the compiler compliance level. - if (ProjectHelper.checkCompilerCompliance(project) != - ProjectHelper.COMPILER_COMPLIANCE_OK) { - // we exit silently - stopBuild(Messages.Compiler_Compliance_Error); - } - - // Check that the SDK directory has been setup. - String osSdkFolder = AdtPlugin.getOsSdkFolder(); - - if (osSdkFolder == null || osSdkFolder.length() == 0) { - stopBuild(Messages.No_SDK_Setup_Error); + // abort if there are TARGET or ADT type markers + IMarker[] markers = project.findMarkers(AdtConstants.MARKER_TARGET, + false /*includeSubtypes*/, IResource.DEPTH_ZERO); + + if (markers.length > 0) { + stopBuild(""); } - - IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); - if (projectTarget == null) { - // no target. error has been output by the container initializer: - // exit silently. - stopBuild("Project has no target"); + + markers = project.findMarkers(AdtConstants.MARKER_ADT, false /*includeSubtypes*/, + IResource.DEPTH_ZERO); + + if (markers.length > 0) { + stopBuild(""); } } @@ -925,5 +920,22 @@ abstract class BaseBuilder extends IncrementalProjectBuilder { throw new CoreException(new Status(IStatus.CANCEL, AdtPlugin.PLUGIN_ID, String.format(error, args))); } - + + /** + * Recursively delete all the derived resources. + */ + protected void removeDerivedResources(IResource resource, IProgressMonitor monitor) + throws CoreException { + if (resource.exists()) { + if (resource.isDerived()) { + resource.delete(true, new SubProgressMonitor(monitor, 10)); + } else if (resource.getType() == IResource.FOLDER) { + IFolder folder = (IFolder)resource; + IResource[] members = folder.members(); + for (IResource member : members) { + removeDerivedResources(member, monitor); + } + } + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java index a0e446c..fb1608c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java @@ -19,7 +19,6 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.FixLaunchConfig; -import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestHelper; @@ -27,8 +26,8 @@ import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.XmlErrorHandler.BasicXmlErrorListener; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; -import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; @@ -36,13 +35,13 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourceAttributes; 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.NullProgressMonitor; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -68,13 +67,8 @@ public class PreCompilerBuilder extends BaseBuilder { private static final String PROPERTY_PACKAGE = "manifestPackage"; //$NON-NLS-1$ - private static final String PROPERTY_SOURCE_FOLDER = - "manifestPackageSourceFolder"; //$NON-NLS-1$ - private static final String PROPERTY_COMPILE_RESOURCES = "compileResources"; //$NON-NLS-1$ - - static final String PROPERTY_ANDROID_GENERATED = "androidGenerated"; //$NON-NLS-1$ - static final String PROPERTY_ANDROID_CONFLICT = "androidConflict"; //$NON-NLS-1$ + private static final String PROPERTY_COMPILE_AIDL = "compileAidl"; //$NON-NLS-1$ /** * Single line aidl error<br> @@ -83,24 +77,58 @@ public class PreCompilerBuilder extends BaseBuilder { private static Pattern sAidlPattern1 = Pattern.compile("^(.+?):(\\d+):\\s(.+)$"); //$NON-NLS-1$ /** - * Compile flag. This is set to true if one of the changed/added/removed - * file is a resource file. Upon visiting all the delta resources, if - * this flag is true, then we know we'll have to compile the resources - * into R.java + * Data to temporarly store aidl source file information + */ + static class AidlData { + IFile aidlFile; + IFolder sourceFolder; + + AidlData(IFolder sourceFolder, IFile aidlFile) { + this.sourceFolder = sourceFolder; + this.aidlFile = aidlFile; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof AidlData) { + AidlData file = (AidlData)obj; + return aidlFile.equals(file.aidlFile) && sourceFolder.equals(file.sourceFolder); + } + + return false; + } + } + + /** + * Resource Compile flag. This flag is reset to false after each successful compilation, and + * stored in the project persistent properties. This allows the builder to remember its state + * when the project is closed/opened. */ - private boolean mCompileResources = false; + private boolean mMustCompileResources = false; /** List of .aidl files found that are modified or new. */ - private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>(); + private final ArrayList<AidlData> mAidlToCompile = new ArrayList<AidlData>(); /** List of .aidl files that have been removed. */ - private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>(); + private final ArrayList<AidlData> mAidlToRemove = new ArrayList<AidlData>(); /** cache of the java package defined in the manifest */ private String mManifestPackage; + + /** Output folder for generated Java File. Created on the Builder init + * @see #startupOnInitialize() + */ + private IFolder mGenFolder; - /** Source folder containing the java package defined in the manifest. */ - private IFolder mManifestPackageSourceFolder; + /** + * Progress monitor used at the end of every build to refresh the content of the 'gen' folder + * and set the generated files as derived. + */ + private DerivedProgressMonitor mDerivedProgressMonitor; /** * Progress monitor waiting the end of the process to set a persistent value @@ -109,91 +137,36 @@ public class PreCompilerBuilder extends BaseBuilder { * to be known by eclipse, before we can call <code>resource.setPersistentProperty</code> on * a new file. */ - private static class RefreshProgressMonitor implements IProgressMonitor { + private static class DerivedProgressMonitor implements IProgressMonitor { private boolean mCancelled = false; - private IFile mNewFile; - private IFile mSource; - private boolean mDoneExecuted = false; - public RefreshProgressMonitor(IFile newFile, IFile source) { - mNewFile = newFile; - mSource = source; - } - - public void beginTask(String name, int totalWork) { - } - - public void done() { - if (mDoneExecuted == false) { - mDoneExecuted = true; - if (mNewFile.exists()) { - ProjectHelper.saveResourceProperty(mNewFile, PROPERTY_ANDROID_GENERATED, - mSource); - try { - mNewFile.setDerived(true); - } catch (CoreException e) { - // This really shouldn't happen since we check that the resource exist. - // Worst case scenario, the resource isn't marked as derived. - } - } - } - } - - public void internalWorked(double work) { - } - - public boolean isCanceled() { - return mCancelled; - } - - public void setCanceled(boolean value) { - mCancelled = value; - } - - public void setTaskName(String name) { + private final ArrayList<IFile> mFileList = new ArrayList<IFile>(); + private boolean mDone = false; + public DerivedProgressMonitor() { } - - public void subTask(String name) { - } - - public void worked(int work) { + + void addFile(IFile file) { + mFileList.add(file); } - } - - /** - * Progress Monitor setting up to two files as derived once their parent is refreshed. - * This is used as ProgressMonitor to refresh the R.java/Manifest.java parent (to display - * the newly created files in the package explorer). - */ - private static class DerivedProgressMonitor implements IProgressMonitor { - private boolean mCancelled = false; - private IFile mFile1; - private IFile mFile2; - private boolean mDoneExecuted = false; - public DerivedProgressMonitor(IFile file1, IFile file2) { - mFile1 = file1; - mFile2 = file2; + + void reset() { + mFileList.clear(); + mDone = false; } public void beginTask(String name, int totalWork) { } public void done() { - if (mDoneExecuted == false) { - if (mFile1 != null && mFile1.exists()) { - mDoneExecuted = true; - try { - mFile1.setDerived(true); - } catch (CoreException e) { - // This really shouldn't happen since we check that the resource edit. - // Worst case scenario, the resource isn't marked as derived. - } - } - if (mFile2 != null && mFile2.exists()) { - try { - mFile2.setDerived(true); - } catch (CoreException e) { - // This really shouldn't happen since we check that the resource edit. - // Worst case scenario, the resource isn't marked as derived. + if (mDone == false) { + mDone = true; + for (IFile file : mFileList) { + if (file.exists()) { + try { + file.setDerived(true); + } catch (CoreException e) { + // This really shouldn't happen since we check that the resource exist. + // Worst case scenario, the resource isn't marked as derived. + } } } } @@ -229,322 +202,306 @@ public class PreCompilerBuilder extends BaseBuilder { @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { - // First thing we do is go through the resource delta to not - // lose it if we have to abort the build for any reason. + try { + mDerivedProgressMonitor.reset(); - // get the project objects - IProject project = getProject(); - - // Top level check to make sure the build can move forward. - abortOnBadSetup(project); - - IJavaProject javaProject = JavaCore.create(project); - IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); - - // now we need to get the classpath list - ArrayList<IPath> sourceList = BaseProjectHelper.getSourceClasspaths(javaProject); - - PreCompilerDeltaVisitor dv = null; - String javaPackage = null; - - if (kind == FULL_BUILD) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Start_Full_Pre_Compiler); - mCompileResources = true; - buildAidlCompilationList(project, sourceList); - } else { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Start_Inc_Pre_Compiler); - - // Go through the resources and see if something changed. - // Even if the mCompileResources flag is true from a previously aborted - // build, we need to go through the Resource delta to get a possible - // list of aidl files to compile/remove. - IResourceDelta delta = getDelta(project); - if (delta == null) { - mCompileResources = true; - buildAidlCompilationList(project, sourceList); + // First thing we do is go through the resource delta to not + // lose it if we have to abort the build for any reason. + + // get the project objects + IProject project = getProject(); + + // Top level check to make sure the build can move forward. + abortOnBadSetup(project); + + IJavaProject javaProject = JavaCore.create(project); + IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); + + // now we need to get the classpath list + ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( + javaProject); + + PreCompilerDeltaVisitor dv = null; + String javaPackage = null; + + if (kind == FULL_BUILD) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Start_Full_Pre_Compiler); + mMustCompileResources = true; + buildAidlCompilationList(project, sourceFolderPathList); } else { - dv = new PreCompilerDeltaVisitor(this, sourceList); - delta.accept(dv); - - // record the state - mCompileResources |= dv.getCompileResources(); - - // handle aidl modification - if (dv.getFullAidlRecompilation()) { - buildAidlCompilationList(project, sourceList); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Start_Inc_Pre_Compiler); + + // Go through the resources and see if something changed. + // Even if the mCompileResources flag is true from a previously aborted + // build, we need to go through the Resource delta to get a possible + // list of aidl files to compile/remove. + IResourceDelta delta = getDelta(project); + if (delta == null) { + mMustCompileResources = true; + buildAidlCompilationList(project, sourceFolderPathList); } else { + dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList); + delta.accept(dv); + + // record the state + mMustCompileResources |= dv.getCompileResources(); + + // handle aidl modification, and update mMustCompileAidl mergeAidlFileModifications(dv.getAidlToCompile(), dv.getAidlToRemove()); + + // get the java package from the visitor + javaPackage = dv.getManifestPackage(); } - - // get the java package from the visitor - javaPackage = dv.getManifestPackage(); } - } - - // store the build status in the persistent storage - saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mCompileResources); - // TODO also needs to store the list of aidl to compile/remove - - // if there was some XML errors, we just return w/o doing - // anything since we've put some markers in the files anyway. - if (dv != null && dv.mXmlError) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Xml_Error); - - // This interrupts the build. The next builders will not run. - stopBuild(Messages.Xml_Error); - } - - - // get the manifest file - IFile manifest = AndroidManifestHelper.getManifest(project); - - if (manifest == null) { - String msg = String.format(Messages.s_File_Missing, - AndroidConstants.FN_ANDROID_MANIFEST); - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); - markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } - - // lets check the XML of the manifest first, if that hasn't been done by the - // resource delta visitor yet. - if (dv == null || dv.getCheckedManifestXml() == false) { - BasicXmlErrorListener errorListener = new BasicXmlErrorListener(); - AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest, - errorListener); - - if (errorListener.mHasXmlError == true) { - // there was an error in the manifest, its file has been marked, - // by the XmlErrorHandler. - // We return; - String msg = String.format(Messages.s_Contains_Xml_Error, + + // store the build status in the persistent storage + saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mMustCompileResources); + + // if there was some XML errors, we just return w/o doing + // anything since we've put some markers in the files anyway. + if (dv != null && dv.mXmlError) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Xml_Error); + + // This interrupts the build. The next builders will not run. + stopBuild(Messages.Xml_Error); + } + + + // get the manifest file + IFile manifest = AndroidManifestHelper.getManifest(project); + + if (manifest == null) { + String msg = String.format(Messages.s_File_Missing, AndroidConstants.FN_ANDROID_MANIFEST); AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); - + markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + // This interrupts the build. The next builders will not run. stopBuild(msg); } - - // get the java package from the parser - javaPackage = parser.getPackage(); - } - - if (javaPackage == null || javaPackage.length() == 0) { - // looks like the AndroidManifest file isn't valid. - String msg = String.format(Messages.s_Doesnt_Declare_Package_Error, - AndroidConstants.FN_ANDROID_MANIFEST); - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - msg); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } - - // at this point we have the java package. We need to make sure it's not a different package - // than the previous one that were built. - if (javaPackage.equals(mManifestPackage) == false) { - // The manifest package has changed, the user may want to update - // the launch configuration - if (mManifestPackage != null) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Checking_Package_Change); - - FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, javaPackage); - flc.start(); + + // lets check the XML of the manifest first, if that hasn't been done by the + // resource delta visitor yet. + if (dv == null || dv.getCheckedManifestXml() == false) { + BasicXmlErrorListener errorListener = new BasicXmlErrorListener(); + AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(manifest, + errorListener); + + if (errorListener.mHasXmlError == true) { + // there was an error in the manifest, its file has been marked, + // by the XmlErrorHandler. + // We return; + String msg = String.format(Messages.s_Contains_Xml_Error, + AndroidConstants.FN_ANDROID_MANIFEST); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); + + // This interrupts the build. The next builders will not run. + stopBuild(msg); + } + + // get the java package from the parser + javaPackage = parser.getPackage(); } - - // now we delete the generated classes from their previous location - deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS, - mManifestPackageSourceFolder, mManifestPackage); - deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, - mManifestPackageSourceFolder, mManifestPackage); - - // record the new manifest package, and save it. - mManifestPackage = javaPackage; - saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage); - } - - if (mCompileResources) { - // we need to figure out where to store the R class. - // get the parent folder for R.java and update mManifestPackageSourceFolder - IFolder packageFolder = getManifestPackageFolder(project, sourceList); - - // at this point, either we have found the package or not. - // if we haven't well it's time to tell the user and abort - if (mManifestPackageSourceFolder == null) { - // mark the manifest file - String message = String.format(Messages.Package_s_Doesnt_Exist_Error, - mManifestPackage); - BaseProjectHelper.addMarker(manifest, AndroidConstants.MARKER_AAPT_COMPILE, message, - IMarker.SEVERITY_ERROR); - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, message); - - // abort + + if (javaPackage == null || javaPackage.length() == 0) { + // looks like the AndroidManifest file isn't valid. + String msg = String.format(Messages.s_Doesnt_Declare_Package_Error, + AndroidConstants.FN_ANDROID_MANIFEST); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + msg); + // This interrupts the build. The next builders will not run. - stopBuild(message); + stopBuild(msg); } - - - // found the folder in which to write the stuff - - // get the resource folder - IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); - - // get the file system path - IPath outputLocation = mManifestPackageSourceFolder.getLocation(); - IPath resLocation = resFolder.getLocation(); - IPath manifestLocation = manifest.getLocation(); - - // those locations have to exist for us to do something! - if (outputLocation != null && resLocation != null - && manifestLocation != null) { - String osOutputPath = outputLocation.toOSString(); - String osResPath = resLocation.toOSString(); - String osManifestPath = manifestLocation.toOSString(); - - // remove the aapt markers - removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); - removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); - - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Preparing_Generated_Files); - - // since the R.java file may be already existing in read-only - // mode we need to make it readable so that aapt can overwrite - // it - IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); - prepareFileForExternalModification(rJavaFile); - - // do the same for the Manifest.java class - IFile manifestJavaFile = packageFolder.getFile(AndroidConstants.FN_MANIFEST_CLASS); - prepareFileForExternalModification(manifestJavaFile); - - // we actually need to delete the manifest.java as it may become empty and in this - // case aapt doesn't generate an empty one, but instead doesn't touch it. - manifestJavaFile.delete(true, null); - - // launch aapt: create the command line - ArrayList<String> array = new ArrayList<String>(); - array.add(projectTarget.getPath(IAndroidTarget.AAPT)); - array.add("package"); //$NON-NLS-1$ - array.add("-m"); //$NON-NLS-1$ - if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { - array.add("-v"); //$NON-NLS-1$ + + // at this point we have the java package. We need to make sure it's not a different + // package than the previous one that were built. + if (javaPackage.equals(mManifestPackage) == false) { + // The manifest package has changed, the user may want to update + // the launch configuration + if (mManifestPackage != null) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Checking_Package_Change); + + FixLaunchConfig flc = new FixLaunchConfig(project, mManifestPackage, + javaPackage); + flc.start(); } - array.add("-J"); //$NON-NLS-1$ - array.add(osOutputPath); - array.add("-M"); //$NON-NLS-1$ - array.add(osManifestPath); - array.add("-S"); //$NON-NLS-1$ - array.add(osResPath); - array.add("-I"); //$NON-NLS-1$ - array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); - - if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { - StringBuilder sb = new StringBuilder(); - for (String c : array) { - sb.append(c); - sb.append(' '); + + // now we delete the generated classes from their previous location + deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS, + mManifestPackage); + deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, + mManifestPackage); + + // record the new manifest package, and save it. + mManifestPackage = javaPackage; + saveProjectStringProperty(PROPERTY_PACKAGE, mManifestPackage); + } + + if (mMustCompileResources) { + // we need to figure out where to store the R class. + // get the parent folder for R.java and update mManifestPackageSourceFolder + IFolder packageFolder = getGenManifestPackageFolder(project); + + // get the resource folder + IFolder resFolder = project.getFolder(AndroidConstants.WS_RESOURCES); + + // get the file system path + IPath outputLocation = mGenFolder.getLocation(); + IPath resLocation = resFolder.getLocation(); + IPath manifestLocation = manifest.getLocation(); + + // those locations have to exist for us to do something! + if (outputLocation != null && resLocation != null + && manifestLocation != null) { + String osOutputPath = outputLocation.toOSString(); + String osResPath = resLocation.toOSString(); + String osManifestPath = manifestLocation.toOSString(); + + // remove the aapt markers + removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE); + removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE); + + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Preparing_Generated_Files); + + // since the R.java file may be already existing in read-only + // mode we need to make it readable so that aapt can overwrite + // it + IFile rJavaFile = packageFolder.getFile(AndroidConstants.FN_RESOURCE_CLASS); + + // do the same for the Manifest.java class + IFile manifestJavaFile = packageFolder.getFile( + AndroidConstants.FN_MANIFEST_CLASS); + + // we actually need to delete the manifest.java as it may become empty and + // in this case aapt doesn't generate an empty one, but instead doesn't + // touch it. + manifestJavaFile.delete(true, null); + + // launch aapt: create the command line + ArrayList<String> array = new ArrayList<String>(); + array.add(projectTarget.getPath(IAndroidTarget.AAPT)); + array.add("package"); //$NON-NLS-1$ + array.add("-m"); //$NON-NLS-1$ + if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { + array.add("-v"); //$NON-NLS-1$ } - String cmd_line = sb.toString(); - AdtPlugin.printToConsole(project, cmd_line); - } - - // launch - int execError = 1; - try { - // launch the command line process - Process process = Runtime.getRuntime().exec( - array.toArray(new String[array.size()])); - - // list to store each line of stderr - ArrayList<String> results = new ArrayList<String>(); - - // get the output and return code from the process - execError = grabProcessOutput(process, results); - - // attempt to parse the error output - boolean parsingError = parseAaptOutput(results, project); - - // if we couldn't parse the output we display it in the console. - if (parsingError) { - if (execError != 0) { - AdtPlugin.printErrorToConsole(project, results.toArray()); - } else { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_NORMAL, - project, results.toArray()); + array.add("-J"); //$NON-NLS-1$ + array.add(osOutputPath); + array.add("-M"); //$NON-NLS-1$ + array.add(osManifestPath); + array.add("-S"); //$NON-NLS-1$ + array.add(osResPath); + array.add("-I"); //$NON-NLS-1$ + array.add(projectTarget.getPath(IAndroidTarget.ANDROID_JAR)); + + if (AdtPlugin.getBuildVerbosity() == AdtConstants.BUILD_VERBOSE) { + StringBuilder sb = new StringBuilder(); + for (String c : array) { + sb.append(c); + sb.append(' '); } + String cmd_line = sb.toString(); + AdtPlugin.printToConsole(project, cmd_line); } - - if (execError != 0) { - // if the exec failed, and we couldn't parse the error output (and therefore - // not all files that should have been marked, were marked), we put a - // generic marker on the project and abort. + + // launch + int execError = 1; + try { + // launch the command line process + Process process = Runtime.getRuntime().exec( + array.toArray(new String[array.size()])); + + // list to store each line of stderr + ArrayList<String> results = new ArrayList<String>(); + + // get the output and return code from the process + execError = grabProcessOutput(process, results); + + // attempt to parse the error output + boolean parsingError = parseAaptOutput(results, project); + + // if we couldn't parse the output we display it in the console. if (parsingError) { - markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors, - IMarker.SEVERITY_ERROR); + if (execError != 0) { + AdtPlugin.printErrorToConsole(project, results.toArray()); + } else { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_NORMAL, + project, results.toArray()); + } } - - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.AAPT_Error); - - // abort if exec failed. + + if (execError != 0) { + // if the exec failed, and we couldn't parse the error output + // (and therefore not all files that should have been marked, + // were marked), we put a generic marker on the project and abort. + if (parsingError) { + markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AAPT_Errors, + IMarker.SEVERITY_ERROR); + } + + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.AAPT_Error); + + // abort if exec failed. + // This interrupts the build. The next builders will not run. + stopBuild(Messages.AAPT_Error); + } + } catch (IOException e1) { + // something happen while executing the process, + // mark the project and exit + String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); + markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + // This interrupts the build. The next builders will not run. - stopBuild(Messages.AAPT_Error); + stopBuild(msg); + } catch (InterruptedException e) { + // we got interrupted waiting for the process to end... + // mark the project and exit + String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); + markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); + + // This interrupts the build. The next builders will not run. + stopBuild(msg); + } + + // if the return code was OK, we refresh the folder that + // contains R.java to force a java recompile. + if (execError == 0) { + // now add the R.java/Manifest.java to the list of file to be marked + // as derived. + mDerivedProgressMonitor.addFile(rJavaFile); + mDerivedProgressMonitor.addFile(manifestJavaFile); + + // build has been done. reset the state of the builder + mMustCompileResources = false; + + // and store it + saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, + mMustCompileResources); } - } catch (IOException e1) { - // something happen while executing the process, - // mark the project and exit - String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); - markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } catch (InterruptedException e) { - // we got interrupted waiting for the process to end... - // mark the project and exit - String msg = String.format(Messages.AAPT_Exec_Error, array.get(0)); - markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR); - - // This interrupts the build. The next builders will not run. - stopBuild(msg); - } - - // if the return code was OK, we refresh the folder that - // contains R.java to force a java recompile. - if (execError == 0) { - // now set the R.java/Manifest.java file as read only. - finishJavaFilesAfterExternalModification(rJavaFile, manifestJavaFile); - - // build has been done. reset the state of the builder - mCompileResources = false; - - // and store it - saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, mCompileResources); } + } else { + // nothing to do } - } else { - // nothing to do - } - - // now handle the aidl stuff. - // look for a preprocessed aidl file - IResource projectAidl = project.findMember("project.aidl"); //$NON-NLS-1$ - String folderAidlPath = null; - if (projectAidl != null && projectAidl.exists()) { - folderAidlPath = projectAidl.getLocation().toOSString(); - } - boolean aidlStatus = handleAidl(projectTarget, sourceList, folderAidlPath, monitor); - - if (aidlStatus == false && mCompileResources == false) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.Nothing_To_Compile); + + // now handle the aidl stuff. + boolean aidlStatus = handleAidl(projectTarget, sourceFolderPathList, monitor); + + if (aidlStatus == false && mMustCompileResources == false) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + Messages.Nothing_To_Compile); + } + } finally { + // refresh the 'gen' source folder. Once this is done with the custom progress + // monitor to mark all new files as derived + mGenFolder.refreshLocal(IResource.DEPTH_INFINITE, mDerivedProgressMonitor); } return null; @@ -557,67 +514,57 @@ public class PreCompilerBuilder extends BaseBuilder { AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(), Messages.Removing_Generated_Classes); - // check if we have the R.java info already. - if (mManifestPackageSourceFolder != null && mManifestPackage != null) { - deleteObsoleteGeneratedClass(AndroidConstants.FN_RESOURCE_CLASS, - mManifestPackageSourceFolder, mManifestPackage); - deleteObsoleteGeneratedClass(AndroidConstants.FN_MANIFEST_CLASS, - mManifestPackageSourceFolder, mManifestPackage); - } - - // FIXME: delete all java generated from aidl. + // remove all the derived resources from the 'gen' source folder. + removeDerivedResources(mGenFolder, monitor); } @Override protected void startupOnInitialize() { super.startupOnInitialize(); + + mDerivedProgressMonitor = new DerivedProgressMonitor(); + + IProject project = getProject(); // load the previous IFolder and java package. mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE); - IResource resource = loadProjectResourceProperty(PROPERTY_SOURCE_FOLDER); - if (resource instanceof IFolder) { - mManifestPackageSourceFolder = (IFolder)resource; - } + + // get the source folder in which all the Java files are created + mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); - // Load the current compile flag. We ask for true if not found to force a + // Load the current compile flags. We ask for true if not found to force a // recompile. - mCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true); + mMustCompileResources = loadProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES, true); + boolean mustCompileAidl = loadProjectBooleanProperty(PROPERTY_COMPILE_AIDL, true); + + // if we stored that we have to compile some aidl, we build the list that will compile them + // all + if (mustCompileAidl) { + IJavaProject javaProject = JavaCore.create(project); + ArrayList<IPath> sourceFolderPathList = BaseProjectHelper.getSourceClasspaths( + javaProject); + + buildAidlCompilationList(project, sourceFolderPathList); + } } /** * Delete the a generated java class associated with the specified java package. * @param filename Name of the generated file to remove. - * @param sourceFolder The source Folder containing the old java package. * @param javaPackage the old java package */ - private void deleteObsoleteGeneratedClass(String filename, IFolder sourceFolder, - String javaPackage) { - if (sourceFolder == null || javaPackage == null) { + private void deleteObsoleteGeneratedClass(String filename, String javaPackage) { + if (javaPackage == null) { return; } - - // convert the java package into path - String[] segments = javaPackage.split(AndroidConstants.RE_DOT); - - StringBuilder path = new StringBuilder(); - for (String s : segments) { - path.append(AndroidConstants.WS_SEP_CHAR); - path.append(s); - } - - // appends the name of the generated file - path.append(AndroidConstants.WS_SEP_CHAR); - path.append(filename); - - Path iPath = new Path(path.toString()); + + IPath packagePath = getJavaPackagePath(javaPackage); + IPath iPath = packagePath.append(filename); // Find a matching resource object. - IResource javaFile = sourceFolder.findMember(iPath); + IResource javaFile = mGenFolder.findMember(iPath); if (javaFile != null && javaFile.exists() && javaFile.getType() == IResource.FILE) { try { - // remove the read-only tag - prepareFileForExternalModification((IFile)javaFile); - // delete javaFile.delete(true, null); @@ -626,7 +573,8 @@ public class PreCompilerBuilder extends BaseBuilder { } catch (CoreException e) { // failed to delete it, the user will have to delete it manually. - String message = String.format(Messages.Delete_Obsolete_Error, path); + String message = String.format(Messages.Delete_Obsolete_Error, + javaFile.getFullPath()); IProject project = getProject(); AdtPlugin.printErrorToConsole(project, message); AdtPlugin.printErrorToConsole(project, e.getMessage()); @@ -635,94 +583,38 @@ public class PreCompilerBuilder extends BaseBuilder { } /** - * Looks for the folder containing the package defined in the manifest. It looks in the - * list of source folders for the one containing folders matching the package defined in the - * manifest (from the field <code>mManifestPackage</code>). It returns the final folder, which - * will contain the R class, and update the field <code>mManifestPackageSourceFolder</code> - * to be the source folder containing the full package. + * Creates a relative {@link IPath} from a java package. + * @param javaPackageName the java package. + */ + private IPath getJavaPackagePath(String javaPackageName) { + // convert the java package into path + String[] segments = javaPackageName.split(AndroidConstants.RE_DOT); + + StringBuilder path = new StringBuilder(); + for (String s : segments) { + path.append(AndroidConstants.WS_SEP_CHAR); + path.append(s); + } + + return new Path(path.toString()); + } + + /** + * Returns an {@link IFolder} (located inside the 'gen' source folder), that matches the + * package defined in the manifest. This {@link IFolder} may not actually exist + * (aapt will create it anyway). * @param project The project. - * @param sourceList The list of source folders for the project. - * @return the package that will contain the R class or null if the folder was not found. + * @return the {@link IFolder} that will contain the R class or null if the folder was not found. * @throws CoreException */ - private IFolder getManifestPackageFolder(IProject project, ArrayList<IPath> sourceList) + private IFolder getGenManifestPackageFolder(IProject project) throws CoreException { - // split the package in segments - String[] packageSegments = mManifestPackage.split(AndroidConstants.RE_DOT); - - // we look for 2 folders. - // 1. The source folder that contains the full java package. - // we will store the folder in the field mJavaSourceFolder, for reuse during - IFolder manifestPackageSourceFolder = null; - // subsequent builds. This is the folder we will give to aapt. - // 2. The folder actually containing the R.java files. We need this one to do a refresh - IFolder packageFolder = null; - - for (IPath iPath : sourceList) { - int packageSegmentIndex = 0; - - // the path is relative to the workspace. We ignore the first segment, - // when getting the resource from the IProject object. - IResource classpathEntry = project.getFolder(iPath.removeFirstSegments(1)); - - if (classpathEntry instanceof IFolder) { - IFolder classpathFolder = (IFolder)classpathEntry; - IFolder folder = classpathFolder; - - boolean failed = false; - while (failed == false - && packageSegmentIndex < packageSegments.length) { - - // loop on that folder content looking for folders - // that match the package - // defined in AndroidManifest.xml - - // get the folder content - IResource[] content = folder.members(); - - // this is the segment we look for - String segment = packageSegments[packageSegmentIndex]; - - // did we find it at this level - boolean found = false; - - for (IResource r : content) { - // look for the java package segment - if (r instanceof IFolder) { - if (r.getName().equals(segment)) { - // we need to skip to the next one - folder = (IFolder)r; - packageSegmentIndex++; - found = true; - break; - } - } - } - - // if we didn't find it at this level we just fail. - if (found == false) { - failed = true; - } - } - - // if we didn't fail then we found it. no point in - // looping through the rest - // or the classpathEntry - if (failed == false) { - // save the target folder reference - manifestPackageSourceFolder = classpathFolder; - packageFolder = folder; - break; - } - } - } - - // save the location of the folder into the persistent storage - if (manifestPackageSourceFolder != mManifestPackageSourceFolder) { - mManifestPackageSourceFolder = manifestPackageSourceFolder; - saveProjectResourceProperty(PROPERTY_SOURCE_FOLDER, mManifestPackageSourceFolder); - } - return packageFolder; + // get the path for the package + IPath packagePath = getJavaPackagePath(mManifestPackage); + + // get a folder for this path under the 'gen' source folder, and return it. + // This IFolder may not reference an actual existing folder. + return mGenFolder.getFolder(packagePath); } /** @@ -730,27 +622,22 @@ public class PreCompilerBuilder extends BaseBuilder { * created from aidl files that are now gone. * @param projectTarget Target of the project * @param sourceFolders the list of source folders, relative to the workspace. - * @param folderAidlPath * @param monitor the projess monitor * @returns true if it did something * @throws CoreException */ private boolean handleAidl(IAndroidTarget projectTarget, ArrayList<IPath> sourceFolders, - String folderAidlPath, IProgressMonitor monitor) throws CoreException { + IProgressMonitor monitor) throws CoreException { if (mAidlToCompile.size() == 0 && mAidlToRemove.size() == 0) { return false; } - // create the command line - String[] command = new String[4 + sourceFolders.size() + (folderAidlPath != null ? 1 : 0)]; + String[] command = new String[4 + sourceFolders.size()]; int index = 0; - int aidlIndex; command[index++] = projectTarget.getPath(IAndroidTarget.AIDL); - command[aidlIndex = index++] = "-p"; //$NON-NLS-1$ - if (folderAidlPath != null) { - command[index++] = "-p" + folderAidlPath; //$NON-NLS-1$ - } + command[index++] = "-p" + Sdk.getCurrent().getTarget(getProject()).getPath( //$NON-NLS-1$ + IAndroidTarget.ANDROID_AIDL); // since the path are relative to the workspace and not the project itself, we need // the workspace root. @@ -761,81 +648,44 @@ public class PreCompilerBuilder extends BaseBuilder { } // list of files that have failed compilation. - ArrayList<IFile> stillNeedCompilation = new ArrayList<IFile>(); + ArrayList<AidlData> stillNeedCompilation = new ArrayList<AidlData>(); // if an aidl file is being removed before we managed to compile it, it'll be in // both list. We *need* to remove it from the compile list or it'll never go away. - for (IFile aidlFile : mAidlToRemove) { + for (AidlData aidlFile : mAidlToRemove) { int pos = mAidlToCompile.indexOf(aidlFile); if (pos != -1) { mAidlToCompile.remove(pos); } } - + // loop until we've compile them all - for (IFile aidlFile : mAidlToCompile) { + for (AidlData aidlData : mAidlToCompile) { // Remove the AIDL error markers from the aidl file - removeMarkersFromFile(aidlFile, AndroidConstants.MARKER_AIDL); - - // get the path - IPath iPath = aidlFile.getLocation(); - String osPath = iPath.toOSString(); - - // get the parent container - IContainer parentContainer = aidlFile.getParent(); - - // replace the extension in both the full path and the - // last segment - String osJavaPath = osPath.replaceAll(AndroidConstants.RE_AIDL_EXT, - AndroidConstants.DOT_JAVA); - String javaName = aidlFile.getName().replaceAll(AndroidConstants.RE_AIDL_EXT, - AndroidConstants.DOT_JAVA); - - // check if we can compile it, or if there is a conflict with a java file - boolean conflict = ProjectHelper.loadBooleanProperty(aidlFile, - PROPERTY_ANDROID_CONFLICT, false); - if (conflict) { - String msg = String.format(Messages.AIDL_Java_Conflict, javaName, - aidlFile.getName()); - - // put a marker - BaseProjectHelper.addMarker(aidlFile, AndroidConstants.MARKER_AIDL, msg, - IMarker.SEVERITY_ERROR); - - // output an error - AdtPlugin.printErrorToConsole(getProject(), msg); - - stillNeedCompilation.add(aidlFile); + removeMarkersFromFile(aidlData.aidlFile, AndroidConstants.MARKER_AIDL); - // move on to next file - continue; - } - - // get the resource for the java file. - Path javaIPath = new Path(javaName); - IFile javaFile = parentContainer.getFile(javaIPath); - - // if the file was read-only, this will make it readable. - prepareFileForExternalModification(javaFile); + // get the path of the source file. + IPath sourcePath = aidlData.aidlFile.getLocation(); + String osSourcePath = sourcePath.toOSString(); + + IFile javaFile = getGenDestinationFile(aidlData, true /*createFolders*/, monitor); // finish to set the command line. - command[aidlIndex] = "-p" + Sdk.getCurrent().getTarget(aidlFile.getProject()).getPath( - IAndroidTarget.ANDROID_AIDL); //$NON-NLS-1$ - command[index] = osPath; - command[index + 1] = osJavaPath; + command[index] = osSourcePath; + command[index + 1] = javaFile.getLocation().toOSString(); // launch the process - if (execAidl(command, aidlFile) == false) { + if (execAidl(command, aidlData.aidlFile) == false) { // aidl failed. File should be marked. We add the file to the list // of file that will need compilation again. - stillNeedCompilation.add(aidlFile); + stillNeedCompilation.add(aidlData); // and we move on to the next one. continue; } else { - // since the exec worked, we refresh the parent, and set the - // file as read only. - finishFileAfterExternalModification(javaFile, aidlFile); + // make sure the file will be marked as derived once we refresh the 'gen' source + // folder. + mDerivedProgressMonitor.addFile(javaFile); } } @@ -844,44 +694,68 @@ public class PreCompilerBuilder extends BaseBuilder { mAidlToCompile.addAll(stillNeedCompilation); // Remove the java files created from aidl files that have been removed. - for (IFile aidlFile : mAidlToRemove) { - // make the java filename - String javaName = aidlFile.getName().replaceAll( - AndroidConstants.RE_AIDL_EXT, - AndroidConstants.DOT_JAVA); - - // get the parent container - IContainer ic = aidlFile.getParent(); - - // and get the IFile corresponding to the java file. - IFile javaFile = ic.getFile(new Path(javaName)); - if (javaFile != null && javaFile.exists() ) { - // check if this java file has a persistent data marking it as generated by - // the builder. - // While we put the aidl path as a resource, internally it's all string anyway. - // We use loadStringProperty, because loadResourceProperty tries to match - // the string value (a path in this case) with an existing resource, but - // the aidl file was deleted, so it would return null, even though the property - // existed. - String aidlPath = ProjectHelper.loadStringProperty(javaFile, - PROPERTY_ANDROID_GENERATED); - - if (aidlPath != null) { - // This confirms the java file was generated by the builder, - // we can delete the aidlFile. - javaFile.delete(true, null); - - // Refresh parent. - ic.refreshLocal(IResource.DEPTH_ONE, monitor); - } + for (AidlData aidlData : mAidlToRemove) { + IFile javaFile = getGenDestinationFile(aidlData, false /*createFolders*/, monitor); + if (javaFile.exists()) { + // This confirms the java file was generated by the builder, + // we can delete the aidlFile. + javaFile.delete(true, null); + + // Refresh parent. + javaFile.getParent().refreshLocal(IResource.DEPTH_ONE, monitor); } } + mAidlToRemove.clear(); + // store the build state. If there are any files that failed to compile, we will + // force a full aidl compile on the next project open. (unless a full compilation succeed + // before the project is closed/re-opened.) + // TODO: Optimize by saving only the files that need compilation + saveProjectBooleanProperty(PROPERTY_COMPILE_AIDL , mAidlToCompile.size() > 0); + return true; } /** + * Returns the {@link IFile} handle to the destination file for a given aild source file + * ({@link AidlData}). + * @param aidlData the data for the aidl source file. + * @param createFolders whether or not the parent folder of the destination should be created + * if it does not exist. + * @param monitor the progress monitor + * @return the handle to the destination file. + * @throws CoreException + */ + private IFile getGenDestinationFile(AidlData aidlData, boolean createFolders, + IProgressMonitor monitor) throws CoreException { + // build the destination folder path. + // Use the path of the source file, except for the path leading to its source folder, + // and for the last segment which is the filename. + int segmentToSourceFolderCount = aidlData.sourceFolder.getFullPath().segmentCount(); + IPath packagePath = aidlData.aidlFile.getFullPath().removeFirstSegments( + segmentToSourceFolderCount).removeLastSegments(1); + Path destinationPath = new Path(packagePath.toString()); + + // get an IFolder for this path. It's relative to the 'gen' folder already + IFolder destinationFolder = mGenFolder.getFolder(destinationPath); + + // create it if needed + if (destinationFolder.exists() == false && createFolders) { + destinationFolder.create(true /*force*/, true /*local*/, + new SubProgressMonitor(monitor, 10));; + } + + // Build the Java file name from the aidl name. + String javaName = aidlData.aidlFile.getName().replaceAll(AndroidConstants.RE_AIDL_EXT, + AndroidConstants.DOT_JAVA); + + // get the resource for the java file. + IFile javaFile = destinationFolder.getFile(javaName); + return javaFile; + } + + /** * Execute the aidl command line, parse the output, and mark the aidl file * with any reported errors. * @param command the String array containing the command line to execute. @@ -933,43 +807,28 @@ public class PreCompilerBuilder extends BaseBuilder { * Goes through the build paths and fills the list of aidl files to compile * ({@link #mAidlToCompile}). * @param project The project. - * @param buildPaths The list of build paths. + * @param sourceFolderPathList The list of source folder paths. */ private void buildAidlCompilationList(IProject project, - ArrayList<IPath> buildPaths) { - for (IPath p : buildPaths) { - // Because the path contains the name of the project as well, we - // need to remove it, to access the final folder. - String[] segments = p.segments(); - IContainer folder = project; - for (int i = 1; i < segments.length; i++) { - IResource r = folder.findMember(segments[i]); - if (r != null && r.exists() && - r.getType() == IResource.FOLDER) { - folder = (IContainer)r; - } else { - // hmm looks like the build path is corrupted/wrong. - // reset and break - folder = project; - break; - } - } - - // did we ge a folder? - if (folder != project) { - // then we scan! - scanContainerForAidl(folder); + ArrayList<IPath> sourceFolderPathList) { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + for (IPath sourceFolderPath : sourceFolderPathList) { + IFolder sourceFolder = root.getFolder(sourceFolderPath); + // we don't look in the 'gen' source folder as there will be no source in there. + if (sourceFolder.exists() && sourceFolder.equals(mGenFolder) == false) { + scanFolderForAidl(sourceFolder, sourceFolder); } } } /** - * Scans a container and fills the list of aidl files to compile. - * @param container The container to scan. + * Scans a folder and fills the list of aidl files to compile. + * @param sourceFolder the root source folder. + * @param container The folder to scan. */ - private void scanContainerForAidl(IContainer container) { + private void scanFolderForAidl(IFolder sourceFolder, IFolder folder) { try { - IResource[] members = container.members(); + IResource[] members = folder.members(); for (IResource r : members) { // get the type of the resource switch (r.getType()) { @@ -978,12 +837,12 @@ public class PreCompilerBuilder extends BaseBuilder { // and that it's an aidl file if (r.exists() && AndroidConstants.EXT_AIDL.equalsIgnoreCase(r.getFileExtension())) { - mAidlToCompile.add((IFile)r); + mAidlToCompile.add(new AidlData(sourceFolder, (IFile)r)); } break; case IResource.FOLDER: // recursively go through children - scanContainerForAidl((IFolder)r); + scanFolderForAidl(sourceFolder, (IFolder)r); break; default: // this would mean it's a project or the workspace root @@ -1051,13 +910,12 @@ public class PreCompilerBuilder extends BaseBuilder { * @param toCompile List of file to compile * @param toRemove List of file to remove */ - private void mergeAidlFileModifications(ArrayList<IFile> toCompile, - ArrayList<IFile> toRemove) { - + private void mergeAidlFileModifications(ArrayList<AidlData> toCompile, + ArrayList<AidlData> toRemove) { // loop through the new toRemove list, and add it to the old one, // plus remove any file that was still to compile and that are now // removed - for (IFile r : toRemove) { + for (AidlData r : toRemove) { if (mAidlToRemove.indexOf(r) == -1) { mAidlToRemove.add(r); } @@ -1072,7 +930,7 @@ public class PreCompilerBuilder extends BaseBuilder { // Also look for them in the remove list, this would mean that they // were removed, then added back, and we shouldn't remove them, just // recompile them. - for (IFile r : toCompile) { + for (AidlData r : toCompile) { if (mAidlToCompile.indexOf(r) == -1) { mAidlToCompile.add(r); } @@ -1083,68 +941,4 @@ public class PreCompilerBuilder extends BaseBuilder { } } } - - /** - * Prepare an already existing file for modification. File generated from - * command line processed are marked as read-only. This method prepares - * them (mark them as read-write) before the command line process is - * started. A check is made to be sure the file exists. - * @param file The IResource object for the file to prepare. - * @throws CoreException - */ - private void prepareFileForExternalModification(IFile file) - throws CoreException { - // file may not exist yet, so we check that. - if (file != null && file.exists()) { - // get the attributes. - ResourceAttributes ra = file.getResourceAttributes(); - if (ra != null) { - // change the attributes - ra.setReadOnly(false); - - // set the new attributes in the file. - file.setResourceAttributes(ra); - } - } - } - - /** - * Finish a file created/modified by an outside command line process. - * The file is marked as modified by Android, and the parent folder is refreshed, so that, - * in case the file didn't exist beforehand, the file appears in the package explorer. - * @param rFile The R file to "finish". - * @param manifestFile The manifest file to "finish". - * @throws CoreException - */ - private void finishJavaFilesAfterExternalModification(IFile rFile, IFile manifestFile) - throws CoreException { - IContainer parent = rFile.getParent(); - - IProgressMonitor monitor = new DerivedProgressMonitor(rFile, manifestFile); - - // refresh the parent node in the package explorer. Once this is done the custom progress - // monitor will mark them as derived. - parent.refreshLocal(IResource.DEPTH_ONE, monitor); - } - - /** - * Finish a file created/modified by an outside command line process. - * The file is marked as modified by Android, and the parent folder is refreshed, so that, - * in case the file didn't exist beforehand, the file appears in the package explorer. - * @param file The file to "finish". - * @param aidlFile The AIDL file to "finish". - * @throws CoreException - */ - private void finishFileAfterExternalModification(IFile file, IFile aidlFile) - throws CoreException { - IContainer parent = file.getParent(); - - // we need to add a link to the aidl file. - // We need to wait for the refresh of the parent to be done, so we'll do - // it in the monitor. This will also set the file as derived. - IProgressMonitor monitor = new RefreshProgressMonitor(file, aidlFile); - - // refresh the parent node in the package explorer. - parent.refreshLocal(IResource.DEPTH_ONE, monitor); - } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java index f4778d7..6841830 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerDeltaVisitor.java @@ -19,7 +19,7 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor; -import com.android.ide.eclipse.adt.project.ProjectHelper; +import com.android.ide.eclipse.adt.build.PreCompilerBuilder.AidlData; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; @@ -31,14 +31,25 @@ import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; import java.util.ArrayList; /** * Resource Delta visitor for the pre-compiler. + * <p/>This delta visitor only cares about files that are the source or the result of actions of the + * {@link PreCompilerBuilder}: + * <ul><li>R.java/Manifest.java generated by compiling the resources</li> + * <li>Any Java files generated by <code>aidl</code></li></ul>. + * + * Therefore it looks for the following: + * <ul><li>Any modification in the resource folder</li> + * <li>Removed files from the source folder receiving generated Java files</li> + * <li>Any modification to aidl files.</li> + * */ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements IResourceDeltaVisitor { @@ -53,14 +64,11 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements private boolean mCompileResources = false; /** List of .aidl files found that are modified or new. */ - private final ArrayList<IFile> mAidlToCompile = new ArrayList<IFile>(); + private final ArrayList<AidlData> mAidlToCompile = new ArrayList<AidlData>(); /** List of .aidl files that have been removed. */ - private final ArrayList<IFile> mAidlToRemove = new ArrayList<IFile>(); + private final ArrayList<AidlData> mAidlToRemove = new ArrayList<AidlData>(); - /** Aidl forced recompilation flag. This is set to true if project.aidl is modified. */ - private boolean mFullAidlCompilation = false; - /** Manifest check/parsing flag. */ private boolean mCheckedManifestXml = false; @@ -75,36 +83,36 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements private boolean mInRes = false; /** - * In Source folder flag. This allows us to know if we're in a source - * folder. + * Current Source folder. This allows us to know if we're in a source + * folder, and which folder. */ - private boolean mInSrc = false; + private IFolder mSourceFolder = null; /** List of source folders. */ private ArrayList<IPath> mSourceFolders; + private boolean mIsGenSourceFolder = false; + + private IWorkspaceRoot mRoot; public PreCompilerDeltaVisitor(BaseBuilder builder, ArrayList<IPath> sourceFolders) { super(builder); mSourceFolders = sourceFolders; + mRoot = ResourcesPlugin.getWorkspace().getRoot(); } public boolean getCompileResources() { return mCompileResources; } - public ArrayList<IFile> getAidlToCompile() { + public ArrayList<AidlData> getAidlToCompile() { return mAidlToCompile; } - public ArrayList<IFile> getAidlToRemove() { + public ArrayList<AidlData> getAidlToRemove() { return mAidlToRemove; } - public boolean getFullAidlRecompilation() { - return mFullAidlCompilation; - } - /** * Returns whether the manifest file was parsed/checked for error during the resource delta * visiting. @@ -149,11 +157,13 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // since the delta visitor also visits the root we return true if // segments.length = 1 if (segments.length == 1) { + // FIXME: check this is an Android project. return true; } else if (segments.length == 2) { // if we are at an item directly under the root directory, // then we are not yet in a source or resource folder - mInRes = mInSrc = false; + mInRes = false; + mSourceFolder = null; if (SdkConstants.FD_RESOURCES.equalsIgnoreCase(segments[1])) { // this is the resource folder that was modified. we want to @@ -162,7 +172,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // since we're going to visit its children next, we set the // flag mInRes = true; - mInSrc = false; + mSourceFolder = null; return true; } else if (AndroidConstants.FN_ANDROID_MANIFEST.equalsIgnoreCase(segments[1])) { // any change in the manifest could trigger a new R.java @@ -183,9 +193,6 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // we don't want to go to the children, not like they are // any for this resource anyway. return false; - } else if (AndroidConstants.FN_PROJECT_AIDL.equalsIgnoreCase(segments[1])) { - // need to force recompilation of all the aidl files - mFullAidlCompilation = true; } } @@ -198,7 +205,7 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // so first we test if we already know we are in a source or // resource folder. - if (mInSrc) { + if (mSourceFolder != null) { // if we are in the res folder, we are looking for the following changes: // - added/removed/modified aidl files. // - missing R.java file @@ -216,130 +223,84 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements // get the modification kind int kind = delta.getKind(); - if (kind == IResourceDelta.ADDED) { - // we only care about added files (inside the source folders), if they - // are aidl files. + // we process normal source folder and the 'gen' source folder differently. + if (mIsGenSourceFolder) { + // this is the generated java file source folder. + // - if R.java/Manifest.java are removed/modified, we recompile the resources + // - if aidl files are removed/modified, we recompile them. - // get the extension of the resource - String ext = resource.getFileExtension(); + boolean outputWarning = false; - if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) { - // look for an already existing matching java file - String javaName = resource.getName().replaceAll( - AndroidConstants.RE_AIDL_EXT, - AndroidConstants.DOT_JAVA); - - // get the parent container - IContainer ic = resource.getParent(); - - IFile javaFile = ic.getFile(new Path(javaName)); - if (javaFile != null && javaFile.exists()) { - // check if that file was generated by the plugin. Normally those files are - // deleted automatically, but it's better to check. - String aidlPath = ProjectHelper.loadStringProperty(javaFile, - PreCompilerBuilder.PROPERTY_ANDROID_GENERATED); - if (aidlPath == null) { - // mark the aidl file that it cannot be compile just yet - ProjectHelper.saveBooleanProperty(file, - PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, true); - } + String fileName = resource.getName(); - // we add it anyway so that we can try to compile it at every compilation - // until the conflict is fixed. - mAidlToCompile.add(file); + // Special case of R.java/Manifest.java. + if (AndroidConstants.FN_RESOURCE_CLASS.equals(fileName) || + AndroidConstants.FN_MANIFEST_CLASS.equals(fileName)) { + // if it was removed, there's a possibility that it was removed due to a + // package change, or an aidl that was removed, but the only thing + // that will happen is that we'll have an extra build. Not much of a problem. + mCompileResources = true; - } else { - // the java file doesn't exist, we can safely add the file to the list - // of files to compile. - mAidlToCompile.add(file); + // we want a warning + outputWarning = true; + } else { + // this has to be a Java file created from an aidl file. + // Look for the source aidl file in all the source folders. + String aidlFileName = fileName.replaceAll(AndroidConstants.RE_JAVA_EXT, + AndroidConstants.DOT_AIDL); + + for (IPath sourceFolderPath : mSourceFolders) { + // do not search in the current source folder as it is the 'gen' folder. + if (sourceFolderPath.equals(mSourceFolder.getFullPath())) { + continue; + } + + IFolder sourceFolder = getFolder(sourceFolderPath); + if (sourceFolder != null) { + // go recursively, segment by segment. + // index starts at 2 (0 is project, 1 is 'gen' + IFile sourceFile = findFile(sourceFolder, segments, 2, aidlFileName); + + if (sourceFile != null) { + // found the source. add it to the list of files to compile + mAidlToCompile.add(new AidlData(sourceFolder, sourceFile)); + outputWarning = true; + break; + } + } } } - return false; - } - - // get the filename - String fileName = segments[segments.length - 1]; - - boolean outputMessage = false; - - // Special case of R.java/Manifest.java. - // FIXME: This does not check the package. Any modification of R.java/Manifest.java in another project will trigger a new recompilation of the resources. - if (AndroidConstants.FN_RESOURCE_CLASS.equals(fileName) || - AndroidConstants.FN_MANIFEST_CLASS.equals(fileName)) { - // if it was removed, there's a possibility that it was removed due to a - // package change, or an aidl that was removed, but the only thing - // that will happen is that we'll have an extra build. Not much of a problem. - mCompileResources = true; + if (outputWarning) { + if (kind == IResourceDelta.REMOVED) { + // We pring an error just so that it's red, but it's just a warning really. + String msg = String.format(Messages.s_Removed_Recreating_s, fileName); + AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg); + } else if (kind == IResourceDelta.CHANGED) { + // the file was modified manually! we can't allow it. + String msg = String.format(Messages.s_Modified_Manually_Recreating_s, + fileName); + AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg); + } + } - // we want a warning - outputMessage = true; } else { + // this is another source folder. + // We only care about aidl files being added/modified/removed. // get the extension of the resource String ext = resource.getFileExtension(); - if (AndroidConstants.EXT_AIDL.equalsIgnoreCase(ext)) { if (kind == IResourceDelta.REMOVED) { - mAidlToRemove.add(file); + // we'll have to remove the generated file. + mAidlToRemove.add(new AidlData(mSourceFolder, file)); } else { - mAidlToCompile.add(file); - } - } else { - if (kind == IResourceDelta.REMOVED) { - // the file has been removed. we need to check it's a java file and that - // there's a matching aidl file. We can't check its persistent storage - // anymore. - if (AndroidConstants.EXT_JAVA.equalsIgnoreCase(ext)) { - String aidlFile = resource.getName().replaceAll( - AndroidConstants.RE_JAVA_EXT, - AndroidConstants.DOT_AIDL); - - // get the parent container - IContainer ic = resource.getParent(); - - IFile f = ic.getFile(new Path(aidlFile)); - if (f != null && f.exists() ) { - // make sure that the aidl file is not in conflict anymore, in - // case the java file was not generated by us. - if (ProjectHelper.loadBooleanProperty(f, - PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, false)) { - ProjectHelper.saveBooleanProperty(f, - PreCompilerBuilder.PROPERTY_ANDROID_CONFLICT, false); - } else { - outputMessage = true; - } - mAidlToCompile.add(f); - } - } - } else { - // check if it's an android generated java file. - IResource aidlSource = ProjectHelper.loadResourceProperty( - file, PreCompilerBuilder.PROPERTY_ANDROID_GENERATED); - - if (aidlSource != null && aidlSource.exists() && - aidlSource.getType() == IResource.FILE) { - // it looks like this was a java file created from an aidl file. - // we need to add the aidl file to the list of aidl file to compile - mAidlToCompile.add((IFile)aidlSource); - outputMessage = true; - } + // add the aidl file to the list of file to (re)compile + mAidlToCompile.add(new AidlData(mSourceFolder, file)); } } } - if (outputMessage) { - if (kind == IResourceDelta.REMOVED) { - // We pring an error just so that it's red, but it's just a warning really. - String msg = String.format(Messages.s_Removed_Recreating_s, fileName); - AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg); - } else if (kind == IResourceDelta.CHANGED) { - // the file was modified manually! we can't allow it. - String msg = String.format(Messages.s_Modified_Manually_Recreating_s, fileName); - AdtPlugin.printErrorToConsole(mBuilder.getProject(), msg); - } - } - // no children. return false; } else if (mInRes) { @@ -403,19 +364,25 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements } } else if (resource instanceof IFolder) { // in this case we may be inside a folder that contains a source - // folder. - String[] sourceFolderSegments = findMatchingSourceFolder(mSourceFolders, segments); - if (sourceFolderSegments != null) { - // we have a match! - mInRes = false; - - // Check if the current folder is actually a source folder - if (sourceFolderSegments.length == segments.length) { - mInSrc = true; + // folder, go through the list of known source folders + + for (IPath sourceFolderPath : mSourceFolders) { + // first check if they match exactly. + if (sourceFolderPath.equals(path)) { + // this is a source folder! + mInRes = false; + mSourceFolder = getFolder(sourceFolderPath); // all non null due to test above + mIsGenSourceFolder = path.segmentCount() == 2 && + path.segment(1).equals(SdkConstants.FD_GEN_SOURCES); + return true; } - // and return true to visit the content, no matter what - return true; + // check if we are on the way to a source folder. + int count = sourceFolderPath.matchingFirstSegments(path); + if (count == path.segmentCount()) { + mInRes = false; + return true; + } } // if we're here, we are visiting another folder @@ -429,4 +396,46 @@ class PreCompilerDeltaVisitor extends BaseDeltaVisitor implements return false; } + + /** + * Searches for and return a file in a folder. The file is defined by its segments, and a new + * name (replacing the last segment). + * @param folder the folder we are searching + * @param segments the segments of the file to search. + * @param index the index of the current segment we are looking for + * @param filename the new name to replace the last segment. + * @return the {@link IFile} representing the searched file, or null if not found + */ + private IFile findFile(IFolder folder, String[] segments, int index, String filename) { + boolean lastSegment = index == segments.length - 1; + IResource resource = folder.findMember(lastSegment ? filename : segments[index]); + if (resource != null && resource.exists()) { + if (lastSegment) { + if (resource.getType() == IResource.FILE) { + return (IFile)resource; + } + } else { + if (resource.getType() == IResource.FOLDER) { + return findFile((IFolder)resource, segments, index+1, filename); + } + } + } + return null; + } + + /** + * Returns a handle to the folder identified by the given path in this container. + * <p/>The different with {@link IContainer#getFolder(IPath)} is that this returns a non + * null object only if the resource actually exists and is a folder (and not a file) + * @param path the path of the folder to return. + * @return a handle to the folder if it exists, or null otherwise. + */ + private IFolder getFolder(IPath path) { + IResource resource = mRoot.findMember(path); + if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) { + return (IFolder)resource; + } + + return null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java index 19d7185..0255f9f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ResourceManagerBuilder.java @@ -19,19 +19,27 @@ package com.android.ide.eclipse.adt.build; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.ProjectHelper; -import com.android.ide.eclipse.adt.sdk.LoadStatus; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +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.SubProgressMonitor; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import java.util.ArrayList; import java.util.Map; /** @@ -56,6 +64,10 @@ 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); // Check the compiler compliance level, displaying the error message // since this is the first builder. @@ -83,8 +95,7 @@ public class ResourceManagerBuilder extends BaseBuilder { String osSdkFolder = AdtPlugin.getOsSdkFolder(); if (osSdkFolder == null || osSdkFolder.length() == 0) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, - Messages.No_SDK_Setup_Error); + AdtPlugin.printErrorToConsole(project, Messages.No_SDK_Setup_Error); markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error, IMarker.SEVERITY_ERROR); @@ -92,13 +103,6 @@ public class ResourceManagerBuilder extends BaseBuilder { stopBuild(Messages.No_SDK_Setup_Error); } - // check if we have finished loading the SDK. - if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) { - // we exit silently - // This interrupts the build. The next builders will not run. - stopBuild("SDK is not loaded yet"); - } - // check the project has a target IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project); if (projectTarget == null) { @@ -106,6 +110,85 @@ 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) { + if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) { + IPath path = e.getPath(); + if (path.segmentCount() == 2 && + path.segment(1).equals(SdkConstants.FD_GEN_SOURCES)) { + hasGenSrcFolder = true; + break; + } + } + } + } + + 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). + String message; + if (resource.getType() == IResource.FOLDER) { + // folder exists already! This is an error. If the folder had been created + // by the NewProjectWizard, it'd be a source folder. + message = String.format("%1$s already exists but is not a source folder. Convert to a source folder or rename it.", + resource.getFullPath().toString()); + } else { + // resource exists but is not a folder. + message = String.format( + "Resource %1$s is in the way. ADT needs a source folder called 'gen' to work. Rename or delete resource.", + resource.getFullPath().toString()); + } + + AdtPlugin.printErrorToConsole(project, message); + markProject(AdtConstants.MARKER_ADT, message, IMarker.SEVERITY_ERROR); + + // This interrupts the build. The next builders will not run. + stopBuild(message); + } else if (hasGenSrcFolder == false || genFolderPresent == false) { + // either there is no 'gen' source folder in the project (older SDK), + // or the folder does not exist (was deleted, or was a fresh svn checkout maybe.) + + // In case we are migrating from an older SDK, we go through the current source + // folders and delete the generated Java files. + ArrayList<IPath> sourceFolders = BaseProjectHelper.getSourceClasspaths(javaProject); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + for (IPath path : sourceFolders) { + IResource member = root.findMember(path); + if (member != null) { + removeDerivedResources(member, monitor); + } + } + + // create the new source folder, if needed + IFolder genFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES); + if (genFolderPresent == false) { + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, + "Creating 'gen' source folder for generated Java files"); + genFolder.create(true /* force */, true /* local */, + new SubProgressMonitor(monitor, 10)); + } + + // add it to the source folder list, if needed only (or it will throw) + if (hasGenSrcFolder == false) { + IClasspathEntry[] entries = javaProject.getRawClasspath(); + entries = ProjectHelper.addEntryToClasspath(entries, + JavaCore.newSourceEntry(genFolder.getFullPath())); + javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10)); + } + + // refresh the whole project + project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, 10)); + } // Check the preference to be sure we are supposed to refresh // the folders. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunch.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java index 3d60401..7029206 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunch.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.launching; +package com.android.ide.eclipse.adt.launch; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.ILaunchConfiguration; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index ac003df..9c5f09b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.launching; +package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.Client; @@ -28,8 +28,7 @@ import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.SyncService.SyncResult; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.debug.launching.DeviceChooserDialog.DeviceChooserResponse; -import com.android.ide.eclipse.adt.debug.ui.EmulatorConfigTab; +import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.project.AndroidManifestHelper; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java index a260350..275addf 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/DeviceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.launching; +package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.Client; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java index d919c1f..5b4cdbb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/EmulatorConfigTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.ui; +package com.android.ide.eclipse.adt.launch; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.ddms.DdmsPlugin; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegate.java new file mode 100644 index 0000000..7a74309 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegate.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2009 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.launch; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.common.AndroidConstants; +import com.android.sdklib.SdkConstants; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate; +import org.osgi.framework.Bundle; +import java.io.IOException; +import java.net.URL; + +/** + * <p> + * For Android projects, android.jar gets added to the launch configuration of + * JUnit tests as a bootstrap entry. This breaks JUnit tests as android.jar + * contains a skeleton version of JUnit classes and the JVM will stop with an error similar + * to: <blockquote> Error occurred during initialization of VM + * java/lang/NoClassDefFoundError: java/lang/ref/FinalReference </blockquote> + * <p> + * At compile time, Eclipse does not know that there is no valid junit.jar in + * the classpath since it can find a correct reference to all the necessary + * org.junit.* classes in the android.jar so it does not prompt the user to add + * the JUnit3 or JUnit4 jar. + * <p> + * This delegates removes the android.jar from the bootstrap path and if + * necessary also puts back the junit.jar in the user classpath. + * <p> + * This delegate will be present for both Java and Android projects (delegates + * setting instead of only the current project) but the behavior for Java + * projects should be neutral since: + * <ol> + * <li>Java tests can only compile (and then run) when a valid junit.jar is + * present + * <li>There is no android.jar in Java projects + * </ol> + */ +public class JUnitLaunchConfigDelegate extends JUnitLaunchConfigurationDelegate { + + private static final String JUNIT_JAR = "junit.jar"; //$NON-NLS-1$ + + @Override + public String[][] getBootpathExt(ILaunchConfiguration configuration) throws CoreException { + String[][] bootpath = super.getBootpathExt(configuration); + return fixBootpathExt(bootpath); + } + + @Override + public String[] getClasspath(ILaunchConfiguration configuration) throws CoreException { + String[] classpath = super.getClasspath(configuration); + return fixClasspath(classpath, getJavaProjectName(configuration)); + } + + /** + * Removes the android.jar from the bootstrap path if present. + * + * @param bootpath Array of Arrays of bootstrap class paths + * @return a new modified (if applicable) bootpath + */ + public static String[][] fixBootpathExt(String[][] bootpath) { + for (int i = 0; i < bootpath.length; i++) { + if (bootpath[i] != null) { + // we assume that the android.jar can only be present in the + // bootstrap path of android tests + if (bootpath[i][0].endsWith(SdkConstants.FN_FRAMEWORK_LIBRARY)) { + bootpath[i] = null; + } + } + } + return bootpath; + } + + /** + * Add the junit.jar to the user classpath; since Eclipse was relying on + * android.jar to provide the appropriate org.junit classes, it does not + * know it actually needs the junit.jar. + * + * @param classpath Array containing classpath + * @param projectName The name of the project (for logging purposes) + * + * @return a new modified (if applicable) classpath + */ + public static String[] fixClasspath(String[] classpath, String projectName) { + // search for junit.jar; if any are found return immediately + for (int i = 0; i < classpath.length; i++) { + if (classpath[i].endsWith(JUNIT_JAR)) { + return classpath; + } + } + + // This delegate being called without a junit.jar present is only + // possible for Android projects. In a non-Android project, the test + // would not compile and would be unable to run. + try { + // junit4 is backward compatible with junit3 and they uses the + // same junit.jar from bundle org.junit: + // When a project has mixed JUnit3 and JUnit4 tests, if JUnit3 jar + // is added first it is then replaced by the JUnit4 jar when user is + // prompted to fix the JUnit4 test failure + String jarLocation = getJunitJarLocation(); + // we extend the classpath by one element and append junit.jar + String[] newClasspath = new String[classpath.length + 1]; + System.arraycopy(classpath, 0, newClasspath, 0, classpath.length); + newClasspath[newClasspath.length - 1] = jarLocation; + classpath = newClasspath; + } catch (IOException e) { + // This should not happen as we depend on the org.junit + // plugin explicitly; the error is logged here so that the user can + // trace back the cause when the test fails to run + AdtPlugin.log(e, "Could not find a valid junit.jar"); + AdtPlugin.printErrorToConsole(projectName, + "Could not find a valid junit.jar"); + // Return the classpath as-is (with no junit.jar) anyway because we + // will let the actual launch config fails. + } + + return classpath; + } + + /** + * Returns the path of the junit jar in the highest version bundle. + * + * (This is public only so that the test can call it) + * + * @return the path as a string + * @throws IOException + */ + public static String getJunitJarLocation() throws IOException { + Bundle bundle = Platform.getBundle("org.junit"); //$NON-NLS-1$ + if (bundle == null) { + throw new IOException("Cannot find org.junit bundle"); + } + URL jarUrl = bundle.getEntry(AndroidConstants.WS_SEP + JUNIT_JAR); + return FileLocator.resolve(jarUrl).getFile(); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java index bbd320b..a46f56c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchConfigDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.launching; +package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.AndroidLaunchConfiguration; +import com.android.ide.eclipse.adt.launch.AndroidLaunchController.AndroidLaunchConfiguration; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/LaunchConfigTabGroup.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigTabGroup.java index c0dbd54..f1dbd26 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/LaunchConfigTabGroup.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigTabGroup.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.ui; +package com.android.ide.eclipse.adt.launch; import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; import org.eclipse.debug.ui.CommonTab; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchShortcut.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchShortcut.java index 92677f1..6b2744c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/launching/LaunchShortcut.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchShortcut.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.launching; +package com.android.ide.eclipse.adt.launch; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java index 6a40ed0..599da5f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/debug/ui/MainLaunchConfigTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.android.ide.eclipse.adt.debug.ui; +package com.android.ide.eclipse.adt.launch; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController; -import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ProjectChooserHelper; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java deleted file mode 100644 index a1b3c38..0000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/CreateAidlImportAction.java +++ /dev/null @@ -1,210 +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.project; - -import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.common.AndroidConstants; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IPackageFragment; -import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.IType; -import org.eclipse.jdt.core.ITypeHierarchy; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IObjectActionDelegate; -import org.eclipse.ui.IWorkbenchPart; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; - -/** - * Action going through all the source of a project and creating a pre-processed aidl file - * with all the custom parcelable classes. - */ -public class CreateAidlImportAction implements IObjectActionDelegate { - - private ISelection mSelection; - - public CreateAidlImportAction() { - // pass - } - - public void setActivePart(IAction action, IWorkbenchPart targetPart) { - // pass - } - - /* - * (non-Javadoc) - * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) - */ - public void run(IAction action) { - if (mSelection instanceof IStructuredSelection) { - for (Iterator<?> it = ((IStructuredSelection)mSelection).iterator(); it.hasNext();) { - Object element = it.next(); - IProject project = null; - if (element instanceof IProject) { - project = (IProject)element; - } else if (element instanceof IAdaptable) { - project = (IProject)((IAdaptable)element).getAdapter(IProject.class); - } - if (project != null) { - final IProject fproject = project; - new Job("Aidl preprocess") { - @Override - protected IStatus run(IProgressMonitor monitor) { - return createImportFile(fproject, monitor); - } - }.schedule(); - } - } - } - } - - public void selectionChanged(IAction action, ISelection selection) { - mSelection = selection; - } - - private IStatus createImportFile(IProject project, IProgressMonitor monitor) { - try { - if (monitor != null) { - monitor.beginTask(String.format( - "Creating aid preprocess file for %1$s", project.getName()), 1); - } - - ArrayList<String> parcelables = new ArrayList<String>(); - - IJavaProject javaProject = JavaCore.create(project); - - IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); - - for (IPackageFragmentRoot root : roots) { - if (root.isArchive() == false && root.isExternal() == false) { - parsePackageFragmentRoot(root, parcelables, monitor); - } - } - - // create the file with the parcelables - if (parcelables.size() > 0) { - IPath path = project.getLocation(); - path = path.append(AndroidConstants.FN_PROJECT_AIDL); - - File f = new File(path.toOSString()); - if (f.exists() == false) { - if (f.createNewFile() == false) { - return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - "Failed to create /project.aidl"); - } - } - - FileWriter fw = new FileWriter(f); - - fw.write("// This file is auto-generated by the\n"); - fw.write("// 'Create Aidl preprocess file for Parcelable classes'\n"); - fw.write("// action. Do not modify!\n\n"); - - for (String parcelable : parcelables) { - fw.write("parcelable "); //$NON-NLS-1$ - fw.write(parcelable); - fw.append(";\n"); //$NON-NLS-1$ - } - - fw.close(); - - // need to refresh the level just below the project to make sure it's being picked - // up by eclipse. - project.refreshLocal(IResource.DEPTH_ONE, monitor); - } - - if (monitor != null) { - monitor.worked(1); - monitor.done(); - } - - return Status.OK_STATUS; - } catch (JavaModelException e) { - return e.getJavaModelStatus(); - } catch (IOException e) { - return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, - "Failed to create /project.aidl", e); - } catch (CoreException e) { - return e.getStatus(); - } finally { - if (monitor != null) { - monitor.done(); - } - } - } - - private void parsePackageFragmentRoot(IPackageFragmentRoot root, - ArrayList<String> parcelables, IProgressMonitor monitor) throws JavaModelException { - - IJavaElement[] elements = root.getChildren(); - - for (IJavaElement element : elements) { - if (element instanceof IPackageFragment) { - ICompilationUnit[] compilationUnits = - ((IPackageFragment)element).getCompilationUnits(); - - for (ICompilationUnit unit : compilationUnits) { - IType[] types = unit.getTypes(); - - for (IType type : types) { - parseType(type, parcelables, monitor); - } - } - } - } - } - - private void parseType(IType type, ArrayList<String> parcelables, IProgressMonitor monitor) - throws JavaModelException { - // first look in this type if it somehow extends parcelable. - ITypeHierarchy typeHierarchy = type.newSupertypeHierarchy(monitor); - - IType[] superInterfaces = typeHierarchy.getAllSuperInterfaces(type); - for (IType superInterface : superInterfaces) { - if (AndroidConstants.CLASS_PARCELABLE.equals(superInterface.getFullyQualifiedName())) { - parcelables.add(type.getFullyQualifiedName()); - } - } - - // then look in inner types. - IType[] innerTypes = type.getTypes(); - - for (IType innerType : innerTypes) { - parseType(innerType, parcelables, monitor); - } - } -} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FixLaunchConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FixLaunchConfig.java index b8a0b0c..49bbd81 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FixLaunchConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FixLaunchConfig.java @@ -17,7 +17,7 @@ package com.android.ide.eclipse.adt.project; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.debug.launching.LaunchConfigDelegate; +import com.android.ide.eclipse.adt.launch.LaunchConfigDelegate; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java index 7fc3318..59b2b06 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/FolderDecorator.java @@ -39,7 +39,7 @@ public class FolderDecorator implements ILightweightLabelDecorator { private ImageDescriptor mDescriptor; public FolderDecorator() { - mDescriptor = AdtPlugin.getImageDescriptor("/icons/android_project.png"); + mDescriptor = AdtPlugin.getImageDescriptor("/icons/android_project.png"); //$NON-NLS-1$ } public void decorate(Object element, IDecoration decoration) { @@ -55,13 +55,13 @@ public class FolderDecorator implements ILightweightLabelDecorator { if (folder.getParent().getType() == IResource.PROJECT) { String name = folder.getName(); if (name.equals(SdkConstants.FD_ASSETS)) { - decorate(decoration, " [Android assets]"); - decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); + doDecoration(decoration, null); } else if (name.equals(SdkConstants.FD_RESOURCES)) { - decorate(decoration, " [Android resources]"); - decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); - } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) { - decorate(decoration, " [Native Libraries]"); + doDecoration(decoration, null); + } else if (name.equals(SdkConstants.FD_GEN_SOURCES)) { + doDecoration(decoration, " [Generated Java Files]"); + } else if (name.equals(SdkConstants.FD_NATIVE_LIBS)) { + doDecoration(decoration, null); } } } @@ -72,20 +72,24 @@ public class FolderDecorator implements ILightweightLabelDecorator { } } - public void decorate(IDecoration decoration, String suffix) { - decoration.addOverlay(mDescriptor, IDecoration.TOP_RIGHT); + public void doDecoration(IDecoration decoration, String suffix) { + decoration.addOverlay(mDescriptor, IDecoration.TOP_LEFT); - // this is broken as it changes the color of the whole text, not only of the decoration. - // TODO: figure out how to change the color of the decoration only. -// decoration.addSuffix(suffix); -// ITheme theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme(); -// ColorRegistry registry = theme.getColorRegistry(); -// decoration.setForegroundColor(registry.get("org.eclipse.jdt.ui.ColoredLabels.decorations")); + if (suffix != null) { + decoration.addSuffix(suffix); + + // this is broken as it changes the color of the whole text, not only of the decoration. + // TODO: figure out how to change the color of the decoration only. +// ITheme theme = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme(); +// ColorRegistry registry = theme.getColorRegistry(); +// decoration.setForegroundColor( +// registry.get("org.eclipse.jdt.ui.ColoredLabels.decorations")); //$NON-NLS-1$ + } } public boolean isLabelProperty(Object element, String property) { - // at this time return false. + // Property change do not affect the label return false; } @@ -93,13 +97,11 @@ public class FolderDecorator implements ILightweightLabelDecorator { // No state change will affect the rendering. } - - public void removeListener(ILabelProviderListener listener) { // No state change will affect the rendering. } public void dispose() { - // nothind to dispose + // nothing to dispose } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java index e201132..5abfd81 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java @@ -87,7 +87,6 @@ public class AndroidConstants { /** Name of the manifest file, i.e. "AndroidManifest.xml". */ public static final String FN_ANDROID_MANIFEST = "AndroidManifest.xml"; //$NON-NLS-1$ - public static final String FN_PROJECT_AIDL = "project.aidl"; //$NON-NLS-1$ /** Name of the android sources directory */ public static final String FD_ANDROID_SOURCES = "sources"; //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF index 266008c..c7f5ba8 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF +++ b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF @@ -13,7 +13,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.jdt.launching, org.eclipse.ui.views, com.android.ide.eclipse.ddms -Eclipse-LazyStart: true +Bundle-ActivationPolicy: lazy Bundle-Vendor: The Android Open Source Project Bundle-ClassPath: kxml2-2.3.0.jar, . diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegateTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegateTest.java new file mode 100644 index 0000000..df3745e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/launch/JUnitLaunchConfigDelegateTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2009 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.launch; + +import java.io.IOException; +import java.util.Arrays; +import junit.framework.TestCase; + +public class JUnitLaunchConfigDelegateTest extends TestCase { + + public void testAbleToFetchJunitJar() throws IOException { + assertTrue(JUnitLaunchConfigDelegate.getJunitJarLocation().endsWith("junit.jar")); + } + + public void testFixBootpathExtWithAndroidJar() { + String[][] testArray = { + null, + { "android.jar"}, + null, + { "some_other_jar.jar" }, + }; + + String[][] expectedArray = { + null, + null, + null, + { "some_other_jar.jar" }, + }; + + assertEqualsArrays(expectedArray, JUnitLaunchConfigDelegate.fixBootpathExt(testArray)); + } + + public void testFixBootpathExtWithNoAndroidJar() { + String[][] testArray = { + null, + { "somejar.jar"}, + null, + }; + + String[][] expectedArray = { + null, + { "somejar.jar"}, + null, + }; + + assertEqualsArrays(expectedArray, JUnitLaunchConfigDelegate.fixBootpathExt(testArray)); + } + + public void testFixClasspathWithJunitJar() throws IOException { + String[] testArray = { + JUnitLaunchConfigDelegate.getJunitJarLocation(), + }; + + String[] expectedArray = { + JUnitLaunchConfigDelegate.getJunitJarLocation(), + }; + + assertEqualsArrays(expectedArray, + JUnitLaunchConfigDelegate.fixClasspath(testArray, "test")); + } + + public void testFixClasspathWithoutJunitJar() throws IOException { + String[] testArray = { + "random.jar", + }; + + String[] expectedArray = { + "random.jar", + JUnitLaunchConfigDelegate.getJunitJarLocation(), + }; + + assertEqualsArrays(expectedArray, + JUnitLaunchConfigDelegate.fixClasspath(testArray, "test")); + } + + + public void testFixClasspathWithNoJars() throws IOException { + String[] testArray = { + }; + + String[] expectedArray = { + JUnitLaunchConfigDelegate.getJunitJarLocation(), + }; + + assertEqualsArrays(expectedArray, + JUnitLaunchConfigDelegate.fixClasspath(testArray, "test")); + } + + private void assertEqualsArrays(String[][] a1, String[][] a2) { + assertTrue(Arrays.deepEquals(a1, a2)); + } + + private void assertEqualsArrays(String[] a1, String[] a2) { + assertTrue(Arrays.deepEquals(a1, a2)); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java index 29538bb..6aaa209 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/EclipseTestCollector.java @@ -15,15 +15,14 @@ */ package com.android.ide.eclipse.tests; -import junit.framework.TestCase; -import junit.framework.TestSuite; - import org.eclipse.core.runtime.Plugin; import java.lang.reflect.Modifier; import java.net.URL; import java.util.Enumeration; -import java.util.logging.Logger; + +import junit.framework.TestCase; +import junit.framework.TestSuite; /** * Class for collecting all test cases in an eclipse plugin @@ -31,8 +30,6 @@ import java.util.logging.Logger; */ public class EclipseTestCollector { - private static final Logger sLogger = Logger.getLogger(EclipseTestCollector.class.getName()); - /** * Constructor */ @@ -49,13 +46,13 @@ public class EclipseTestCollector { */ public void addTestCases(TestSuite suite, Plugin plugin, String expectedPackage) { if (plugin != null) { - Enumeration entries = plugin.getBundle().findEntries("/", "*.class", true); + Enumeration<?> entries = plugin.getBundle().findEntries("/", "*.class", true); while (entries.hasMoreElements()) { URL entry = (URL)entries.nextElement(); String filePath = entry.getPath().replace(".class", ""); try { - Class testClass = getClass(filePath, expectedPackage); + Class<?> testClass = getClass(filePath, expectedPackage); if (isTestClass(testClass)) { suite.addTestSuite(testClass); } @@ -69,11 +66,11 @@ public class EclipseTestCollector { } /** - * Returns true if given class shouk\ld be added to suite + * Returns true if given class should be added to suite * @param testClass * @return */ - protected boolean isTestClass(Class testClass) { + protected boolean isTestClass(Class<?> testClass) { return TestCase.class.isAssignableFrom(testClass) && Modifier.isPublic(testClass.getModifiers()) && hasPublicConstructor(testClass); @@ -84,7 +81,7 @@ public class EclipseTestCollector { * @param testClass * @return */ - protected boolean hasPublicConstructor(Class testClass) { + protected boolean hasPublicConstructor(Class<?> testClass) { try { TestSuite.getTestConstructor(testClass); } catch(NoSuchMethodException e) { @@ -100,7 +97,7 @@ public class EclipseTestCollector { * @return * @throws ClassNotFoundException */ - protected Class getClass(String filePath, String expectedPackage) throws ClassNotFoundException { + protected Class<?> getClass(String filePath, String expectedPackage) throws ClassNotFoundException { String dotPath = filePath.replace('/', '.'); // remove the output folders, by finding where package name starts int index = dotPath.indexOf(expectedPackage); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java index ac928db..67e7cb2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/UnitTests.java @@ -49,7 +49,7 @@ public class UnitTests { * Override parent class to exclude functional tests */ @Override - protected boolean isTestClass(Class testClass) { + protected boolean isTestClass(Class<?> testClass) { return super.isTestClass(testClass) && !testClass.getPackage().getName().startsWith(FuncTests.FUNC_TEST_PACKAGE); } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java index 25a86c3..46e60ba 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/ConfigMatchTest.java @@ -31,7 +31,6 @@ import com.android.ide.eclipse.mock.FolderMock; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; import junit.framework.TestCase; @@ -47,7 +46,6 @@ public class ConfigMatchTest extends TestCase { private FolderConfiguration config2; private FolderConfiguration config1; - @SuppressWarnings("unchecked") @Override protected void setUp() throws Exception { super.setUp(); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/QualifierListTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/QualifierListTest.java index 6a555a4..0920f00 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/QualifierListTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/editors/resources/manager/QualifierListTest.java @@ -20,7 +20,6 @@ import com.android.ide.eclipse.editors.resources.configurations.FolderConfigurat import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier; import java.lang.reflect.Field; -import java.util.ArrayList; import junit.framework.TestCase; @@ -41,7 +40,6 @@ public class QualifierListTest extends TestCase { mManager = null; } - @SuppressWarnings("unchecked") public void testQualifierList() { try { // get the list of qualifier in the resource manager diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java index a95286c..2220ed1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java @@ -424,11 +424,13 @@ public class FileMock implements IFile { throw new NotImplementedException(); } - public Map getPersistentProperties() throws CoreException { + @SuppressWarnings("unchecked") + public Map getPersistentProperties() throws CoreException { throw new NotImplementedException(); } - public Map getSessionProperties() throws CoreException { + @SuppressWarnings("unchecked") + public Map getSessionProperties() throws CoreException { throw new NotImplementedException(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FolderMock.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FolderMock.java index 223deb0..73a69aa 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FolderMock.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FolderMock.java @@ -428,11 +428,11 @@ public final class FolderMock implements IFolder { throw new NotImplementedException(); } - public Map getPersistentProperties() throws CoreException { + public Map<?,?> getPersistentProperties() throws CoreException { throw new NotImplementedException(); } - public Map getSessionProperties() throws CoreException { + public Map<?,?> getSessionProperties() throws CoreException { throw new NotImplementedException(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/ProjectMock.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/ProjectMock.java index 4c409dc..0e6fde0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/ProjectMock.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/ProjectMock.java @@ -42,6 +42,7 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.net.URI; import java.util.Map; +@SuppressWarnings("deprecation") public class ProjectMock implements IProject { public void build(int kind, IProgressMonitor monitor) throws CoreException { @@ -95,7 +96,6 @@ public class ProjectMock implements IProject { throw new NotImplementedException(); } - @SuppressWarnings("deprecation") public IPath getPluginWorkingLocation(IPluginDescriptor plugin) { throw new NotImplementedException(); } @@ -459,6 +459,8 @@ public class ProjectMock implements IProject { throw new NotImplementedException(); } + + @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { throw new NotImplementedException(); } @@ -476,11 +478,11 @@ public class ProjectMock implements IProject { throw new NotImplementedException(); } - public Map getPersistentProperties() throws CoreException { + public Map<?,?> getPersistentProperties() throws CoreException { throw new NotImplementedException(); } - public Map getSessionProperties() throws CoreException { + public Map<?,?> getSessionProperties() throws CoreException { throw new NotImplementedException(); } diff --git a/ninepatch/src/com/android/ninepatch/NinePatch.java b/ninepatch/src/com/android/ninepatch/NinePatch.java index 39e05c6..35a1824 100644 --- a/ninepatch/src/com/android/ninepatch/NinePatch.java +++ b/ninepatch/src/com/android/ninepatch/NinePatch.java @@ -127,8 +127,7 @@ public class NinePatch { try { - if (mPatches.size() == 0 || mHorizontalPatches.size() == 0 || - mVerticalPatches.size() == 0) { + if (mPatches.size() == 0) { g.drawImage(mImage, x, y, scaledWidth, scaledHeight, null); return; } @@ -254,6 +253,14 @@ public class NinePatch { start = rect.x; } } + } else { + int start = -1; + for (Rectangle rect : mPatches) { + if (rect.x > start) { + mHorizontalPatchesSum += rect.width; + start = rect.x; + } + } } mVerticalPatchesSum = 0; @@ -265,6 +272,14 @@ public class NinePatch { start = rect.y; } } + } else { + int start = -1; + for (Rectangle rect : mPatches) { + if (rect.y > start) { + mVerticalPatchesSum += rect.height; + start = rect.y; + } + } } } @@ -286,28 +301,11 @@ public class NinePatch { boolean[] result = new boolean[1]; Pair<List<Pair<Integer>>> left = getPatches(column, result); mVerticalStartWithPatch = result[0]; - - // compute the min size, based on the list of fixed sections, which is stored in - // Pair.mFirst - mMinHeight = 0; - List<Pair<Integer>> fixedSections = left.mFirst; - for (Pair<Integer> section : fixedSections) { - mMinHeight += section.mSecond - section.mFirst; - } result = new boolean[1]; Pair<List<Pair<Integer>>> top = getPatches(row, result); mHorizontalStartWithPatch = result[0]; - // compute the min size, based on the list of fixed sections, which is stored in - // Pair.mFirst - - mMinWidth = 0; - fixedSections = top.mFirst; - for (Pair<Integer> section : fixedSections) { - mMinWidth += section.mSecond - section.mFirst; - } - mFixed = getRectangles(left.mFirst, top.mFirst); mPatches = getRectangles(left.mSecond, top.mSecond); @@ -315,7 +313,15 @@ public class NinePatch { mHorizontalPatches = getRectangles(left.mFirst, top.mSecond); mVerticalPatches = getRectangles(left.mSecond, top.mFirst); } else { - mHorizontalPatches = mVerticalPatches = new ArrayList<Rectangle>(0); + if (top.mFirst.size() > 0) { + mHorizontalPatches = new ArrayList<Rectangle>(0); + mVerticalPatches = getVerticalRectangles(top.mFirst); + } else if (left.mFirst.size() > 0) { + mHorizontalPatches = getHorizontalRectangles(left.mFirst); + mVerticalPatches = new ArrayList<Rectangle>(0); + } else { + mHorizontalPatches = mVerticalPatches = new ArrayList<Rectangle>(0); + } } row = GraphicsUtilities.getPixels(mImage, 0, height - 1, width, 1, row); @@ -326,31 +332,30 @@ public class NinePatch { left = getPatches(column, result); mVerticalPadding = getPadding(left.mFirst); - - mHorizontalPatchesSum = 0; - if (mHorizontalPatches.size() > 0) { - int start = -1; - for (Rectangle rect : mHorizontalPatches) { - if (rect.x > start) { - mHorizontalPatchesSum += rect.width; - start = rect.x; - } - } - } + } - mVerticalPatchesSum = 0; - if (mVerticalPatches.size() > 0) { - int start = -1; - for (Rectangle rect : mVerticalPatches) { - if (rect.y > start) { - mVerticalPatchesSum += rect.height; - start = rect.y; - } - } + private List<Rectangle> getVerticalRectangles(List<Pair<Integer>> topPairs) { + List<Rectangle> rectangles = new ArrayList<Rectangle>(); + for (Pair<Integer> top : topPairs) { + int x = top.mFirst; + int width = top.mSecond - top.mFirst; + + rectangles.add(new Rectangle(x, 1, width, mImage.getHeight() - 2)); } + return rectangles; + } + private List<Rectangle> getHorizontalRectangles(List<Pair<Integer>> leftPairs) { + List<Rectangle> rectangles = new ArrayList<Rectangle>(); + for (Pair<Integer> left : leftPairs) { + int y = left.mFirst; + int height = left.mSecond - left.mFirst; + + rectangles.add(new Rectangle(1, y, mImage.getWidth() - 2, height)); + } + return rectangles; } - + private Pair<Integer> getPadding(List<Pair<Integer>> pairs) { if (pairs.size() == 0) { return new Pair<Integer>(0, 0); @@ -366,14 +371,14 @@ public class NinePatch { pairs.get(index).mSecond - pairs.get(index).mFirst); } } - + private List<Rectangle> getRectangles(List<Pair<Integer>> leftPairs, List<Pair<Integer>> topPairs) { List<Rectangle> rectangles = new ArrayList<Rectangle>(); for (Pair<Integer> left : leftPairs) { int y = left.mFirst; int height = left.mSecond - left.mFirst; - for (Pair<Integer> top: topPairs) { + for (Pair<Integer> top : topPairs) { int x = top.mFirst; int width = top.mSecond - top.mFirst; @@ -382,7 +387,7 @@ public class NinePatch { } return rectangles; } - + private Pair<List<Pair<Integer>>> getPatches(int[] pixels, boolean[] startWithPatch) { int lastIndex = 1; int lastPixel = pixels[1]; @@ -390,7 +395,7 @@ public class NinePatch { List<Pair<Integer>> fixed = new ArrayList<Pair<Integer>>(); List<Pair<Integer>> patches = new ArrayList<Pair<Integer>>(); - + for (int i = 1; i < pixels.length - 1; i++) { int pixel = pixels[i]; if (pixel != lastPixel) { @@ -418,6 +423,7 @@ public class NinePatch { startWithPatch[0] = true; fixed.clear(); } + return new Pair<List<Pair<Integer>>>(fixed, patches); } |