• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 
17 package com.android.ide.eclipse.adt.internal.build.builders;
18 
19 import com.android.ide.common.sdk.LoadStatus;
20 import com.android.ide.eclipse.adt.AdtConstants;
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.ide.eclipse.adt.internal.build.BuildHelper;
23 import com.android.ide.eclipse.adt.internal.build.Messages;
24 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
25 import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
26 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler;
27 import com.android.ide.eclipse.adt.internal.project.XmlErrorHandler.XmlErrorListener;
28 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
29 import com.android.sdklib.IAndroidTarget;
30 
31 import org.eclipse.core.resources.IContainer;
32 import org.eclipse.core.resources.IFile;
33 import org.eclipse.core.resources.IFolder;
34 import org.eclipse.core.resources.IMarker;
35 import org.eclipse.core.resources.IProject;
36 import org.eclipse.core.resources.IResource;
37 import org.eclipse.core.resources.IncrementalProjectBuilder;
38 import org.eclipse.core.runtime.CoreException;
39 import org.eclipse.core.runtime.IPath;
40 import org.eclipse.core.runtime.IProgressMonitor;
41 import org.eclipse.jdt.core.IJavaProject;
42 import org.xml.sax.SAXException;
43 
44 import java.util.ArrayList;
45 
46 import javax.xml.parsers.ParserConfigurationException;
47 import javax.xml.parsers.SAXParser;
48 import javax.xml.parsers.SAXParserFactory;
49 
50 /**
51  * Base builder for XML files. This class allows for basic XML parsing with
52  * error checking and marking the files for errors/warnings.
53  */
54 public abstract class BaseBuilder extends IncrementalProjectBuilder {
55 
56 
57     /** SAX Parser factory. */
58     private SAXParserFactory mParserFactory;
59 
60     /**
61      * Base Resource Delta Visitor to handle XML error
62      */
63     protected static class BaseDeltaVisitor implements XmlErrorListener {
64 
65         /** The Xml builder used to validate XML correctness. */
66         protected BaseBuilder mBuilder;
67 
68         /**
69          * XML error flag. if true, we keep parsing the ResourceDelta but the
70          * compilation will not happen (we're putting markers)
71          */
72         public boolean mXmlError = false;
73 
BaseDeltaVisitor(BaseBuilder builder)74         public BaseDeltaVisitor(BaseBuilder builder) {
75             mBuilder = builder;
76         }
77 
78         /**
79          * Finds a matching Source folder for the current path. This checks if the current path
80          * leads to, or is a source folder.
81          * @param sourceFolders The list of source folders
82          * @param pathSegments The segments of the current path
83          * @return The segments of the source folder, or null if no match was found
84          */
findMatchingSourceFolder(ArrayList<IPath> sourceFolders, String[] pathSegments)85         protected static String[] findMatchingSourceFolder(ArrayList<IPath> sourceFolders,
86                 String[] pathSegments) {
87 
88             for (IPath p : sourceFolders) {
89                 // check if we are inside one of those source class path
90 
91                 // get the segments
92                 String[] srcSegments = p.segments();
93 
94                 // compare segments. We want the path of the resource
95                 // we're visiting to be
96                 boolean valid = true;
97                 int segmentCount = pathSegments.length;
98 
99                 for (int i = 0 ; i < segmentCount; i++) {
100                     String s1 = pathSegments[i];
101                     String s2 = srcSegments[i];
102 
103                     if (s1.equalsIgnoreCase(s2) == false) {
104                         valid = false;
105                         break;
106                     }
107                 }
108 
109                 if (valid) {
110                     // this folder, or one of this children is a source
111                     // folder!
112                     // we return its segments
113                     return srcSegments;
114                 }
115             }
116 
117             return null;
118         }
119 
120         /**
121          * Sent when an XML error is detected.
122          * @see XmlErrorListener
123          */
errorFound()124         public void errorFound() {
125             mXmlError = true;
126         }
127     }
128 
129     protected static class AbortBuildException extends Exception {
130         private static final long serialVersionUID = 1L;
131     }
132 
BaseBuilder()133     public BaseBuilder() {
134         super();
135         mParserFactory = SAXParserFactory.newInstance();
136 
137         // FIXME when the compiled XML support for namespace is in, set this to true.
138         mParserFactory.setNamespaceAware(false);
139     }
140 
141     /**
142      * Checks an Xml file for validity. Errors/warnings will be marked on the
143      * file
144      * @param resource the resource to check
145      * @param visitor a valid resource delta visitor
146      */
checkXML(IResource resource, BaseDeltaVisitor visitor)147     protected final void checkXML(IResource resource, BaseDeltaVisitor visitor) {
148 
149         // first make sure this is an xml file
150         if (resource instanceof IFile) {
151             IFile file = (IFile)resource;
152 
153             // remove previous markers
154             removeMarkersFromResource(file, AdtConstants.MARKER_XML);
155 
156             // create  the error handler
157             XmlErrorHandler reporter = new XmlErrorHandler(file, visitor);
158             try {
159                 // parse
160                 getParser().parse(file.getContents(), reporter);
161             } catch (Exception e1) {
162             }
163         }
164     }
165 
166     /**
167      * Returns the SAXParserFactory, instantiating it first if it's not already
168      * created.
169      * @return the SAXParserFactory object
170      * @throws ParserConfigurationException
171      * @throws SAXException
172      */
getParser()173     protected final SAXParser getParser() throws ParserConfigurationException,
174             SAXException {
175         return mParserFactory.newSAXParser();
176     }
177 
178     /**
179      * Adds a marker to the current project.  This methods catches thrown {@link CoreException},
180      * and returns null instead.
181      *
182      * @param markerId The id of the marker to add.
183      * @param message the message associated with the mark
184      * @param severity the severity of the marker.
185      * @return the marker that was created (or null if failure)
186      * @see IMarker
187      */
markProject(String markerId, String message, int severity)188     protected final IMarker markProject(String markerId, String message, int severity) {
189         return BaseProjectHelper.markResource(getProject(), markerId, message, severity);
190     }
191 
192     /**
193      * Removes markers from a resource and only the resource (not its children).
194      * @param file The file from which to delete the markers.
195      * @param markerId The id of the markers to remove. If null, all marker of
196      * type <code>IMarker.PROBLEM</code> will be removed.
197      */
removeMarkersFromResource(IResource resource, String markerId)198     public final void removeMarkersFromResource(IResource resource, String markerId) {
199         try {
200             if (resource.exists()) {
201                 resource.deleteMarkers(markerId, true, IResource.DEPTH_ZERO);
202             }
203         } catch (CoreException ce) {
204             String msg = String.format(Messages.Marker_Delete_Error, markerId, resource.toString());
205             AdtPlugin.printErrorToConsole(getProject(), msg);
206         }
207     }
208 
209     /**
210      * Removes markers from a container and its children.
211      * @param folder The container from which to delete the markers.
212      * @param markerId The id of the markers to remove. If null, all marker of
213      * type <code>IMarker.PROBLEM</code> will be removed.
214      */
removeMarkersFromContainer(IContainer folder, String markerId)215     protected final void removeMarkersFromContainer(IContainer folder, String markerId) {
216         try {
217             if (folder.exists()) {
218                 folder.deleteMarkers(markerId, true, IResource.DEPTH_INFINITE);
219             }
220         } catch (CoreException ce) {
221             String msg = String.format(Messages.Marker_Delete_Error, markerId, folder.toString());
222             AdtPlugin.printErrorToConsole(getProject(), msg);
223         }
224     }
225 
226     /**
227      * Get the stderr output of a process and return when the process is done.
228      * @param process The process to get the ouput from
229      * @param results The array to store the stderr output
230      * @return the process return code.
231      * @throws InterruptedException
232      */
grabProcessOutput(final Process process, final ArrayList<String> results)233     protected final int grabProcessOutput(final Process process,
234             final ArrayList<String> results) throws InterruptedException {
235         return BuildHelper.grabProcessOutput(getProject(), process, results);
236     }
237 
238 
239 
240     /**
241      * Saves a String property into the persistent storage of the project.
242      * @param propertyName the name of the property. The id of the plugin is added to this string.
243      * @param value the value to save
244      * @return true if the save succeeded.
245      */
saveProjectStringProperty(String propertyName, String value)246     protected boolean saveProjectStringProperty(String propertyName, String value) {
247         IProject project = getProject();
248         return ProjectHelper.saveStringProperty(project, propertyName, value);
249     }
250 
251 
252     /**
253      * Loads a String property from the persistent storage of the project.
254      * @param propertyName the name of the property. The id of the plugin is added to this string.
255      * @return the property value or null if it was not found.
256      */
loadProjectStringProperty(String propertyName)257     protected String loadProjectStringProperty(String propertyName) {
258         IProject project = getProject();
259         return ProjectHelper.loadStringProperty(project, propertyName);
260     }
261 
262     /**
263      * Saves a property into the persistent storage of the project.
264      * @param propertyName the name of the property. The id of the plugin is added to this string.
265      * @param value the value to save
266      * @return true if the save succeeded.
267      */
saveProjectBooleanProperty(String propertyName, boolean value)268     protected boolean saveProjectBooleanProperty(String propertyName, boolean value) {
269         IProject project = getProject();
270         return ProjectHelper.saveStringProperty(project, propertyName, Boolean.toString(value));
271     }
272 
273     /**
274      * Loads a boolean property from the persistent storage of the project.
275      * @param propertyName the name of the property. The id of the plugin is added to this string.
276      * @param defaultValue The default value to return if the property was not found.
277      * @return the property value or the default value if the property was not found.
278      */
loadProjectBooleanProperty(String propertyName, boolean defaultValue)279     protected boolean loadProjectBooleanProperty(String propertyName, boolean defaultValue) {
280         IProject project = getProject();
281         return ProjectHelper.loadBooleanProperty(project, propertyName, defaultValue);
282     }
283 
284     /**
285      * Aborts the build if the SDK/project setups are broken. This does not
286      * display any errors.
287      *
288      * @param javaProject The {@link IJavaProject} being compiled.
289      * @throws CoreException
290      */
abortOnBadSetup(IJavaProject javaProject)291     protected void abortOnBadSetup(IJavaProject javaProject) throws AbortBuildException {
292         IProject iProject = javaProject.getProject();
293         // check if we have finished loading the project target.
294         Sdk sdk = Sdk.getCurrent();
295         if (sdk == null) {
296             throw new AbortBuildException();
297         }
298 
299         // get the target for the project
300         IAndroidTarget target = sdk.getTarget(javaProject.getProject());
301 
302         if (target == null) {
303             throw new AbortBuildException();
304         }
305 
306         // check on the target data.
307         if (sdk.checkAndLoadTargetData(target, javaProject) != LoadStatus.LOADED) {
308             throw new AbortBuildException();
309        }
310 
311         // abort if there are TARGET or ADT type markers
312         stopOnMarker(iProject, AdtConstants.MARKER_TARGET, IResource.DEPTH_ZERO,
313                 false /*checkSeverity*/);
314         stopOnMarker(iProject, AdtConstants.MARKER_ADT, IResource.DEPTH_ZERO,
315                 false /*checkSeverity*/);
316     }
317 
stopOnMarker(IProject project, String markerType, int depth, boolean checkSeverity)318     protected void stopOnMarker(IProject project, String markerType, int depth,
319             boolean checkSeverity)
320             throws AbortBuildException {
321         try {
322             IMarker[] markers = project.findMarkers(markerType, false /*includeSubtypes*/, depth);
323 
324             if (markers.length > 0) {
325                 if (checkSeverity == false) {
326                     throw new AbortBuildException();
327                 } else {
328                     for (IMarker marker : markers) {
329                         int severity = marker.getAttribute(IMarker.SEVERITY, -1 /*defaultValue*/);
330                         if (severity == IMarker.SEVERITY_ERROR) {
331                             throw new AbortBuildException();
332                         }
333                     }
334                 }
335             }
336         } catch (CoreException e) {
337             // don't stop, something's really screwed up and the build will break later with
338             // a better error message.
339         }
340     }
341 
342     /**
343      * Recursively delete all the derived resources from a root resource. The root resource is not
344      * deleted.
345      * @param rootResource the root resource
346      * @param monitor a progress monitor.
347      * @throws CoreException
348      *
349      */
removeDerivedResources(IResource rootResource, IProgressMonitor monitor)350     protected void removeDerivedResources(IResource rootResource, IProgressMonitor monitor)
351             throws CoreException {
352         removeDerivedResources(rootResource, false, monitor);
353     }
354 
355     /**
356      * delete a resource and its children. returns true if the root resource was deleted. All
357      * sub-folders *will* be deleted if they were emptied (not if they started empty).
358      * @param rootResource the root resource
359      * @param deleteRoot whether to delete the root folder.
360      * @param monitor a progress monitor.
361      * @throws CoreException
362      */
removeDerivedResources(IResource rootResource, boolean deleteRoot, IProgressMonitor monitor)363     private void removeDerivedResources(IResource rootResource, boolean deleteRoot,
364             IProgressMonitor monitor) throws CoreException {
365         if (rootResource.exists()) {
366             // if it's a folder, delete derived member.
367             if (rootResource.getType() == IResource.FOLDER) {
368                 IFolder folder = (IFolder)rootResource;
369                 IResource[] members = folder.members();
370                 boolean wasNotEmpty = members.length > 0;
371                 for (IResource member : members) {
372                     removeDerivedResources(member, true /*deleteRoot*/, monitor);
373                 }
374 
375                 // if the folder had content that is now all removed, delete the folder.
376                 if (deleteRoot && wasNotEmpty && folder.members().length == 0) {
377                     rootResource.getLocation().toFile().delete();
378                 }
379             }
380 
381             // if the root resource is derived, delete it.
382             if (rootResource.isDerived()) {
383                 rootResource.getLocation().toFile().delete();
384             }
385         }
386     }
387 }
388