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

PreCompilerDeltaVisitor

public class PreCompilerDeltaVisitor extends com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor implements org.eclipse.core.resources.IResourceDeltaVisitor
Resource Delta visitor for the pre-compiler.

This delta visitor only cares about files that are the source or the result of actions of the {@link PreCompilerBuilder}:

  • R.java/Manifest.java generated by compiling the resources
  • Any Java files generated by aidl
. Therefore it looks for the following:
  • Any modification in the resource folder
  • Removed files from the source folder receiving generated Java files
  • Any modification to aidl files.

Fields Summary
private boolean
mCompileResources
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
private boolean
mForceAidlCompile
Aidl force recompilation flag. If true, we'll attempt to recompile all aidl files.
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 boolean
mCheckedManifestXml
Manifest check/parsing flag.
private String
mJavaPackage
Application Package, gathered from the parsing of the manifest
private int
mMinSdkVersion
minSDKVersion attribute value, gathered from the parsing of the manifest
private boolean
mInRes
In Resource folder flag. This allows us to know if we're in the resource folder.
private org.eclipse.core.resources.IFolder
mSourceFolder
Current Source folder. This allows us to know if we're in a source folder, and which folder.
private ArrayList
mSourceFolders
List of source folders.
private boolean
mIsGenSourceFolder
private org.eclipse.core.resources.IWorkspaceRoot
mRoot
Constructors Summary
public PreCompilerDeltaVisitor(BaseBuilder builder, ArrayList sourceFolders)



         
        super(builder);
        mSourceFolders = sourceFolders;
        mRoot = ResourcesPlugin.getWorkspace().getRoot();
    
Methods Summary
private org.eclipse.core.resources.IFilefindFile(org.eclipse.core.resources.IFolder folder, java.lang.String[] segments, int index, java.lang.String filename)
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

        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;
    
public java.util.ArrayListgetAidlToCompile()

        return mAidlToCompile;
    
public java.util.ArrayListgetAidlToRemove()

        return mAidlToRemove;
    
private com.android.ide.eclipse.adt.build.PreCompilerDeltaVisitor$AidlTypegetAidlType(org.eclipse.core.resources.IFile file)
Returns the type of the aidl file. Aidl files can either declare interfaces, or declare parcelables. This method will attempt to parse the file and return the type. If the type cannot be determined, then it will return {@link AidlType#UNKNOWN}.

param
file The aidl file
return
the type of the aidl.
throws
CoreException

        // At this time, parsing isn't available, so we return UNKNOWN. This will force
        // a recompilation of all aidl file as soon as one is changed.
        return AidlType.UNKNOWN;

        // TODO: properly parse aidl file to determine type and generate dependency graphs.
//
//        String className = file.getName().substring(0,
//                file.getName().length() - AndroidConstants.DOT_AIDL.length());
//
//        InputStream input = file.getContents(true /* force*/);
//        try {
//            BufferedReader reader = new BufferedReader(new InputStreamReader(input));
//            String line;
//            while ((line = reader.readLine()) != null) {
//                if (line.length() == 0) {
//                    continue;
//                }
//
//                Matcher m = sParcelablePattern.matcher(line);
//                if (m.matches() && m.group(1).equals(className)) {
//                    return AidlType.PARCELABLE;
//                }
//
//                m = sInterfacePattern.matcher(line);
//                if (m.matches() && m.group(1).equals(className)) {
//                    return AidlType.INTERFACE;
//                }
//            }
//        } catch (IOException e) {
//            throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
//                    "Error parsing aidl file", e));
//        } finally {
//            try {
//                input.close();
//            } catch (IOException e) {
//                throw new CoreException(new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
//                        "Error parsing aidl file", e));
//            }
//        }
//
//        return AidlType.UNKNOWN;
    
public booleangetCheckedManifestXml()
Returns whether the manifest file was parsed/checked for error during the resource delta visiting.

        return mCheckedManifestXml;
    
public booleangetCompileResources()

        return mCompileResources;
    
private org.eclipse.core.resources.IFoldergetFolder(org.eclipse.core.runtime.IPath path)
Returns a handle to the folder identified by the given path in this container.

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.

        IResource resource = mRoot.findMember(path);
        if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) {
            return (IFolder)resource;
        }
        
        return null;
    
public booleangetForceAidlCompile()

        return mForceAidlCompile;
    
public java.lang.StringgetManifestPackage()
Returns the manifest package if the manifest was checked/parsed.

This can return null in two cases:

  • The manifest was not part of the resource change delta, and the manifest was not checked/parsed ({@link #getCheckedManifestXml()} returns false)
  • The manifest was parsed ({@link #getCheckedManifestXml()} returns true), but the package declaration is missing

return
the manifest package or null.

        return mJavaPackage;
    
public intgetMinSdkVersion()
Returns the minSDkVersion attribute from the manifest if it was checked/parsed.

This can return {@link AndroidManifestParser#INVALID_MIN_SDK} in two cases:

  • The manifest was not part of the resource change delta, and the manifest was not checked/parsed ({@link #getCheckedManifestXml()} returns false)
  • The manifest was parsed ({@link #getCheckedManifestXml()} returns true), but the package declaration is missing

return
the minSdkVersion or {@link AndroidManifestParser#INVALID_MIN_SDK}.

        return mMinSdkVersion;
    
public booleanvisit(org.eclipse.core.resources.IResourceDelta delta)

        // we are only going to look for changes in res/, source folders and in
        // AndroidManifest.xml since the delta visitor goes through the main
        // folder before its children we can check when the path segment
        // count is 2 (format will be /$Project/folder) and make sure we are
        // processing res/, source folders or AndroidManifest.xml

        IResource resource = delta.getResource();
        IPath path = resource.getFullPath();
        String[] segments = path.segments();

        // 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 = false;
            mSourceFolder = null;

            if (SdkConstants.FD_RESOURCES.equalsIgnoreCase(segments[1])) {
                // this is the resource folder that was modified. we want to
                // see its content.

                // since we're going to visit its children next, we set the
                // flag
                mInRes = true;
                mSourceFolder = null;
                return true;
            } else if (AndroidConstants.FN_ANDROID_MANIFEST.equalsIgnoreCase(segments[1])) {
                // any change in the manifest could trigger a new R.java
                // class, so we don't need to check the delta kind
                if (delta.getKind() != IResourceDelta.REMOVED) {
                    // parse the manifest for errors
                    AndroidManifestParser parser = BaseProjectHelper.parseManifestForError(
                            (IFile)resource, this);
                    
                    if (parser != null) {
                        mJavaPackage = parser.getPackage();
                        mMinSdkVersion = parser.getApiLevelRequirement();
                    }

                    mCheckedManifestXml = true;
                }
                mCompileResources = true;

                // we don't want to go to the children, not like they are
                // any for this resource anyway.
                return false;
            }
        }

        // at this point we can either be in the source folder or in the
        // resource folder or in a different folder that contains a source
        // folder.
        // This is due to not all source folder being src/. Some could be
        // something/somethingelse/src/

        // so first we test if we already know we are in a source or
        // resource folder.

        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

            // if the resource is a folder, we just go straight to the children
            if (resource.getType() == IResource.FOLDER) {
                return true;
            }

            if (resource.getType() != IResource.FILE) {
                return false;
            }
            IFile file = (IFile)resource;

            // get the modification kind
            int kind = delta.getKind();

            // 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.

                boolean outputWarning = false;

                String fileName = resource.getName();

                // 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;

                    // 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;
                            }
                        }
                    }
                }

                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);
                    }
                }
            } 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)) {
                    // first check whether it's a regular file or a parcelable.
                    AidlType type = getAidlType(file);
                    
                    if (type == AidlType.INTERFACE) {
                        if (kind == IResourceDelta.REMOVED) {
                            // we'll have to remove the generated file.
                            mAidlToRemove.add(new AidlData(mSourceFolder, file));
                        } else if (mForceAidlCompile == false) {
                            // add the aidl file to the list of file to (re)compile
                            mAidlToCompile.add(new AidlData(mSourceFolder, file));
                        }
                    } else {
                        // force recompilations of all Aidl Files.
                        mForceAidlCompile = true;
                        mAidlToCompile.clear();
                    }
                }
            }

            // no children.
            return false;
        } else if (mInRes) {
            // if we are in the res folder, we are looking for the following
            // changes:
            // - added/removed/modified xml files.
            // - added/removed files of any other type

            // if the resource is a folder, we just go straight to the
            // children
            if (resource.getType() == IResource.FOLDER) {
                return true;
            }

            // get the extension of the resource
            String ext = resource.getFileExtension();
            int kind = delta.getKind();

            String p = resource.getProjectRelativePath().toString();
            String message = null;
            switch (kind) {
                case IResourceDelta.CHANGED:
                    // display verbose message
                    message = String.format(Messages.s_Modified_Recreating_s, p,
                            AndroidConstants.FN_RESOURCE_CLASS);
                    break;
                case IResourceDelta.ADDED:
                    // display verbose message
                    message = String.format(Messages.Added_s_s_Needs_Updating, p,
                            AndroidConstants.FN_RESOURCE_CLASS);
                    break;
                case IResourceDelta.REMOVED:
                    // display verbose message
                    message = String.format(Messages.s_Removed_s_Needs_Updating, p,
                            AndroidConstants.FN_RESOURCE_CLASS);
                    break;
            }
            if (message != null) {
                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
                        mBuilder.getProject(), message);
            }

            if (AndroidConstants.EXT_XML.equalsIgnoreCase(ext)) {
                if (kind != IResourceDelta.REMOVED) {
                    // check xml Validity
                    mBuilder.checkXML(resource, this);
                }

                // if we are going through this resource, it was modified
                // somehow.
                // we don't care if it was an added/removed/changed event
                mCompileResources = true;
                return false;
            } else {
                // this is a non xml resource.
                if (kind == IResourceDelta.ADDED
                        || kind == IResourceDelta.REMOVED) {
                    mCompileResources = true;
                    return false;
                }
            }
        } else if (resource instanceof IFolder) {
            // in this case we may be inside a folder that contains a source
            // 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;
                }
                
                // 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
            // like /$Project/bin/ for instance (we get notified for changes
            // in .class!)
            // This could also be another source folder and we have found
            // R.java in a previous source folder
            // We don't want to visit its children
            return false;
        }

        return false;