blob: 3b1c29fe97c73feb2d5f9bbef09e2cbf702137d2 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.project;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.PreCompilerBuilder;
import com.android.ide.eclipse.adt.internal.build.builders.ResourceManagerBuilder;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.JavaCore;
/**
* Project nature for the Android Projects.
*/
public class AndroidNature implements IProjectNature {
/** the project this nature object is associated with */
private IProject mProject;
/**
* Configures this nature for its project. This is called by the workspace
* when natures are added to the project using
* <code>IProject.setDescription</code> and should not be called directly
* by clients. The nature extension id is added to the list of natures
* before this method is called, and need not be added here.
*
* Exceptions thrown by this method will be propagated back to the caller of
* <code>IProject.setDescription</code>, but the nature will remain in
* the project description.
*
* The Android nature adds the pre-builder and the APK builder if necessary.
*
* @see org.eclipse.core.resources.IProjectNature#configure()
* @throws CoreException if configuration fails.
*/
@Override
public void configure() throws CoreException {
configureResourceManagerBuilder(mProject);
configurePreBuilder(mProject);
configureApkBuilder(mProject);
}
/**
* De-configures this nature for its project. This is called by the
* workspace when natures are removed from the project using
* <code>IProject.setDescription</code> and should not be called directly
* by clients. The nature extension id is removed from the list of natures
* before this method is called, and need not be removed here.
*
* Exceptions thrown by this method will be propagated back to the caller of
* <code>IProject.setDescription</code>, but the nature will still be
* removed from the project description.
*
* The Android nature removes the custom pre builder and APK builder.
*
* @see org.eclipse.core.resources.IProjectNature#deconfigure()
* @throws CoreException if configuration fails.
*/
@Override
public void deconfigure() throws CoreException {
// remove the android builders
removeBuilder(mProject, ResourceManagerBuilder.ID);
removeBuilder(mProject, PreCompilerBuilder.ID);
removeBuilder(mProject, PostCompilerBuilder.ID);
}
/**
* Returns the project to which this project nature applies.
*
* @return the project handle
* @see org.eclipse.core.resources.IProjectNature#getProject()
*/
@Override
public IProject getProject() {
return mProject;
}
/**
* Sets the project to which this nature applies. Used when instantiating
* this project nature runtime. This is called by
* <code>IProject.create()</code> or
* <code>IProject.setDescription()</code> and should not be called
* directly by clients.
*
* @param project the project to which this nature applies
* @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject)
*/
@Override
public void setProject(IProject project) {
mProject = project;
}
/**
* Adds the Android Nature and the Java Nature to the project if it doesn't
* already have them.
*
* @param project An existing or new project to update
* @param monitor An optional progress monitor. Can be null.
* @param addAndroidNature true if the Android Nature should be added to the project; false to
* add only the Java nature.
* @throws CoreException if fails to change the nature.
*/
public static synchronized void setupProjectNatures(IProject project,
IProgressMonitor monitor, boolean addAndroidNature) throws CoreException {
if (project == null || !project.isOpen()) return;
if (monitor == null) monitor = new NullProgressMonitor();
// Add the natures. We need to add the Java nature first, so it adds its builder to the
// project first. This way, when the android nature is added, we can control where to put
// the android builders in relation to the java builder.
// Adding the java nature after the android one, would place the java builder before the
// android builders.
addNatureToProjectDescription(project, JavaCore.NATURE_ID, monitor);
if (addAndroidNature) {
addNatureToProjectDescription(project, AdtConstants.NATURE_DEFAULT, monitor);
}
}
/**
* Add the specified nature to the specified project. The nature is only
* added if not already present.
* <p/>
* Android Natures are always inserted at the beginning of the list of natures in order to
* have the jdt views/dialogs display the proper icon.
*
* @param project The project to modify.
* @param natureId The Id of the nature to add.
* @param monitor An existing progress monitor.
* @throws CoreException if fails to change the nature.
*/
private static void addNatureToProjectDescription(IProject project,
String natureId, IProgressMonitor monitor) throws CoreException {
if (!project.hasNature(natureId)) {
IProjectDescription description = project.getDescription();
String[] natures = description.getNatureIds();
String[] newNatures = new String[natures.length + 1];
// Android natures always come first.
if (natureId.equals(AdtConstants.NATURE_DEFAULT)) {
System.arraycopy(natures, 0, newNatures, 1, natures.length);
newNatures[0] = natureId;
} else {
System.arraycopy(natures, 0, newNatures, 0, natures.length);
newNatures[natures.length] = natureId;
}
description.setNatureIds(newNatures);
project.setDescription(description, new SubProgressMonitor(monitor, 10));
}
}
/**
* Adds the ResourceManagerBuilder, if its not already there. It'll insert
* itself as the first builder.
* @throws CoreException
*
*/
public static void configureResourceManagerBuilder(IProject project)
throws CoreException {
// get the builder list
IProjectDescription desc = project.getDescription();
ICommand[] commands = desc.getBuildSpec();
// look for the builder in case it's already there.
for (int i = 0; i < commands.length; ++i) {
if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
return;
}
}
// it's not there, lets add it at the beginning of the builders
ICommand[] newCommands = new ICommand[commands.length + 1];
System.arraycopy(commands, 0, newCommands, 1, commands.length);
ICommand command = desc.newCommand();
command.setBuilderName(ResourceManagerBuilder.ID);
newCommands[0] = command;
desc.setBuildSpec(newCommands);
project.setDescription(desc, null);
}
/**
* Adds the PreCompilerBuilder if its not already there. It'll check for
* presence of the ResourceManager and insert itself right after.
* @param project
* @throws CoreException
*/
public static void configurePreBuilder(IProject project)
throws CoreException {
// get the builder list
IProjectDescription desc = project.getDescription();
ICommand[] commands = desc.getBuildSpec();
// look for the builder in case it's already there.
for (int i = 0; i < commands.length; ++i) {
if (PreCompilerBuilder.ID.equals(commands[i].getBuilderName())) {
return;
}
}
// we need to add it after the resource manager builder.
// Let's look for it
int index = -1;
for (int i = 0; i < commands.length; ++i) {
if (ResourceManagerBuilder.ID.equals(commands[i].getBuilderName())) {
index = i;
break;
}
}
// we're inserting after
index++;
// do the insertion
// copy the builders before.
ICommand[] newCommands = new ICommand[commands.length + 1];
System.arraycopy(commands, 0, newCommands, 0, index);
// insert the new builder
ICommand command = desc.newCommand();
command.setBuilderName(PreCompilerBuilder.ID);
newCommands[index] = command;
// copy the builder after
System.arraycopy(commands, index, newCommands, index + 1, commands.length-index);
// set the new builders in the project
desc.setBuildSpec(newCommands);
project.setDescription(desc, null);
}
public static void configureApkBuilder(IProject project)
throws CoreException {
// Add the .apk builder at the end if it's not already there
IProjectDescription desc = project.getDescription();
ICommand[] commands = desc.getBuildSpec();
for (int i = 0; i < commands.length; ++i) {
if (PostCompilerBuilder.ID.equals(commands[i].getBuilderName())) {
return;
}
}
ICommand[] newCommands = new ICommand[commands.length + 1];
System.arraycopy(commands, 0, newCommands, 0, commands.length);
ICommand command = desc.newCommand();
command.setBuilderName(PostCompilerBuilder.ID);
newCommands[commands.length] = command;
desc.setBuildSpec(newCommands);
project.setDescription(desc, null);
}
/**
* Removes a builder from the project.
* @param project The project to remove the builder from.
* @param id The String ID of the builder to remove.
* @return true if the builder was found and removed.
* @throws CoreException
*/
public static boolean removeBuilder(IProject project, String id) throws CoreException {
IProjectDescription description = project.getDescription();
ICommand[] commands = description.getBuildSpec();
for (int i = 0; i < commands.length; ++i) {
if (id.equals(commands[i].getBuilderName())) {
ICommand[] newCommands = new ICommand[commands.length - 1];
System.arraycopy(commands, 0, newCommands, 0, i);
System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
description.setBuildSpec(newCommands);
project.setDescription(description, null);
return true;
}
}
return false;
}
}