Home | History | Annotate | Download | only in project
      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
     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
     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
     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
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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