• 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.project;
18 
19 import com.android.ide.eclipse.adt.AdtConstants;
20 import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
21 import com.android.ide.eclipse.adt.internal.build.builders.PreCompilerBuilder;
22 import com.android.ide.eclipse.adt.internal.build.builders.ResourceManagerBuilder;
23 
24 import org.eclipse.core.resources.ICommand;
25 import org.eclipse.core.resources.IProject;
26 import org.eclipse.core.resources.IProjectDescription;
27 import org.eclipse.core.resources.IProjectNature;
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.NullProgressMonitor;
31 import org.eclipse.core.runtime.SubProgressMonitor;
32 import org.eclipse.jdt.core.JavaCore;
33 
34 /**
35  * Project nature for the Android Projects.
36  */
37 public class AndroidNature implements IProjectNature {
38 
39     /** the project this nature object is associated with */
40     private IProject mProject;
41 
42     /**
43      * Configures this nature for its project. This is called by the workspace
44      * when natures are added to the project using
45      * <code>IProject.setDescription</code> and should not be called directly
46      * by clients. The nature extension id is added to the list of natures
47      * before this method is called, and need not be added here.
48      *
49      * Exceptions thrown by this method will be propagated back to the caller of
50      * <code>IProject.setDescription</code>, but the nature will remain in
51      * the project description.
52      *
53      * The Android nature adds the pre-builder and the APK builder if necessary.
54      *
55      * @see org.eclipse.core.resources.IProjectNature#configure()
56      * @throws CoreException if configuration fails.
57      */
configure()58     public void configure() throws CoreException {
59         configureResourceManagerBuilder(mProject);
60         configurePreBuilder(mProject);
61         configureApkBuilder(mProject);
62     }
63 
64     /**
65      * De-configures this nature for its project. This is called by the
66      * workspace when natures are removed from the project using
67      * <code>IProject.setDescription</code> and should not be called directly
68      * by clients. The nature extension id is removed from the list of natures
69      * before this method is called, and need not be removed here.
70      *
71      * Exceptions thrown by this method will be propagated back to the caller of
72      * <code>IProject.setDescription</code>, but the nature will still be
73      * removed from the project description.
74      *
75      * The Android nature removes the custom pre builder and APK builder.
76      *
77      * @see org.eclipse.core.resources.IProjectNature#deconfigure()
78      * @throws CoreException if configuration fails.
79      */
deconfigure()80     public void deconfigure() throws CoreException {
81         // remove the android builders
82         removeBuilder(mProject, ResourceManagerBuilder.ID);
83         removeBuilder(mProject, PreCompilerBuilder.ID);
84         removeBuilder(mProject, PostCompilerBuilder.ID);
85     }
86 
87     /**
88      * Returns the project to which this project nature applies.
89      *
90      * @return the project handle
91      * @see org.eclipse.core.resources.IProjectNature#getProject()
92      */
getProject()93     public IProject getProject() {
94         return mProject;
95     }
96 
97     /**
98      * Sets the project to which this nature applies. Used when instantiating
99      * this project nature runtime. This is called by
100      * <code>IProject.create()</code> or
101      * <code>IProject.setDescription()</code> and should not be called
102      * directly by clients.
103      *
104      * @param project the project to which this nature applies
105      * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject)
106      */
setProject(IProject project)107     public void setProject(IProject project) {
108         mProject = project;
109     }
110 
111     /**
112      * Adds the Android Nature and the Java Nature to the project if it doesn't
113      * already have them.
114      *
115      * @param project An existing or new project to update
116      * @param monitor An optional progress monitor. Can be null.
117      * @throws CoreException if fails to change the nature.
118      */
setupProjectNatures(IProject project, IProgressMonitor monitor)119     public static synchronized void setupProjectNatures(IProject project,
120             IProgressMonitor monitor) throws CoreException {
121         if (project == null || !project.isOpen()) return;
122         if (monitor == null) monitor = new NullProgressMonitor();
123 
124         // Add the natures. We need to add the Java nature first, so it adds its builder to the
125         // project first. This way, when the android nature is added, we can control where to put
126         // the android builders in relation to the java builder.
127         // Adding the java nature after the android one, would place the java builder before the
128         // android builders.
129         addNatureToProjectDescription(project, JavaCore.NATURE_ID, monitor);
130         addNatureToProjectDescription(project, AdtConstants.NATURE_DEFAULT, monitor);
131     }
132 
133     /**
134      * Add the specified nature to the specified project. The nature is only
135      * added if not already present.
136      * <p/>
137      * Android Natures are always inserted at the beginning of the list of natures in order to
138      * have the jdt views/dialogs display the proper icon.
139      *
140      * @param project The project to modify.
141      * @param natureId The Id of the nature to add.
142      * @param monitor An existing progress monitor.
143      * @throws CoreException if fails to change the nature.
144      */
addNatureToProjectDescription(IProject project, String natureId, IProgressMonitor monitor)145     private static void addNatureToProjectDescription(IProject project,
146             String natureId, IProgressMonitor monitor) throws CoreException {
147         if (!project.hasNature(natureId)) {
148 
149             IProjectDescription description = project.getDescription();
150             String[] natures = description.getNatureIds();
151             String[] newNatures = new String[natures.length + 1];
152 
153             // Android natures always come first.
154             if (natureId.equals(AdtConstants.NATURE_DEFAULT)) {
155                 System.arraycopy(natures, 0, newNatures, 1, natures.length);
156                 newNatures[0] = natureId;
157             } else {
158                 System.arraycopy(natures, 0, newNatures, 0, natures.length);
159                 newNatures[natures.length] = natureId;
160             }
161 
162             description.setNatureIds(newNatures);
163             project.setDescription(description, new SubProgressMonitor(monitor, 10));
164         }
165     }
166 
167     /**
168      * Adds the ResourceManagerBuilder, if its not already there. It'll insert
169      * itself as the first builder.
170      * @throws CoreException
171      *
172      */
configureResourceManagerBuilder(IProject project)173     public static void configureResourceManagerBuilder(IProject project)
174             throws CoreException {
175         // get the builder list
176         IProjectDescription desc = project.getDescription();
177         ICommand[] commands = desc.getBuildSpec();
178 
179         // look for the builder in case it's already there.
180         for (int i = 0; i < commands.length; ++i) {
181             if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
182                 return;
183             }
184         }
185 
186         // it's not there, lets add it at the beginning of the builders
187         ICommand[] newCommands = new ICommand[commands.length + 1];
188         System.arraycopy(commands, 0, newCommands, 1, commands.length);
189         ICommand command = desc.newCommand();
190         command.setBuilderName(ResourceManagerBuilder.ID);
191         newCommands[0] = command;
192         desc.setBuildSpec(newCommands);
193         project.setDescription(desc, null);
194     }
195 
196     /**
197      * Adds the PreCompilerBuilder if its not already there. It'll check for
198      * presence of the ResourceManager and insert itself right after.
199      * @param project
200      * @throws CoreException
201      */
configurePreBuilder(IProject project)202     public static void configurePreBuilder(IProject project)
203             throws CoreException {
204         // get the builder list
205         IProjectDescription desc = project.getDescription();
206         ICommand[] commands = desc.getBuildSpec();
207 
208         // look for the builder in case it's already there.
209         for (int i = 0; i < commands.length; ++i) {
210             if (PreCompilerBuilder.ID.equals(commands[i].getBuilderName())) {
211                 return;
212             }
213         }
214 
215         // we need to add it after the resource manager builder.
216         // Let's look for it
217         int index = -1;
218         for (int i = 0; i < commands.length; ++i) {
219             if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
220                 index = i;
221                 break;
222             }
223         }
224 
225         // we're inserting after
226         index++;
227 
228         // do the insertion
229 
230         // copy the builders before.
231         ICommand[] newCommands = new ICommand[commands.length + 1];
232         System.arraycopy(commands, 0, newCommands, 0, index);
233 
234         // insert the new builder
235         ICommand command = desc.newCommand();
236         command.setBuilderName(PreCompilerBuilder.ID);
237         newCommands[index] = command;
238 
239         // copy the builder after
240         System.arraycopy(commands, index, newCommands, index + 1, commands.length-index);
241 
242         // set the new builders in the project
243         desc.setBuildSpec(newCommands);
244         project.setDescription(desc, null);
245     }
246 
configureApkBuilder(IProject project)247     public static void configureApkBuilder(IProject project)
248             throws CoreException {
249         // Add the .apk builder at the end if it's not already there
250         IProjectDescription desc = project.getDescription();
251         ICommand[] commands = desc.getBuildSpec();
252 
253         for (int i = 0; i < commands.length; ++i) {
254             if (PostCompilerBuilder.ID.equals(commands[i].getBuilderName())) {
255                 return;
256             }
257         }
258 
259         ICommand[] newCommands = new ICommand[commands.length + 1];
260         System.arraycopy(commands, 0, newCommands, 0, commands.length);
261         ICommand command = desc.newCommand();
262         command.setBuilderName(PostCompilerBuilder.ID);
263         newCommands[commands.length] = command;
264         desc.setBuildSpec(newCommands);
265         project.setDescription(desc, null);
266     }
267 
268     /**
269      * Removes a builder from the project.
270      * @param project The project to remove the builder from.
271      * @param id The String ID of the builder to remove.
272      * @return true if the builder was found and removed.
273      * @throws CoreException
274      */
removeBuilder(IProject project, String id)275     public static boolean removeBuilder(IProject project, String id) throws CoreException {
276         IProjectDescription description = project.getDescription();
277         ICommand[] commands = description.getBuildSpec();
278         for (int i = 0; i < commands.length; ++i) {
279             if (id.equals(commands[i].getBuilderName())) {
280                 ICommand[] newCommands = new ICommand[commands.length - 1];
281                 System.arraycopy(commands, 0, newCommands, 0, i);
282                 System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
283                 description.setBuildSpec(newCommands);
284                 project.setDescription(description, null);
285                 return true;
286             }
287         }
288 
289         return false;
290     }
291 }
292