• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.ide.eclipse.adt.internal.resources.manager;
17 
18 import static com.android.ide.eclipse.adt.AdtConstants.MARKER_AAPT_COMPILE;
19 import static org.eclipse.core.resources.IResource.DEPTH_ONE;
20 import static org.eclipse.core.resources.IResource.DEPTH_ZERO;
21 
22 import com.android.ide.common.resources.ResourceRepository;
23 import com.android.ide.common.resources.ScanningContext;
24 import com.android.ide.eclipse.adt.AdtPlugin;
25 import com.android.ide.eclipse.adt.internal.build.AaptParser;
26 import com.android.util.Pair;
27 
28 import org.eclipse.core.resources.IFolder;
29 import org.eclipse.core.resources.IMarker;
30 import org.eclipse.core.resources.IProject;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.runtime.CoreException;
33 
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39 
40 /**
41  * An {@link IdeScanningContext} is a specialized {@link ScanningContext} which
42  * carries extra information about the scanning state, such as which file is
43  * currently being scanned, and which files have been scanned in the past, such
44  * that at the end of a scan we can mark and clear errors, etc.
45  */
46 public class IdeScanningContext extends ScanningContext {
47     private final IProject mProject;
48     private final List<IResource> mScannedResources = new ArrayList<IResource>();
49     private IResource mCurrentFile;
50     private List<Pair<IResource, String>> mErrors;
51     private Set<IProject> mFullAaptProjects;
52 
53     /**
54      * Constructs a new {@link IdeScanningContext}
55      *
56      * @param repository the associated {@link ResourceRepository}
57      * @param project the associated project
58      */
IdeScanningContext(ResourceRepository repository, IProject project)59     public IdeScanningContext(ResourceRepository repository, IProject project) {
60         super(repository);
61         mProject = project;
62     }
63 
64     @Override
addError(String error)65     public void addError(String error) {
66         super.addError(error);
67 
68         if (mErrors == null) {
69             mErrors = new ArrayList<Pair<IResource,String>>();
70         }
71         mErrors.add(Pair.of(mCurrentFile, error));
72     }
73 
74     /**
75      * Notifies the context that the given resource is about to be scanned.
76      *
77      * @param resource the resource about to be scanned
78      */
startScanning(IResource resource)79     public void startScanning(IResource resource) {
80         assert mCurrentFile == null : mCurrentFile;
81         mCurrentFile = resource;
82         mScannedResources.add(resource);
83     }
84 
85     /**
86      * Notifies the context that the given resource has been scanned.
87      *
88      * @param resource the resource that was scanned
89      */
finishScanning(IResource resource)90     public void finishScanning(IResource resource) {
91         assert mCurrentFile != null;
92         mCurrentFile = null;
93     }
94 
95     /**
96      * Process any errors found to add error markers in the affected files (and
97      * also clear up any aapt errors in files that are no longer applicable)
98      *
99      * @param async if true, delay updating markers until the next display
100      *            thread event loop update
101      */
updateMarkers(boolean async)102     public void updateMarkers(boolean async) {
103         // Run asynchronously? This is necessary for example when adding markers
104         // as the result of a resource change notification, since at that point the
105         // resource tree is locked for modifications and attempting to create a
106         // marker will throw a org.eclipse.core.internal.resources.ResourceException.
107         if (async) {
108             AdtPlugin.getDisplay().asyncExec(new Runnable() {
109                 @Override
110                 public void run() {
111                     updateMarkers(false);
112                 }
113             });
114             return;
115         }
116 
117         // First clear out old/previous markers
118         for (IResource resource : mScannedResources) {
119             try {
120                 if (resource.exists()) {
121                     int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO;
122                     resource.deleteMarkers(MARKER_AAPT_COMPILE, true, depth);
123                 }
124             } catch (CoreException ce) {
125                 // Pass
126             }
127         }
128 
129         // Add new errors
130         if (mErrors != null && mErrors.size() > 0) {
131             List<String> errors = new ArrayList<String>();
132             for (Pair<IResource, String> pair : mErrors) {
133                 errors.add(pair.getSecond());
134             }
135             AaptParser.parseOutput(errors, mProject);
136         }
137     }
138 
139     @Override
needsFullAapt()140     public boolean needsFullAapt() {
141         // returns true if it was explicitly requested or if a file that has errors was modified.
142         // This handles the case where an edit doesn't add any new id but fix a compile error.
143         return super.needsFullAapt() || hasModifiedFilesWithErrors();
144     }
145 
146     /**
147      * Returns true if any of the scanned resources has an error marker on it.
148      */
hasModifiedFilesWithErrors()149     private boolean hasModifiedFilesWithErrors() {
150         for (IResource resource : mScannedResources) {
151             try {
152                 int depth = resource instanceof IFolder ? DEPTH_ONE : DEPTH_ZERO;
153                 if (resource.exists()) {
154                     IMarker[] markers = resource.findMarkers(IMarker.PROBLEM,
155                             true /*includeSubtypes*/, depth);
156                     for (IMarker marker : markers) {
157                         if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) ==
158                                 IMarker.SEVERITY_ERROR) {
159                             return true;
160                         }
161                     }
162                 }
163             } catch (CoreException ce) {
164                 // Pass
165             }
166         }
167 
168         return false;
169     }
170 
171     @Override
requestFullAapt()172     protected void requestFullAapt() {
173         super.requestFullAapt();
174 
175         if (mCurrentFile != null) {
176             if (mFullAaptProjects == null) {
177                 mFullAaptProjects = new HashSet<IProject>();
178             }
179             mFullAaptProjects.add(mCurrentFile.getProject());
180         } else {
181             assert false : "No current context to apply IdeScanningContext to";
182         }
183     }
184 
185     /**
186      * Returns the collection of projects that scanned resources have requested
187      * a full aapt for.
188      *
189      * @return a collection of projects that scanned resources requested full
190      *         aapt runs for, or null
191      */
getAaptRequestedProjects()192     public Collection<IProject> getAaptRequestedProjects() {
193         return mFullAaptProjects;
194     }
195 }
196