PreCompilerDeltaVisitorpublic class PreCompilerDeltaVisitor extends com.android.ide.eclipse.adt.build.BaseBuilder.BaseDeltaVisitor implements org.eclipse.core.resources.IResourceDeltaVisitorResource 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 | mCompileResourcesCompile 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 | mForceAidlCompileAidl force recompilation flag. If true, we'll attempt to recompile all aidl files. | private final ArrayList | mAidlToCompileList of .aidl files found that are modified or new. | private final ArrayList | mAidlToRemoveList of .aidl files that have been removed. | private boolean | mCheckedManifestXmlManifest check/parsing flag. | private String | mJavaPackageApplication Package, gathered from the parsing of the manifest | private int | mMinSdkVersionminSDKVersion attribute value, gathered from the parsing of the manifest | private boolean | mInResIn Resource folder flag. This allows us to know if we're in the
resource folder. | private org.eclipse.core.resources.IFolder | mSourceFolderCurrent Source folder. This allows us to know if we're in a source
folder, and which folder. | private ArrayList | mSourceFoldersList 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.IFile | findFile(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).
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.ArrayList | getAidlToCompile()
return mAidlToCompile;
| public java.util.ArrayList | getAidlToRemove()
return mAidlToRemove;
| private com.android.ide.eclipse.adt.build.PreCompilerDeltaVisitor$AidlType | getAidlType(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}.
// 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 boolean | getCheckedManifestXml()Returns whether the manifest file was parsed/checked for error during the resource delta
visiting.
return mCheckedManifestXml;
| public boolean | getCompileResources()
return mCompileResources;
| private org.eclipse.core.resources.IFolder | getFolder(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)
IResource resource = mRoot.findMember(path);
if (resource != null && resource.exists() && resource.getType() == IResource.FOLDER) {
return (IFolder)resource;
}
return null;
| public boolean | getForceAidlCompile()
return mForceAidlCompile;
| public java.lang.String | getManifestPackage()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 mJavaPackage;
| public int | getMinSdkVersion()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 mMinSdkVersion;
| public boolean | visit(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;
|
|