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