diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java new file mode 100644 index 000000000..d61324937 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/IdeScanningContext.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.eclipse.org/org/documents/epl-v10.php + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.ide.eclipse.adt.internal.resources.manager; + +import static com.android.SdkConstants.ANDROID_URI; +import static com.android.ide.eclipse.adt.AdtConstants.MARKER_AAPT_COMPILE; +import static org.eclipse.core.resources.IResource.DEPTH_ONE; +import static org.eclipse.core.resources.IResource.DEPTH_ZERO; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.resources.ResourceRepository; +import com.android.ide.common.resources.ScanningContext; +import com.android.ide.common.resources.platform.AttributeInfo; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.build.AaptParser; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.utils.Pair; + +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.runtime.CoreException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * An {@link IdeScanningContext} is a specialized {@link ScanningContext} which + * carries extra information about the scanning state, such as which file is + * currently being scanned, and which files have been scanned in the past, such + * that at the end of a scan we can mark and clear errors, etc. + */ +public class IdeScanningContext extends ScanningContext { + private final IProject mProject; + private final List<IResource> mScannedResources = new ArrayList<IResource>(); + private IResource mCurrentFile; + private List<Pair<IResource, String>> mErrors; + private Set<IProject> mFullAaptProjects; + private boolean mValidate; + private Map<String, AttributeInfo> mAttributeMap; + private ResourceRepository mFrameworkResources; + + /** + * Constructs a new {@link IdeScanningContext} + * + * @param repository the associated {@link ResourceRepository} + * @param project the associated project + * @param validate if true, check that the attributes and resources are + * valid and if not request a full AAPT check + */ + public IdeScanningContext(@NonNull ResourceRepository repository, @NonNull IProject project, + boolean validate) { + super(repository); + mProject = project; + mValidate = validate; + + Sdk sdk = Sdk.getCurrent(); + if (sdk != null) { + AndroidTargetData targetData = sdk.getTargetData(project); + if (targetData != null) { + mAttributeMap = targetData.getAttributeMap(); + mFrameworkResources = targetData.getFrameworkResources(); + } + } + } + + @Override + public void addError(@NonNull String error) { + super.addError(error); + + if (mErrors == null) { + mErrors = new ArrayList<Pair<IResource,String>>(); + } + mErrors.add(Pair.of(mCurrentFile, error)); + } + + /** + * Notifies the context that the given resource is about to be scanned. + * + * @param resource the resource about to be scanned + */ + public void startScanning(@NonNull IResource resource) { + assert mCurrentFile == null : mCurrentFile; + mCurrentFile = resource; + mScannedResources.add(resource); + } + + /** + * Notifies the context that the given resource has been scanned. + * + * @param resource the resource that was scanned + */ + public void finishScanning(@NonNull IResource resource) { + assert mCurrentFile != null; + mCurrentFile = null; + } + + /** + * Process any errors found to add error markers in the affected files (and + * also clear up any aapt errors in files that are no longer applicable) + * + * @param async if true, delay updating markers until the next display + * thread event loop update + */ + public void updateMarkers(boolean async) { + // Run asynchronously? This is necessary for example when adding markers + // as the result of a resource change notification, since at that point the + // resource tree is locked for modifications and attempting to create a + // marker will throw a org.eclipse.core.internal.resources.ResourceException. + if (async) { + AdtPlugin.getDisplay().asyncExec(new Runnable() { + @Override + public void run() { + updateMarkers(false); + } + }); + return; + } + + // First clear out old/previous markers + for (IResource resource : mScannedResources) { + try { + if (resource.exists()) { + int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO; + resource.deleteMarkers(MARKER_AAPT_COMPILE, true, depth); + } + } catch (CoreException ce) { + // Pass + } + } + + // Add new errors + if (mErrors != null && mErrors.size() > 0) { + List<String> errors = new ArrayList<String>(); + for (Pair<IResource, String> pair : mErrors) { + errors.add(pair.getSecond()); + } + AaptParser.parseOutput(errors, mProject); + } + } + + @Override + public boolean needsFullAapt() { + // returns true if it was explicitly requested or if a file that has errors was modified. + // This handles the case where an edit doesn't add any new id but fix a compile error. + return super.needsFullAapt() || hasModifiedFilesWithErrors(); + } + + /** + * Returns true if any of the scanned resources has an error marker on it. + */ + private boolean hasModifiedFilesWithErrors() { + for (IResource resource : mScannedResources) { + try { + int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO; + if (resource.exists()) { + IMarker[] markers = resource.findMarkers(IMarker.PROBLEM, + true /*includeSubtypes*/, depth); + for (IMarker marker : markers) { + if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == + IMarker.SEVERITY_ERROR) { + return true; + } + } + } + } catch (CoreException ce) { + // Pass + } + } + + return false; + } + + @Override + protected void requestFullAapt() { + super.requestFullAapt(); + + if (mCurrentFile != null) { + if (mFullAaptProjects == null) { + mFullAaptProjects = new HashSet<IProject>(); + } + mFullAaptProjects.add(mCurrentFile.getProject()); + } else { + assert false : "No current context to apply IdeScanningContext to"; + } + } + + /** + * Returns the collection of projects that scanned resources have requested + * a full aapt for. + * + * @return a collection of projects that scanned resources requested full + * aapt runs for, or null + */ + public Collection<IProject> getAaptRequestedProjects() { + return mFullAaptProjects; + } + + @Override + public boolean checkValue(@Nullable String uri, @NonNull String name, @NonNull String value) { + if (!mValidate) { + return true; + } + + if (!needsFullAapt() && mAttributeMap != null && ANDROID_URI.equals(uri)) { + AttributeInfo info = mAttributeMap.get(name); + if (info != null && !info.isValid(value, mRepository, mFrameworkResources)) { + return false; + } + } + + return super.checkValue(uri, name, value); + } +} |