FileDocCategorySizeDatePackage
PreCompilerBuilder.javaAPI DocAndroid 1.5 API42096Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.build

PreCompilerBuilder

public class PreCompilerBuilder extends BaseBuilder
Pre Java Compiler. This incremental builder performs 2 tasks:
  • compiles the resources located in the res/ folder, along with the AndroidManifest.xml file into the R.java class.
  • compiles any .aidl files into a corresponding java file.

Fields Summary
public static final String
ID
private static final String
PROPERTY_PACKAGE
private static final String
PROPERTY_COMPILE_RESOURCES
private static final String
PROPERTY_COMPILE_AIDL
private static Pattern
sAidlPattern1
Single line aidl error
"<path>:<line>: <error>" or "<path>:<line> <error>"
private boolean
mMustCompileResources
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 final ArrayList
mAidlToCompile
List of .aidl files found that are modified or new.
private final ArrayList
mAidlToRemove
List of .aidl files that have been removed.
private String
mManifestPackage
cache of the java package defined in the manifest
private org.eclipse.core.resources.IFolder
mGenFolder
Output folder for generated Java File. Created on the Builder init
private DerivedProgressMonitor
mDerivedProgressMonitor
Progress monitor used at the end of every build to refresh the content of the 'gen' folder and set the generated files as derived.
Constructors Summary
public PreCompilerBuilder()

        super();
    
Methods Summary
protected org.eclipse.core.resources.IProject[]build(int kind, java.util.Map args, org.eclipse.core.runtime.IProgressMonitor monitor)

        try {
            mDerivedProgressMonitor.reset();

            // 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;
            int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK;
    
            if (kind == FULL_BUILD) {
                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
                        Messages.Start_Full_Pre_Compiler);
                mMustCompileResources = true;
                buildAidlCompilationList(project, sourceFolderPathList);
            } 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) {
                    mMustCompileResources = true;
                    buildAidlCompilationList(project, sourceFolderPathList);
                } else {
                    dv = new PreCompilerDeltaVisitor(this, sourceFolderPathList);
                    delta.accept(dv);
    
                    // record the state
                    mMustCompileResources |= dv.getCompileResources();
                    
                    if (dv.getForceAidlCompile()) {
                        buildAidlCompilationList(project, sourceFolderPathList);
                    } else {
                        // handle aidl modification, and update mMustCompileAidl
                        mergeAidlFileModifications(dv.getAidlToCompile(),
                                dv.getAidlToRemove());
                    }
                    
                    // get the java package from the visitor
                    javaPackage = dv.getManifestPackage();
                    minSdkVersion = dv.getMinSdkVersion();
                }
            }
    
            // 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 = AndroidManifestParser.getManifest(project);
    
            if (manifest == null) {
                String msg = String.format(Messages.s_File_Missing,
                        AndroidConstants.FN_ANDROID_MANIFEST);
                AdtPlugin.printErrorToConsole(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,
                            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();
                minSdkVersion = parser.getApiLevelRequirement();
            }

            if (minSdkVersion != AndroidManifestParser.INVALID_MIN_SDK &&
                    minSdkVersion < projectTarget.getApiVersionNumber()) {
                // check it against the target api level
                String msg = String.format(
                        "Manifest min SDK version (%1$d) is lower than project target API level (%2$d)",
                        minSdkVersion, projectTarget.getApiVersionNumber());
                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
                BaseProjectHelper.addMarker(manifest, AdtConstants.MARKER_ADT, msg,
                        IMarker.SEVERITY_WARNING);
            }

            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.printErrorToConsole(project, msg);
                markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
    
                // 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();
                }
    
                // 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$
                    }
                    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);
                    }
    
                    // 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());
                            }
                        }
    
                        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(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);
                    }
                }
            } else {
                // nothing to do
            }
    
            // 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;
    
private voidbuildAidlCompilationList(org.eclipse.core.resources.IProject project, java.util.ArrayList sourceFolderPathList)
Goes through the build paths and fills the list of aidl files to compile ({@link #mAidlToCompile}).

param
project The project.
param
sourceFolderPathList The list of source folder paths.

        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);
            }
        }
    
protected voidclean(org.eclipse.core.runtime.IProgressMonitor monitor)

        super.clean(monitor);

        AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, getProject(),
                Messages.Removing_Generated_Classes);

        // remove all the derived resources from the 'gen' source folder.
        removeDerivedResources(mGenFolder, monitor);
    
private voidcreateFolder(org.eclipse.core.resources.IFolder destinationFolder, org.eclipse.core.runtime.IProgressMonitor monitor)
Creates the destination folder. Because {@link IFolder#create(boolean, boolean, IProgressMonitor)} only works if the parent folder already exists, this goes and ensure that all the parent folders actually exist, or it creates them as well.

param
destinationFolder The folder to create
param
monitor the {@link IProgressMonitor},
throws
CoreException

        
        // check the parent exist and create if necessary.
        IContainer parent = destinationFolder.getParent();
        if (parent.getType() == IResource.FOLDER && parent.exists() == false) {
            createFolder((IFolder)parent, monitor);
        }

        // create the folder.
        destinationFolder.create(true /*force*/, true /*local*/,
                new SubProgressMonitor(monitor, 10));
    
private voiddeleteObsoleteGeneratedClass(java.lang.String filename, java.lang.String javaPackage)
Delete the a generated java class associated with the specified java package.

param
filename Name of the generated file to remove.
param
javaPackage the old java package

        if (javaPackage == null) {
            return;
        }
        
        IPath packagePath = getJavaPackagePath(javaPackage);
        IPath iPath = packagePath.append(filename);

        // Find a matching resource object.
        IResource javaFile = mGenFolder.findMember(iPath);
        if (javaFile != null && javaFile.exists() && javaFile.getType() == IResource.FILE) {
            try {
                // delete
                javaFile.delete(true, null);

                // refresh parent
                javaFile.getParent().refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());

            } catch (CoreException e) {
                // failed to delete it, the user will have to delete it manually.
                String message = String.format(Messages.Delete_Obsolete_Error,
                        javaFile.getFullPath());
                IProject project = getProject();
                AdtPlugin.printErrorToConsole(project, message);
                AdtPlugin.printErrorToConsole(project, e.getMessage());
            }
        }
    
private booleanexecAidl(java.lang.String[] command, org.eclipse.core.resources.IFile file)
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.
param
file The IFile object representing the aidl file being compiled.
return
false if the exec failed, and build needs to be aborted.

        // do the exec
        try {
            Process p = Runtime.getRuntime().exec(command);

            // list to store each line of stderr
            ArrayList<String> results = new ArrayList<String>();

            // get the output and return code from the process
            int result = grabProcessOutput(p, results);

            // attempt to parse the error output
            boolean error = parseAidlOutput(results, file);

            // If the process failed and we couldn't parse the output
            // we pring a message, mark the project and exit
            if (result != 0 && error == true) {
                // display the message in the console.
                AdtPlugin.printErrorToConsole(getProject(), results.toArray());

                // mark the project and exit
                markProject(AdtConstants.MARKER_ADT, Messages.Unparsed_AIDL_Errors,
                        IMarker.SEVERITY_ERROR);
                return false;
            }
        } catch (IOException e) {
            // mark the project and exit
            String msg = String.format(Messages.AIDL_Exec_Error, command[0]);
            markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
            return false;
        } catch (InterruptedException e) {
            // mark the project and exit
            String msg = String.format(Messages.AIDL_Exec_Error, command[0]);
            markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
            return false;
        }

        return true;
    
private org.eclipse.core.resources.IFilegetGenDestinationFile(com.android.ide.eclipse.adt.build.PreCompilerBuilder$AidlData aidlData, boolean createFolders, org.eclipse.core.runtime.IProgressMonitor monitor)
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

        // 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) {
            createFolder(destinationFolder, monitor);
        }
        
        // 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;
    
private org.eclipse.core.resources.IFoldergetGenManifestPackageFolder(org.eclipse.core.resources.IProject project)
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.
return
the {@link IFolder} that will contain the R class or null if the folder was not found.
throws
CoreException

        // 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);
    
private org.eclipse.core.runtime.IPathgetJavaPackagePath(java.lang.String javaPackageName)
Creates a relative {@link IPath} from a java package.

param
javaPackageName the java package.

        // 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());
    
private booleanhandleAidl(com.android.sdklib.IAndroidTarget projectTarget, java.util.ArrayList sourceFolders, org.eclipse.core.runtime.IProgressMonitor monitor)
Compiles aidl files into java. This will also removes old java files 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
monitor the projess monitor
returns
true if it did something
throws
CoreException

        if (mAidlToCompile.size() == 0 && mAidlToRemove.size() == 0) {
            return false;
        }

        // create the command line
        String[] command = new String[4 + sourceFolders.size()];
        int index = 0;
        command[index++] = projectTarget.getPath(IAndroidTarget.AIDL);
        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.
        IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); 
        for (IPath p : sourceFolders) {
            IFolder f = wsRoot.getFolder(p);
            command[index++] = "-I" + f.getLocation().toOSString(); //$NON-NLS-1$
        }

        // list of files that have failed compilation.
        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 (AidlData aidlFile : mAidlToRemove) {
            int pos = mAidlToCompile.indexOf(aidlFile);
            if (pos != -1) {
                mAidlToCompile.remove(pos);
            }
        }

        // loop until we've compile them all
        for (AidlData aidlData : mAidlToCompile) {
            // Remove the AIDL error markers from the aidl file
            removeMarkersFromFile(aidlData.aidlFile, AndroidConstants.MARKER_AIDL);

            // 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[index] = osSourcePath;
            command[index + 1] = javaFile.getLocation().toOSString();

            // launch the process
            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(aidlData);

                // and we move on to the next one.
                continue;
            } else {
                // make sure the file will be marked as derived once we refresh the 'gen' source
                // folder.
                mDerivedProgressMonitor.addFile(javaFile);
            }
        }

        // change the list to only contains the file that have failed compilation
        mAidlToCompile.clear();
        mAidlToCompile.addAll(stillNeedCompilation);

        // Remove the java files created from aidl files that have been removed.
        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;
    
private voidmergeAidlFileModifications(java.util.ArrayList toCompile, java.util.ArrayList toRemove)
Merge the current list of aidl file to compile/remove with the new one.

param
toCompile List of file to compile
param
toRemove List of file to remove

        // 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 (AidlData r : toRemove) {
            if (mAidlToRemove.indexOf(r) == -1) {
                mAidlToRemove.add(r);
            }

            int index = mAidlToCompile.indexOf(r);
            if (index != -1) {
                mAidlToCompile.remove(index);
            }
        }

        // now loop through the new files to compile and add it to the list.
        // 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 (AidlData r : toCompile) {
            if (mAidlToCompile.indexOf(r) == -1) {
                mAidlToCompile.add(r);
            }

            int index = mAidlToRemove.indexOf(r);
            if (index != -1) {
                mAidlToRemove.remove(index);
            }
        }
    
private booleanparseAidlOutput(java.util.ArrayList lines, org.eclipse.core.resources.IFile file)
Parse the output of aidl and mark the file with any errors.

param
lines The output to parse.
param
file The file to mark with error.
return
true if the parsing failed, false if success.

        // nothing to parse? just return false;
        if (lines.size() == 0) {
            return false;
        }

        Matcher m;

        for (int i = 0; i < lines.size(); i++) {
            String p = lines.get(i);

            m = sAidlPattern1.matcher(p);
            if (m.matches()) {
                // we can ignore group 1 which is the location since we already
                // have a IFile object representing the aidl file.
                String lineStr = m.group(2);
                String msg = m.group(3);

                // get the line number
                int line = 0;
                try {
                    line = Integer.parseInt(lineStr);
                } catch (NumberFormatException e) {
                    // looks like the string we extracted wasn't a valid
                    // file number. Parsing failed and we return true
                    return true;
                }

                // mark the file
                BaseProjectHelper.addMarker(file, AndroidConstants.MARKER_AIDL, msg, line,
                        IMarker.SEVERITY_ERROR);

                // success, go to the next line
                continue;
            }

            // invalid line format, flag as error, and bail
            return true;
        }

        return false;
    
private voidscanFolderForAidl(org.eclipse.core.resources.IFolder sourceFolder, org.eclipse.core.resources.IFolder folder)
Scans a folder and fills the list of aidl files to compile.

param
sourceFolder the root source folder.
param
folder The folder to scan.

        try {
            IResource[] members = folder.members();
            for (IResource r : members) {
                // get the type of the resource
               switch (r.getType()) {
                   case IResource.FILE:
                       // if this a file, check that the file actually exist
                       // and that it's an aidl file
                       if (r.exists() &&
                               AndroidConstants.EXT_AIDL.equalsIgnoreCase(r.getFileExtension())) {
                           mAidlToCompile.add(new AidlData(sourceFolder, (IFile)r));
                       }
                       break;
                   case IResource.FOLDER:
                       // recursively go through children
                       scanFolderForAidl(sourceFolder, (IFolder)r);
                       break;
                   default:
                       // this would mean it's a project or the workspace root
                       // which is unlikely to happen. we do nothing
                       break;
               }
            }
        } catch (CoreException e) {
            // Couldn't get the members list for some reason. Just return.
        }
    
protected voidstartupOnInitialize()

        super.startupOnInitialize();
        
        mDerivedProgressMonitor = new DerivedProgressMonitor();
        
        IProject project = getProject();

        // load the previous IFolder and java package.
        mManifestPackage = loadProjectStringProperty(PROPERTY_PACKAGE);
        
        // get the source folder in which all the Java files are created
        mGenFolder = project.getFolder(SdkConstants.FD_GEN_SOURCES);

        // Load the current compile flags. We ask for true if not found to force a
        // recompile.
        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);
        }