/* * 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.wizards.newproject; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.AndroidNature; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceStatus; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.ui.actions.OpenJavaPerspectiveAction; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.Wizard; import org.eclipse.ui.INewWizard; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.actions.WorkspaceModifyOperation; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Map.Entry; /** * A "New Android Project" Wizard. * <p/> * Note: this class is public so that it can be accessed from unit tests. * It is however an internal class. Its API may change without notice. * It should semantically be considered as a private final class. * Do not derive from this class. */ public class NewProjectWizard extends Wizard implements INewWizard { private static final String PARAM_SDK_TOOLS_DIR = "ANDROID_SDK_TOOLS"; //$NON-NLS-1$ private static final String PARAM_ACTIVITY = "ACTIVITY_NAME"; //$NON-NLS-1$ private static final String PARAM_APPLICATION = "APPLICATION_NAME"; //$NON-NLS-1$ private static final String PARAM_PACKAGE = "PACKAGE"; //$NON-NLS-1$ private static final String PARAM_PROJECT = "PROJECT_NAME"; //$NON-NLS-1$ private static final String PARAM_STRING_NAME = "STRING_NAME"; //$NON-NLS-1$ private static final String PARAM_STRING_CONTENT = "STRING_CONTENT"; //$NON-NLS-1$ private static final String PARAM_IS_NEW_PROJECT = "IS_NEW_PROJECT"; //$NON-NLS-1$ private static final String PARAM_SRC_FOLDER = "SRC_FOLDER"; //$NON-NLS-1$ private static final String PARAM_SDK_TARGET = "SDK_TARGET"; //$NON-NLS-1$ private static final String PARAM_MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$ private static final String PH_ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$ private static final String PH_USES_SDK = "USES-SDK"; //$NON-NLS-1$ private static final String PH_INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$ private static final String PH_STRINGS = "STRINGS"; //$NON-NLS-1$ private static final String BIN_DIRECTORY = SdkConstants.FD_OUTPUT + AndroidConstants.WS_SEP; private static final String RES_DIRECTORY = SdkConstants.FD_RESOURCES + AndroidConstants.WS_SEP; private static final String ASSETS_DIRECTORY = SdkConstants.FD_ASSETS + AndroidConstants.WS_SEP; private static final String DRAWABLE_DIRECTORY = SdkConstants.FD_DRAWABLE + AndroidConstants.WS_SEP; private static final String LAYOUT_DIRECTORY = SdkConstants.FD_LAYOUT + AndroidConstants.WS_SEP; private static final String VALUES_DIRECTORY = SdkConstants.FD_VALUES + AndroidConstants.WS_SEP; private static final String GEN_SRC_DIRECTORY = SdkConstants.FD_GEN_SOURCES + AndroidConstants.WS_SEP; private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$ private static final String TEMPLATE_MANIFEST = TEMPLATES_DIRECTORY + "AndroidManifest.template"; //$NON-NLS-1$ private static final String TEMPLATE_ACTIVITIES = TEMPLATES_DIRECTORY + "activity.template"; //$NON-NLS-1$ private static final String TEMPLATE_USES_SDK = TEMPLATES_DIRECTORY + "uses-sdk.template"; //$NON-NLS-1$ private static final String TEMPLATE_INTENT_LAUNCHER = TEMPLATES_DIRECTORY + "launcher_intent_filter.template"; //$NON-NLS-1$ private static final String TEMPLATE_STRINGS = TEMPLATES_DIRECTORY + "strings.template"; //$NON-NLS-1$ private static final String TEMPLATE_STRING = TEMPLATES_DIRECTORY + "string.template"; //$NON-NLS-1$ private static final String ICON = "icon.png"; //$NON-NLS-1$ private static final String STRINGS_FILE = "strings.xml"; //$NON-NLS-1$ private static final String STRING_RSRC_PREFIX = "@string/"; //$NON-NLS-1$ private static final String STRING_APP_NAME = "app_name"; //$NON-NLS-1$ private static final String STRING_HELLO_WORLD = "hello"; //$NON-NLS-1$ private static final String[] DEFAULT_DIRECTORIES = new String[] { BIN_DIRECTORY, RES_DIRECTORY, ASSETS_DIRECTORY }; private static final String[] RES_DIRECTORIES = new String[] { DRAWABLE_DIRECTORY, LAYOUT_DIRECTORY, VALUES_DIRECTORY}; private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$ private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$ private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$ private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$ protected static final String MAIN_PAGE_NAME = "newAndroidProjectPage"; //$NON-NLS-1$ private NewProjectCreationPage mMainPage; /** * Initializes this creation wizard using the passed workbench and object * selection. Inherited from org.eclipse.ui.IWorkbenchWizard */ public void init(IWorkbench workbench, IStructuredSelection selection) { setHelpAvailable(false); // TODO have help setWindowTitle("New Android Project"); setImageDescriptor(); mMainPage = createMainPage(); mMainPage.setTitle("New Android Project"); mMainPage.setDescription("Creates a new Android Project resource."); } /** * Creates the wizard page. * <p/> * Please do NOT override this method. * <p/> * This is protected so that it can be overridden by unit tests. * However the contract of this class is private and NO ATTEMPT will be made * to maintain compatibility between different versions of the plugin. */ protected NewProjectCreationPage createMainPage() { return new NewProjectCreationPage(MAIN_PAGE_NAME); } // -- Methods inherited from org.eclipse.jface.wizard.Wizard -- // The Wizard class implements most defaults and boilerplate code needed by // IWizard /** * Adds pages to this wizard. */ @Override public void addPages() { addPage(mMainPage); } /** * Performs any actions appropriate in response to the user having pressed * the Finish button, or refuse if finishing now is not permitted: here, it * actually creates the workspace project and then switch to the Java * perspective. * * @return True */ @Override public boolean performFinish() { if (!createAndroidProject()) { return false; } // Open the default Java Perspective OpenJavaPerspectiveAction action = new OpenJavaPerspectiveAction(); action.run(); return true; } // -- Custom Methods -- /** * Before actually creating the project for a new project (as opposed to using an * existing project), we check if the target location is a directory that either does * not exist or is empty. * * If it's not empty, ask the user for confirmation. * * @param destination The destination folder where the new project is to be created. * @return True if the destination doesn't exist yet or is an empty directory or is * accepted by the user. */ private boolean validateNewProjectLocationIsEmpty(IPath destination) { File f = new File(destination.toOSString()); if (f.isDirectory() && f.list().length > 0) { return AdtPlugin.displayPrompt("New Android Project", "You are going to create a new Android Project in an existing, non-empty, directory. Are you sure you want to proceed?"); } return true; } /** * Creates the android project. * @return True if the project could be created. */ private boolean createAndroidProject() { IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IProject project = workspace.getRoot().getProject(mMainPage.getProjectName()); final IProjectDescription description = workspace.newProjectDescription(project.getName()); final Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put(PARAM_PROJECT, mMainPage.getProjectName()); parameters.put(PARAM_PACKAGE, mMainPage.getPackageName()); parameters.put(PARAM_APPLICATION, STRING_RSRC_PREFIX + STRING_APP_NAME); parameters.put(PARAM_SDK_TOOLS_DIR, AdtPlugin.getOsSdkToolsFolder()); parameters.put(PARAM_IS_NEW_PROJECT, mMainPage.isNewProject()); parameters.put(PARAM_SRC_FOLDER, mMainPage.getSourceFolder()); parameters.put(PARAM_SDK_TARGET, mMainPage.getSdkTarget()); parameters.put(PARAM_MIN_SDK_VERSION, mMainPage.getMinSdkVersion()); if (mMainPage.isCreateActivity()) { // An activity name can be of the form ".package.Class" or ".Class". // The initial dot is ignored, as it is always added later in the templates. String activityName = mMainPage.getActivityName(); if (activityName.startsWith(".")) { //$NON-NLS-1$ activityName = activityName.substring(1); } parameters.put(PARAM_ACTIVITY, activityName); } // create a dictionary of string that will contain name+content. // we'll put all the strings into values/strings.xml final HashMap<String, String> stringDictionary = new HashMap<String, String>(); stringDictionary.put(STRING_APP_NAME, mMainPage.getApplicationName()); IPath path = mMainPage.getLocationPath(); IPath defaultLocation = Platform.getLocation(); if (!path.equals(defaultLocation)) { description.setLocation(path); } if (mMainPage.isNewProject() && !mMainPage.useDefaultLocation() && !validateNewProjectLocationIsEmpty(path)) { return false; } // Create a monitored operation to create the actual project WorkspaceModifyOperation op = new WorkspaceModifyOperation() { @Override protected void execute(IProgressMonitor monitor) throws InvocationTargetException { createProjectAsync(project, description, monitor, parameters, stringDictionary); } }; // Run the operation in a different thread runAsyncOperation(op); return true; } /** * Runs the operation in a different thread and display generated * exceptions. * * @param op The asynchronous operation to run. */ private void runAsyncOperation(WorkspaceModifyOperation op) { try { getContainer().run(true /* fork */, true /* cancelable */, op); } catch (InvocationTargetException e) { // The runnable threw an exception Throwable t = e.getTargetException(); if (t instanceof CoreException) { CoreException core = (CoreException) t; if (core.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) { // The error indicates the file system is not case sensitive // and there's a resource with a similar name. MessageDialog.openError(getShell(), "Error", "Error: Case Variant Exists"); } else { ErrorDialog.openError(getShell(), "Error", null, core.getStatus()); } } else { // Some other kind of exception MessageDialog.openError(getShell(), "Error", t.getMessage()); } e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * Creates the actual project, sets its nature and adds the required folders * and files to it. This is run asynchronously in a different thread. * * @param project The project to create. * @param description A description of the project. * @param monitor An existing monitor. * @param parameters Template parameters. * @param stringDictionary String definition. * @throws InvocationTargetException to wrap any unmanaged exception and * return it to the calling thread. The method can fail if it fails * to create or modify the project or if it is canceled by the user. */ private void createProjectAsync(IProject project, IProjectDescription description, IProgressMonitor monitor, Map<String, Object> parameters, Map<String, String> stringDictionary) throws InvocationTargetException { monitor.beginTask("Create Android Project", 100); try { // Create project and open it project.create(description, new SubProgressMonitor(monitor, 10)); if (monitor.isCanceled()) throw new OperationCanceledException(); project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 10)); // Add the Java and android nature to the project AndroidNature.setupProjectNatures(project, monitor); // Create folders in the project if they don't already exist addDefaultDirectories(project, AndroidConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor); String[] sourceFolders = new String[] { (String) parameters.get(PARAM_SRC_FOLDER), GEN_SRC_DIRECTORY }; addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolders, monitor); // Create the resource folders in the project if they don't already exist. addDefaultDirectories(project, RES_DIRECTORY, RES_DIRECTORIES, monitor); // Setup class path: mark folders as source folders IJavaProject javaProject = JavaCore.create(project); for (String sourceFolder : sourceFolders) { setupSourceFolder(javaProject, sourceFolder, monitor); } // Mark the gen source folder as derived IFolder genSrcFolder = project.getFolder(AndroidConstants.WS_ROOT + GEN_SRC_DIRECTORY); if (genSrcFolder.exists()) { genSrcFolder.setDerived(true); } if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) { // Create files in the project if they don't already exist addManifest(project, parameters, stringDictionary, monitor); // add the default app icon addIcon(project, monitor); // Create the default package components addSampleCode(project, sourceFolders[0], parameters, stringDictionary, monitor); // add the string definition file if needed if (stringDictionary.size() > 0) { addStringDictionaryFile(project, stringDictionary, monitor); } // Set output location javaProject.setOutputLocation(project.getFolder(BIN_DIRECTORY).getFullPath(), monitor); } Sdk.getCurrent().setProject(project, (IAndroidTarget) parameters.get(PARAM_SDK_TARGET), null /* apkConfigMap*/); // Fix the project to make sure all properties are as expected. // Necessary for existing projects and good for new ones to. ProjectHelper.fixProject(project); } catch (CoreException e) { throw new InvocationTargetException(e); } catch (IOException e) { throw new InvocationTargetException(e); } finally { monitor.done(); } } /** * Adds default directories to the project. * * @param project The Java Project to update. * @param parentFolder The path of the parent folder. Must end with a * separator. * @param folders Folders to be added. * @param monitor An existing monitor. * @throws CoreException if the method fails to create the directories in * the project. */ private void addDefaultDirectories(IProject project, String parentFolder, String[] folders, IProgressMonitor monitor) throws CoreException { for (String name : folders) { if (name.length() > 0) { IFolder folder = project.getFolder(parentFolder + name); if (!folder.exists()) { folder.create(true /* force */, true /* local */, new SubProgressMonitor(monitor, 10)); } } } } /** * Adds the manifest to the project. * * @param project The Java Project to update. * @param parameters Template Parameters. * @param stringDictionary String List to be added to a string definition * file. This map will be filled by this method. * @param monitor An existing monitor. * @throws CoreException if the method fails to update the project. * @throws IOException if the method fails to create the files in the * project. */ private void addManifest(IProject project, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { // get IFile to the manifest and check if it's not already there. IFile file = project.getFile(AndroidConstants.FN_ANDROID_MANIFEST); if (!file.exists()) { // Read manifest template String manifestTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_MANIFEST); // Replace all keyword parameters manifestTemplate = replaceParameters(manifestTemplate, parameters); if (parameters.containsKey(PARAM_ACTIVITY)) { // now get the activity template String activityTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_ACTIVITIES); // Replace all keyword parameters to make main activity. String activities = replaceParameters(activityTemplate, parameters); // set the intent. String intent = AdtPlugin.readEmbeddedTextFile(TEMPLATE_INTENT_LAUNCHER); // set the intent to the main activity activities = activities.replaceAll(PH_INTENT_FILTERS, intent); // set the activity(ies) in the manifest manifestTemplate = manifestTemplate.replaceAll(PH_ACTIVITIES, activities); } else { // remove the activity(ies) from the manifest manifestTemplate = manifestTemplate.replaceAll(PH_ACTIVITIES, ""); } String minSdkVersion = (String) parameters.get(PARAM_MIN_SDK_VERSION); if (minSdkVersion != null && minSdkVersion.length() > 0) { String usesSdkTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_USES_SDK); String usesSdk = replaceParameters(usesSdkTemplate, parameters); manifestTemplate = manifestTemplate.replaceAll(PH_USES_SDK, usesSdk); } else { manifestTemplate = manifestTemplate.replaceAll(PH_USES_SDK, ""); } // Save in the project as UTF-8 InputStream stream = new ByteArrayInputStream( manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$ file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10)); } } /** * Adds the string resource file. * * @param project The Java Project to update. * @param strings The list of strings to be added to the string file. * @param monitor An existing monitor. * @throws CoreException if the method fails to update the project. * @throws IOException if the method fails to create the files in the * project. */ private void addStringDictionaryFile(IProject project, Map<String, String> strings, IProgressMonitor monitor) throws CoreException, IOException { // create the IFile object and check if the file doesn't already exist. IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP + VALUES_DIRECTORY + AndroidConstants.WS_SEP + STRINGS_FILE); if (!file.exists()) { // get the Strings.xml template String stringDefinitionTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_STRINGS); // get the template for one string String stringTemplate = AdtPlugin.readEmbeddedTextFile(TEMPLATE_STRING); // get all the string names Set<String> stringNames = strings.keySet(); // loop on it and create the string definitions StringBuilder stringNodes = new StringBuilder(); for (String key : stringNames) { // get the value from the key String value = strings.get(key); // place them in the template String stringDef = stringTemplate.replace(PARAM_STRING_NAME, key); stringDef = stringDef.replace(PARAM_STRING_CONTENT, value); // append to the other string if (stringNodes.length() > 0) { stringNodes.append("\n"); } stringNodes.append(stringDef); } // put the string nodes in the Strings.xml template stringDefinitionTemplate = stringDefinitionTemplate.replace(PH_STRINGS, stringNodes.toString()); // write the file as UTF-8 InputStream stream = new ByteArrayInputStream( stringDefinitionTemplate.getBytes("UTF-8")); //$NON-NLS-1$ file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10)); } } /** * Adds default application icon to the project. * * @param project The Java Project to update. * @param monitor An existing monitor. * @throws CoreException if the method fails to update the project. */ private void addIcon(IProject project, IProgressMonitor monitor) throws CoreException { IFile file = project.getFile(RES_DIRECTORY + AndroidConstants.WS_SEP + DRAWABLE_DIRECTORY + AndroidConstants.WS_SEP + ICON); if (!file.exists()) { // read the content from the template byte[] buffer = AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + ICON); // if valid if (buffer != null) { // Save in the project InputStream stream = new ByteArrayInputStream(buffer); file.create(stream, false /* force */, new SubProgressMonitor(monitor, 10)); } } } /** * Creates the package folder and copies the sample code in the project. * * @param project The Java Project to update. * @param parameters Template Parameters. * @param stringDictionary String List to be added to a string definition * file. This map will be filled by this method. * @param monitor An existing monitor. * @throws CoreException if the method fails to update the project. * @throws IOException if the method fails to create the files in the * project. */ private void addSampleCode(IProject project, String sourceFolder, Map<String, Object> parameters, Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, IOException { // create the java package directories. IFolder pkgFolder = project.getFolder(sourceFolder); String packageName = (String) parameters.get(PARAM_PACKAGE); // The PARAM_ACTIVITY key will be absent if no activity should be created, // in which case activityName will be null. String activityName = (String) parameters.get(PARAM_ACTIVITY); Map<String, Object> java_activity_parameters = parameters; if (activityName != null) { if (activityName.indexOf('.') >= 0) { // There are package names in the activity name. Transform packageName to add // those sub packages and remove them from activityName. packageName += "." + activityName; //$NON-NLS-1$ int pos = packageName.lastIndexOf('.'); activityName = packageName.substring(pos + 1); packageName = packageName.substring(0, pos); // Also update the values used in the JAVA_FILE_TEMPLATE below // (but not the ones from the manifest so don't change the caller's dictionary) java_activity_parameters = new HashMap<String, Object>(parameters); java_activity_parameters.put(PARAM_PACKAGE, packageName); java_activity_parameters.put(PARAM_ACTIVITY, activityName); } } String[] components = packageName.split(AndroidConstants.RE_DOT); for (String component : components) { pkgFolder = pkgFolder.getFolder(component); if (!pkgFolder.exists()) { pkgFolder.create(true /* force */, true /* local */, new SubProgressMonitor(monitor, 10)); } } if (activityName != null) { // create the main activity Java file String activityJava = activityName + AndroidConstants.DOT_JAVA; IFile file = pkgFolder.getFile(activityJava); if (!file.exists()) { copyFile(JAVA_ACTIVITY_TEMPLATE, file, java_activity_parameters, monitor); } } // create the layout file IFolder layoutfolder = project.getFolder(RES_DIRECTORY).getFolder(LAYOUT_DIRECTORY); IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML); if (!file.exists()) { copyFile(LAYOUT_TEMPLATE, file, parameters, monitor); if (activityName != null) { stringDictionary.put(STRING_HELLO_WORLD, "Hello World, " + activityName + "!"); } else { stringDictionary.put(STRING_HELLO_WORLD, "Hello World!"); } } } /** * Adds the given folder to the project's class path. * * @param javaProject The Java Project to update. * @param sourceFolder Template Parameters. * @param monitor An existing monitor. * @throws JavaModelException if the classpath could not be set. */ private void setupSourceFolder(IJavaProject javaProject, String sourceFolder, IProgressMonitor monitor) throws JavaModelException { IProject project = javaProject.getProject(); // Add "src" to class path IFolder srcFolder = project.getFolder(sourceFolder); IClasspathEntry[] entries = javaProject.getRawClasspath(); entries = removeSourceClasspath(entries, srcFolder); entries = removeSourceClasspath(entries, srcFolder.getParent()); entries = ProjectHelper.addEntryToClasspath(entries, JavaCore.newSourceEntry(srcFolder.getFullPath())); javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 10)); } /** * Removes the corresponding source folder from the class path entries if * found. * * @param entries The class path entries to read. A copy will be returned. * @param folder The parent source folder to remove. * @return A new class path entries array. */ private IClasspathEntry[] removeSourceClasspath(IClasspathEntry[] entries, IContainer folder) { if (folder == null) { return entries; } IClasspathEntry source = JavaCore.newSourceEntry(folder.getFullPath()); int n = entries.length; for (int i = n - 1; i >= 0; i--) { if (entries[i].equals(source)) { IClasspathEntry[] newEntries = new IClasspathEntry[n - 1]; if (i > 0) System.arraycopy(entries, 0, newEntries, 0, i); if (i < n - 1) System.arraycopy(entries, i + 1, newEntries, i, n - i - 1); n--; entries = newEntries; } } return entries; } /** * Copies the given file from our resource folder to the new project. * Expects the file to the US-ASCII or UTF-8 encoded. * * @throws CoreException from IFile if failing to create the new file. * @throws MalformedURLException from URL if failing to interpret the URL. * @throws FileNotFoundException from RandomAccessFile. * @throws IOException from RandomAccessFile.length() if can't determine the * length. */ private void copyFile(String resourceFilename, IFile destFile, Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, IOException { // Read existing file. String template = AdtPlugin.readEmbeddedTextFile( TEMPLATES_DIRECTORY + resourceFilename); // Replace all keyword parameters template = replaceParameters(template, parameters); // Save in the project as UTF-8 InputStream stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$ destFile.create(stream, false /* force */, new SubProgressMonitor(monitor, 10)); } /** * Returns an image descriptor for the wizard logo. */ private void setImageDescriptor() { ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE); setDefaultPageImageDescriptor(desc); } /** * Replaces placeholders found in a string with values. * * @param str the string to search for placeholders. * @param parameters a map of <placeholder, Value> to search for in the string * @return A new String object with the placeholder replaced by the values. */ private String replaceParameters(String str, Map<String, Object> parameters) { for (Entry<String, Object> entry : parameters.entrySet()) { if (entry.getValue() instanceof String) { str = str.replaceAll(entry.getKey(), (String) entry.getValue()); } } return str; } }