diff options
Diffstat (limited to 'src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java')
-rw-r--r-- | src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java | 1377 |
1 files changed, 1377 insertions, 0 deletions
diff --git a/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java b/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java new file mode 100644 index 0000000..8551e17 --- /dev/null +++ b/src/plugins/android/src/com/motorola/studio/android/model/ProjectCreationSupport.java @@ -0,0 +1,1377 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.model; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.IWorkspaceRoot; +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.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +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.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.actions.WorkspaceModifyOperation; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.motorola.studio.android.AndroidPlugin; +import com.motorola.studio.android.adt.ProjectUtils; +import com.motorola.studio.android.adt.SdkUtils; +import com.motorola.studio.android.common.IAndroidConstants; +import com.motorola.studio.android.common.exception.AndroidException; +import com.motorola.studio.android.common.log.StudioLogger; +import com.motorola.studio.android.common.utilities.AndroidStatus; +import com.motorola.studio.android.common.utilities.EclipseUtils; +import com.motorola.studio.android.common.utilities.FileUtil; +import com.motorola.studio.android.i18n.AndroidNLS; +import com.motorola.studio.android.model.AndroidProject.SourceTypes; + +/** + * Project Creation Support. + */ +public class ProjectCreationSupport +{ + /** + * Only static calls + */ + private ProjectCreationSupport() + { + } + + private static final String PACKAGE_NAME = "PACKAGE"; //$NON-NLS-1$ + + private static final String APP_NAME = "app_name"; //$NON-NLS-1$ + + private static final String APPLICATION_NAME = "APPLICATION_NAME"; //$NON-NLS-1$ + + private static final String STRING_RSRC_PREFIX = "@string/"; //$NON-NLS-1$ + + private static final String MIN_SDK_VERSION = "MIN_SDK_VERSION"; //$NON-NLS-1$ + + private static final String BIN_DIR = IAndroidConstants.FD_OUTPUT + IPath.SEPARATOR; + + private static final String RES_DIR = IAndroidConstants.FD_RESOURCES + IPath.SEPARATOR; + + private static final String ASSETS_DIR = IAndroidConstants.FD_ASSETS + IPath.SEPARATOR; + + private static final String DRAWABLE_DIR = IAndroidConstants.FD_DRAWABLE; + + private static final String LAYOUT_DIR = IAndroidConstants.FD_LAYOUT + IPath.SEPARATOR; + + private static final String VALUES_DIR = IAndroidConstants.FD_VALUES + IPath.SEPARATOR; + + private static final String GEN_DIR = IAndroidConstants.FD_GEN_SOURCES + IPath.SEPARATOR; + + private static final String XML_DIR = "xml" + IPath.SEPARATOR; + + private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$ + + private static final String MANIFEST_TEMPLATE = TEMPLATES_DIRECTORY + + "AndroidManifest.template"; //$NON-NLS-1$ + + private static final String ACTIVITY_NAME = "ACTIVITY_NAME"; //$NON-NLS-1$ + + private static final String ACTIVITY_TEMPLATE = TEMPLATES_DIRECTORY + "activity.template"; //$NON-NLS-1$ + + private static final String LAUNCHER_INTENT_TEMPLATE = TEMPLATES_DIRECTORY + + "launcher_intent_filter.template"; //$NON-NLS-1$ + + private static final String INTENT_FILTERS = "INTENT_FILTERS"; //$NON-NLS-1$ + + private static final String ACTIVITIES = "ACTIVITIES"; //$NON-NLS-1$ + + private static final String USES_SDK_TEMPLATE = TEMPLATES_DIRECTORY + "uses-sdk.template"; //$NON-NLS-1$ + + private static final String USES_SDK = "USES-SDK"; //$NON-NLS-1$ + + private static final String ICON = "ic_launcher.png"; //$NON-NLS-1$ + + private static final String JAVA_ACTIVITY_TEMPLATE = "java_file.template"; //$NON-NLS-1$ + + private static final String MAIN_LAYOUT_XML = "main.xml"; //$NON-NLS-1$ + + private static final String LAYOUT_TEMPLATE = "layout.template"; //$NON-NLS-1$ + + private static final String STRING_HELLO_WORLD = "hello"; //$NON-NLS-1$ + + private static final String TEST_USES_LIBRARY = "TEST-USES-LIBRARY"; //$NON-NLS-1$ + + private static final String TEST_INSTRUMENTATION = "TEST-INSTRUMENTATION"; //$NON-NLS-1$ + + private static final String[] DPIS = + { + "hdpi", "ldpi", "mdpi" + }; + + /* + * Widget Project manifest creation constants + */ + + private static final String WIDGET_TEMPLATE_FOLDER = "templates/widget_project/"; + + private static final String WIDGET_MANIFEST_TEMPLATE_PATH = + "templates/widget_project/AndroidWidgetManifest.template"; //$NON-NLS-1$ + + private static final String WIDGET_ACTIVITY_TEMPLATE_PATH = + "templates/widget_project/activity.template"; //$NON-NLS-1$ + + private static final String WIDGET_RECEIVER_TEMPLATE_PATH = + "templates/widget_project/receiver.template"; //$NON-NLS-1$ + + private static final String WIDGET_USES_SDK_TEMPLATE_PATH = + "templates/widget_project/uses-sdk.template"; //$NON-NLS-1$ + + private static final String RECEIVERS = "RECEIVERS"; //$NON-NLS-1$ + + private static final String WIDGET_INITIAL_LAYOUT_XML = "widget_initial_layout.xml"; //$NON-NLS-1$ + + private static final String WIDGET_INFO_XML = "widget_info.xml"; //$NON-NLS-1$ + + private static final String WIDGET_PROVIDER_SAMPLE_NAME = "WidgetProvider"; //$NON-NLS-1$ + + private static final String WIDGET_PROVIDER_SAMPLE_TEMPLATE = "WidgetProvider.template"; //$NON-NLS-1$ + + private static final String IMPORT_RESOURCE_CLASS = "IMPORT_RESOURCE_CLASS"; + + /** + * Create a new Android Project + * @param androidProject + * @param container + * @return + * @throws AndroidException + */ + public static boolean createProject(final AndroidProject androidProject, + IWizardContainer container) throws AndroidException + { + boolean created = true; + + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + final IProject project = workspace.getRoot().getProject(androidProject.getName()); + + if (!canCreateProject(workspace.getRoot(), androidProject.getName())) + { + throw new AndroidException( + AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyWorkspace); + } + else + { + + final IProjectDescription description = + workspace.newProjectDescription(project.getName()); + + final Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put(MIN_SDK_VERSION, androidProject.getMinSdkVersion()); + + if ((androidProject.getSourceType() == SourceTypes.NEW) + || (androidProject.getSourceType() == SourceTypes.WIDGET)) + { + /* + * 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 = androidProject.getActivityName(); + if (activityName.startsWith(".")) { //$NON-NLS-1$ + activityName = activityName.substring(1); + } + parameters.put(ACTIVITY_NAME, androidProject.getActivityName()); + parameters.put(PACKAGE_NAME, androidProject.getPackageName()); + parameters.put(APPLICATION_NAME, STRING_RSRC_PREFIX + APP_NAME); + parameters.put(IMPORT_RESOURCE_CLASS, ""); + } + + /* + * 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(APP_NAME, androidProject.getApplicationName()); + + if (!androidProject.isUsingDefaultLocation() && androidProject.isNewProject()) + { + Path destination = new Path(androidProject.getLocation()); + description.setLocation(destination); + + if (!FileUtil.canWrite(destination.toFile())) + { + String errMsg = + NLS.bind( + AndroidNLS.EXC_ProjectCreationSupport_CannotCreateProjectReadOnlyDestination, + destination.toOSString()); + throw new AndroidException(errMsg); + } + + if (!validateNewProjectLocationIsEmpty(destination)) + { + throw new AndroidException(AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolder); + } + } + + if (androidProject.getSourceType() == SourceTypes.EXISTING) + { + Path destination = new Path(androidProject.getLocation()); + description.setLocation(destination); + } + + /* + * Create a monitored operation to create the actual project + */ + WorkspaceModifyOperation op = new WorkspaceModifyOperation() + { + @Override + protected void execute(IProgressMonitor monitor) throws InvocationTargetException + { + + createProjectAsync(project, androidProject, description, monitor, parameters, + stringDictionary); + } + }; + + /* + * Run the operation in a different thread + */ + created = runAsyncOperation(op, container); + } + + return created; + + } + + /** + * Create android project. + * @param project + * @param androidProject + * @param description + * @param monitor + * @param parameters + * @param stringDictionary + * @throws InvocationTargetException + */ + protected static void createProjectAsync(IProject project, AndroidProject androidProject, + IProjectDescription description, IProgressMonitor monitor, + Map<String, Object> parameters, Map<String, String> stringDictionary) + throws InvocationTargetException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorTaskTitle, 1000); + try + { + // Create project and open it + project.create(description, new SubProgressMonitor(monitor, 100)); + if (monitor.isCanceled()) + { + undoProjectCreation(project); + throw new OperationCanceledException(); + } + project.open(IResource.BACKGROUND_REFRESH, new SubProgressMonitor(monitor, 100)); + + ProjectUtils.setupAndroidNatures(project, monitor); + + // Create folders in the project if they don't already exist + createDefaultDir(project, IAndroidConstants.WS_ROOT, BIN_DIR, new SubProgressMonitor( + monitor, 40)); + createDefaultDir(project, IAndroidConstants.WS_ROOT, RES_DIR, new SubProgressMonitor( + monitor, 40)); + createDefaultDir(project, IAndroidConstants.WS_ROOT, ASSETS_DIR, + new SubProgressMonitor(monitor, 40)); + createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR, new SubProgressMonitor( + monitor, 40)); + + switch (androidProject.getSourceType()) + { + case NEW: + // Create the source folders in the project if they don't already exist + List<String> sourceFolders = androidProject.getSourceFolders(); + for (String sourceFolder : sourceFolders) + { + createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder, + new SubProgressMonitor(monitor, 40)); + } + + // Create the resource folders in the project if they don't already exist. + int apiLevel = androidProject.getSdkTarget().getVersion().getApiLevel(); + if (apiLevel < 4) + { + createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator, + new SubProgressMonitor(monitor, 40)); + } + else + { + for (String dpi : DPIS) + { + createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi + + File.separator, new SubProgressMonitor(monitor, 40)); + } + } + createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor, + 40)); + createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor, + 40)); + + // Create files in the project if they don't already exist + createManifest(project, parameters, stringDictionary, new SubProgressMonitor( + monitor, 80)); + // add the default app icon + addIcon(project, apiLevel, new SubProgressMonitor(monitor, 100)); + // Create the default package components + String primarySrcFolder = IAndroidConstants.FD_SOURCES; + if (!sourceFolders.contains(IAndroidConstants.FD_SOURCES)) + { + primarySrcFolder = sourceFolders.get(0); + } + addInitialCode(project, primarySrcFolder, parameters, stringDictionary, + new SubProgressMonitor(monitor, 200)); + // add the string definition file if needed + if (stringDictionary.size() > 0) + { + EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null, + new SubProgressMonitor(monitor, 100)); + } + + break; + case EXISTING: + createDefaultDir(project, IAndroidConstants.WS_ROOT, GEN_DIR, + new SubProgressMonitor(monitor, 650)); + break; + case SAMPLE: + monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_CopyingSamplesMonitorMessage); + FileUtil.copyDir(androidProject.getSample().getFolder(), project.getLocation() + .toFile()); + project.refreshLocal(IResource.DEPTH_INFINITE, new SubProgressMonitor(monitor, + 650)); + break; + case WIDGET: + // Create the source folders in the project if they don't already exist + List<String> widgetSourceFolders = androidProject.getSourceFolders(); + for (String sourceFolder : widgetSourceFolders) + { + createDefaultDir(project, IAndroidConstants.WS_ROOT, sourceFolder, + new SubProgressMonitor(monitor, 40)); + } + + // Create the resource folders in the project if they don't already exist. + int widgetApiLevel = androidProject.getSdkTarget().getVersion().getApiLevel(); + if (widgetApiLevel < 4) + { + createDefaultDir(project, RES_DIR, DRAWABLE_DIR + File.separator, + new SubProgressMonitor(monitor, 40)); + } + else + { + for (String dpi : DPIS) + { + createDefaultDir(project, RES_DIR, DRAWABLE_DIR + "-" + dpi + + File.separator, new SubProgressMonitor(monitor, 40)); + } + } + createDefaultDir(project, RES_DIR, LAYOUT_DIR, new SubProgressMonitor(monitor, + 40)); + createDefaultDir(project, RES_DIR, VALUES_DIR, new SubProgressMonitor(monitor, + 40)); + createDefaultDir(project, RES_DIR, XML_DIR, new SubProgressMonitor(monitor, 40)); + + // Create files in the project if they don't already exist + createWidgetManifest(project, parameters, stringDictionary, + new SubProgressMonitor(monitor, 80)); + // add the default app icon + addIcon(project, widgetApiLevel, new SubProgressMonitor(monitor, 100)); + // Create the default package components + String widgetPrimarySrcFolder = IAndroidConstants.FD_SOURCES; + if (!widgetSourceFolders.contains(IAndroidConstants.FD_SOURCES)) + { + primarySrcFolder = widgetSourceFolders.get(0); + } + addInitialWidgetCode(project, widgetPrimarySrcFolder, parameters, + stringDictionary, new SubProgressMonitor(monitor, 200)); + // add the string definition file if needed + if (stringDictionary.size() > 0) + { + EclipseUtils.createOrUpdateDictionaryFile(project, stringDictionary, null, + new SubProgressMonitor(monitor, 100)); + } + + break; + } + + // Setup class path + IJavaProject javaProject = JavaCore.create(project); + setupSourceFolders(javaProject, androidProject.getSourceFolders(), + new SubProgressMonitor(monitor, 40)); + + // Set output location + javaProject.setOutputLocation(project.getFolder(BIN_DIR).getFullPath(), + new SubProgressMonitor(monitor, 40)); + SdkUtils.associate(project, androidProject.getSdkTarget()); + ProjectUtils.fixProject(project); + } + catch (CoreException e) + { + undoProjectCreation(project); + throw new InvocationTargetException(e); + } + catch (IOException e) + { + undoProjectCreation(project); + throw new InvocationTargetException(e); + } + finally + { + monitor.done(); + } + } + + /** + * Add initial code + * @param project + * @param sourceFolder + * @param parameters + * @param stringDictionary + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void addInitialCode(IProject project, String sourceFolder, + Map<String, Object> parameters, Map<String, String> stringDictionary, + IProgressMonitor monitor) throws CoreException, IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 700); + + try + { + IFolder pkgFolder = project.getFolder(sourceFolder); + + Map<String, Object> processed_parameters = processSampleActivity(parameters); + String activityName = (String) processed_parameters.get(ACTIVITY_NAME); + String packageName = (String) processed_parameters.get(PACKAGE_NAME); + + pkgFolder = + createPackageFolders(new SubProgressMonitor(monitor, 300), pkgFolder, + packageName); + + if (activityName != null) + { + createSampleActivity(new SubProgressMonitor(monitor, 200), pkgFolder, + processed_parameters, activityName); + } + + IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR); + IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML); + if (!file.exists()) + { + copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor, + 100)); + if (activityName != null) + { + stringDictionary + .put(STRING_HELLO_WORLD, NLS.bind( + AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName, + activityName)); + } + else + { + stringDictionary.put(STRING_HELLO_WORLD, + AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple); + } + monitor.worked(100); + } + } + finally + { + monitor.done(); + } + } + + /** + * Add initial widget code + * @param project + * @param sourceFolder + * @param parameters + * @param stringDictionary + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void addInitialWidgetCode(IProject project, String sourceFolder, + Map<String, Object> parameters, Map<String, String> stringDictionary, + IProgressMonitor monitor) throws CoreException, IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Source_Task, 800); + + try + { + IFolder pkgFolder = project.getFolder(sourceFolder); + + Map<String, Object> processed_parameters = processSampleActivity(parameters); + String activityName = (String) processed_parameters.get(ACTIVITY_NAME); + String packageName = (String) processed_parameters.get(PACKAGE_NAME); + + pkgFolder = + createPackageFolders(new SubProgressMonitor(monitor, 200), pkgFolder, + packageName); + + // Create sample activity + if (activityName != null) + { + createSampleActivity(new SubProgressMonitor(monitor, 100), pkgFolder, + processed_parameters, activityName); + } + + // Create sample widget provider + createSampleWidgetProvider(new SubProgressMonitor(monitor, 100), pkgFolder, + processed_parameters); + + // Layout xml file + IFolder layoutfolder = project.getFolder(RES_DIR + LAYOUT_DIR); + + IFile file = layoutfolder.getFile(MAIN_LAYOUT_XML); + if (!file.exists()) + { + copyTemplateFile(LAYOUT_TEMPLATE, file, parameters, new SubProgressMonitor(monitor, + 100)); + if (activityName != null) + { + stringDictionary + .put(STRING_HELLO_WORLD, NLS.bind( + AndroidNLS.GEN_ProjectCreationSupport_HelloWorldWithName, + activityName)); + } + else + { + stringDictionary.put(STRING_HELLO_WORLD, + AndroidNLS.GEN_ProjectCreationSupport_HelloWorldSimple); + } + monitor.worked(100); + } + + // Widget initial layout xml file + IFile initial_layout_file = layoutfolder.getFile(WIDGET_INITIAL_LAYOUT_XML); + if (!initial_layout_file.exists()) + { + copyWidgetTemplateFile(WIDGET_INITIAL_LAYOUT_XML, initial_layout_file, + processed_parameters, new SubProgressMonitor(monitor, 100)); + monitor.worked(100); + } + + // Widget info xml file + IFolder xmlFolder = project.getFolder(RES_DIR + XML_DIR); + + IFile widget_info_file = xmlFolder.getFile(WIDGET_INFO_XML); + if (!widget_info_file.exists()) + { + copyWidgetTemplateFile(WIDGET_INFO_XML, widget_info_file, processed_parameters, + new SubProgressMonitor(monitor, 100)); + monitor.worked(100); + } + + } + finally + { + monitor.done(); + } + } + + private static void createSampleActivity(IProgressMonitor monitor, IFolder pkgFolder, + Map<String, Object> processed_parameters, String activityName) throws CoreException, + IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Activity_Task, + 100); + try + { + IFile file = pkgFolder.getFile(activityName + IAndroidConstants.DOT_JAVA); + if (!file.exists()) + { + monitor.worked(10); + copyTemplateFile(JAVA_ACTIVITY_TEMPLATE, file, processed_parameters, + new SubProgressMonitor(monitor, 90)); + } + } + finally + { + monitor.done(); + } + } + + private static void createSampleWidgetProvider(IProgressMonitor monitor, IFolder pkgFolder, + Map<String, Object> processed_parameters) throws CoreException, IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Sample_Widget_Provider, + 100); + try + { + IFile file = + pkgFolder.getFile(WIDGET_PROVIDER_SAMPLE_NAME + IAndroidConstants.DOT_JAVA); + if (!file.exists()) + { + monitor.worked(10); + copyWidgetTemplateFile(WIDGET_PROVIDER_SAMPLE_TEMPLATE, file, processed_parameters, + new SubProgressMonitor(monitor, 90)); + } + } + finally + { + monitor.done(); + } + } + + private static IFolder createPackageFolders(IProgressMonitor monitor, IFolder pkgFolder, + String packageName) throws CoreException + { + String[] components = packageName.split(IAndroidConstants.RE_DOT); + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Java_Packages_Task, + components.length * 100); + try + { + for (String component : components) + { + pkgFolder = pkgFolder.getFolder(component); + if (!pkgFolder.exists()) + { + pkgFolder.create(true, true, new SubProgressMonitor(monitor, 100)); + } + } + } + finally + { + monitor.done(); + } + + return pkgFolder; + } + + private static Map<String, Object> processSampleActivity(Map<String, Object> parameters) + { + String activityName = (String) parameters.get(ACTIVITY_NAME); + + Map<String, Object> processed_parameters = new HashMap<String, Object>(parameters); + if ((activityName != null) && activityName.contains(".")) //$NON-NLS-1$ + { + String packageName = (String) parameters.get(PACKAGE_NAME); + packageName += "." + activityName.substring(0, activityName.lastIndexOf('.')); //$NON-NLS-1$ + activityName = activityName.substring(activityName.lastIndexOf('.')); + + processed_parameters.put(PACKAGE_NAME, packageName); + processed_parameters.put(ACTIVITY_NAME, activityName); + } + + return processed_parameters; + } + + /** + * Copy template files + * @param resourceFilename + * @param destFile + * @param parameters + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void copyTemplateFile(String resourceFilename, IFile destFile, + Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, + IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150); + InputStream stream = null; + try + { + String template = + readEmbeddedTextFileADT(TEMPLATES_DIRECTORY + resourceFilename, parameters); + monitor.worked(50); + stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$ + destFile.create(stream, false, new SubProgressMonitor(monitor, 100)); + + } + finally + { + if (stream != null) + { + stream.close(); + } + monitor.done(); + } + } + + /** + * Copy widget template files + * @param resourceFilename + * @param destFile + * @param parameters + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void copyWidgetTemplateFile(String resourceFilename, IFile destFile, + Map<String, Object> parameters, IProgressMonitor monitor) throws CoreException, + IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Template_File_Task, 150); + InputStream stream = null; + try + { + String template = + readEmbeddedTextFileStudio(WIDGET_TEMPLATE_FOLDER + resourceFilename, + parameters); + monitor.worked(50); + stream = new ByteArrayInputStream(template.getBytes("UTF-8")); //$NON-NLS-1$ + destFile.create(stream, false, new SubProgressMonitor(monitor, 100)); + + } + finally + { + if (stream != null) + { + stream.close(); + } + monitor.done(); + } + } + + /** + * Add Icon to the project + * @param project + * @param apiLevel + * @param monitor + * @throws CoreException + */ + private static void addIcon(IProject project, int apiLevel, IProgressMonitor monitor) + throws CoreException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Configuring_Project_Icon_Task, 1000); + try + { + if (apiLevel < 4) + { + IFile imageFile = + project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + IPath.SEPARATOR + + ICON); + if (!imageFile.exists()) + { + String fileName = + ICON.substring(0, ICON.length() - 4) + "_" + DPIS[2] + + ICON.substring(ICON.length() - 4); + createImageFromTemplate(monitor, imageFile, fileName); + } + } + else + { + for (String dpi : DPIS) + { + IFile imageFile = + project.getFile(RES_DIR + IPath.SEPARATOR + DRAWABLE_DIR + "-" + dpi + + IPath.SEPARATOR + ICON); + if (!imageFile.exists()) + { + String fileName = + ICON.substring(0, ICON.length() - 4) + "_" + dpi + + ICON.substring(ICON.length() - 4); + createImageFromTemplate(monitor, imageFile, fileName); + } + } + } + } + finally + { + monitor.done(); + } + + } + + private static void createImageFromTemplate(IProgressMonitor monitor, IFile imageFile, + String fileName) throws CoreException + { + byte[] buffer = AdtPlugin.readEmbeddedFile(TEMPLATES_DIRECTORY + fileName); + + if (buffer != null) + { + InputStream stream = null; + try + { + stream = new ByteArrayInputStream(buffer); + imageFile.create(stream, IResource.NONE, new SubProgressMonitor(monitor, 1000)); + } + finally + { + try + { + stream.close(); + } + catch (IOException e) + { + StudioLogger.info("Create image from template could not close stream. " + + e.getMessage()); + } + } + } + } + + /** + * Adds the manifest to the project. + * @param project + * @param parameters + * @param stringDictionary + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void createManifest(IProject project, Map<String, Object> parameters, + Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, + IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300); + try + { + IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST); + if (!manifestFile.exists()) + { + monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task); + String manifestTemplate = readEmbeddedTextFileADT(MANIFEST_TEMPLATE, parameters); + monitor.worked(10); + if (parameters.containsKey(ACTIVITY_NAME)) + { + String activities = readEmbeddedTextFileADT(ACTIVITY_TEMPLATE, parameters); + String intent = AdtPlugin.readEmbeddedTextFile(LAUNCHER_INTENT_TEMPLATE); + activities = activities.replaceAll(INTENT_FILTERS, intent); + manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities); + monitor.worked(90); + } + else + { + manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$ + monitor.worked(90); + } + + //We don't currently supports the TEST parameters. So let's just remove the unused tags. + manifestTemplate = manifestTemplate.replaceAll(TEST_USES_LIBRARY, ""); //$NON-NLS-1$ + manifestTemplate = manifestTemplate.replaceAll(TEST_INSTRUMENTATION, ""); //$NON-NLS-1$ + + String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION); + if ((minSdkVersion != null) && (minSdkVersion.length() > 0)) + { + String usesSdk = readEmbeddedTextFileADT(USES_SDK_TEMPLATE, parameters); + manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk); + monitor.worked(50); + } + else + { + manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$ + monitor.worked(50); + } + + InputStream stream = null; + try + { + stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$ + manifestFile.create(stream, IResource.NONE, + new SubProgressMonitor(monitor, 150)); + } + finally + { + try + { + if (stream != null) + { + stream.close(); + } + } + catch (IOException e) + { + StudioLogger.info("Could not close stream while creating manifest", + e.getMessage()); + } + } + } + } + finally + { + monitor.done(); + } + } + + /** + * Adds the widget manifest to the project. + * @param project + * @param parameters + * @param stringDictionary + * @param monitor + * @throws CoreException + * @throws IOException + */ + private static void createWidgetManifest(IProject project, Map<String, Object> parameters, + Map<String, String> stringDictionary, IProgressMonitor monitor) throws CoreException, + IOException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Creating_Manifest_File_Task, 300); + try + { + IFile manifestFile = project.getFile(IAndroidConstants.FN_ANDROID_MANIFEST); + if (!manifestFile.exists()) + { + monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Reading_Template_File_Task); + + // Manifest skeleton + String manifestTemplate = + readEmbeddedTextFileStudio(WIDGET_MANIFEST_TEMPLATE_PATH, parameters); + monitor.worked(10); + // Activity information + if (parameters.containsKey(ACTIVITY_NAME)) + { + String activities = + readEmbeddedTextFileStudio(WIDGET_ACTIVITY_TEMPLATE_PATH, parameters); + manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, activities); + monitor.worked(70); + } + else + { + manifestTemplate = manifestTemplate.replaceAll(ACTIVITIES, ""); //$NON-NLS-1$ + monitor.worked(70); + } + + // Receiver information + String receivers = + readEmbeddedTextFileStudio(WIDGET_RECEIVER_TEMPLATE_PATH, parameters); + manifestTemplate = manifestTemplate.replaceAll(RECEIVERS, receivers); + monitor.worked(70); + + // Min Sdk information + String minSdkVersion = (String) parameters.get(MIN_SDK_VERSION); + if ((minSdkVersion != null) && (minSdkVersion.length() > 0)) + { + String usesSdk = + readEmbeddedTextFileStudio(WIDGET_USES_SDK_TEMPLATE_PATH, parameters); + manifestTemplate = manifestTemplate.replaceAll(USES_SDK, usesSdk); + monitor.worked(50); + } + else + { + manifestTemplate = manifestTemplate.replaceAll(USES_SDK, ""); //$NON-NLS-1$ + monitor.worked(50); + } + + InputStream stream = null; + + try + { + stream = new ByteArrayInputStream(manifestTemplate.getBytes("UTF-8")); //$NON-NLS-1$ + manifestFile.create(stream, IResource.NONE, + new SubProgressMonitor(monitor, 100)); + } + finally + { + try + { + if (stream != null) + { + stream.close(); + } + } + catch (IOException e) + { + StudioLogger.info( + "Could not close stream while creating manifest for widget", + e.getMessage()); + } + } + } + } + finally + { + monitor.done(); + } + } + + private static String readEmbeddedTextFileADT(String template, Map<String, Object> parameters) + { + String loadedTemplate = AdtPlugin.readEmbeddedTextFile(template); + + for (String key : parameters.keySet()) + { + if (parameters.get(key) instanceof String) + { + loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key)); + } + } + + return loadedTemplate; + } + + private static String readEmbeddedTextFileStudio(String template, Map<String, Object> parameters) + { + String loadedTemplate = + EclipseUtils.readEmbeddedResource(AndroidPlugin.getDefault().getBundle(), template); + + for (String key : parameters.keySet()) + { + if (parameters.get(key) instanceof String) + { + loadedTemplate = loadedTemplate.replaceAll(key, (String) parameters.get(key)); + } + } + + return loadedTemplate; + } + + /** + * Setup src folders + * @param javaProject + * @param sourceFolder + * @param monitor + * @throws JavaModelException + */ + private static void setupSourceFolders(IJavaProject javaProject, List<String> sourceFolders, + IProgressMonitor monitor) throws JavaModelException + { + monitor.beginTask(AndroidNLS.UI_ProjectCreationSupport_Preparing_Source_Folders_Task, + (sourceFolders.size() * 100) + 100); + try + { + IProject project = javaProject.getProject(); + IClasspathEntry[] entries = javaProject.getRawClasspath(); + + for (String sourceFolder : sourceFolders) + { + IFolder srcFolder = project.getFolder(sourceFolder); + entries = removeClasspathEntry(entries, srcFolder); + entries = removeClasspathEntry(entries, srcFolder.getParent()); + entries = + ProjectUtils.addEntryToClasspath(entries, + JavaCore.newSourceEntry(srcFolder.getFullPath())); + monitor.worked(100); + } + + javaProject.setRawClasspath(entries, new SubProgressMonitor(monitor, 100)); + } + finally + { + monitor.done(); + } + } + + /** + * Remove source folder from classpath + * @param entries + * @param folder + * @return + */ + private static IClasspathEntry[] removeClasspathEntry(IClasspathEntry[] entries, + IContainer folder) + { + + IClasspathEntry[] newClassPath = null; + + if (folder != null) + { + IClasspathEntry removeEntry = JavaCore.newSourceEntry(folder.getFullPath()); + List<IClasspathEntry> entriesList = Arrays.asList(entries); + + if (entriesList.contains(removeEntry)) + { + newClassPath = new IClasspathEntry[entries.length - 1]; + int i = 0; + for (IClasspathEntry entry : entriesList) + { + if (!entry.equals(removeEntry)) + { + newClassPath[i] = entry; + i++; + } + } + } + else + { + newClassPath = entries; + } + } + else + { + newClassPath = entries; + } + return newClassPath; + } + + /** + * Add default directory to Project + * @param project + * @param parentFolder + * @param folderName + * @param monitor + * @throws CoreException + */ + private static void createDefaultDir(IProject project, String parentFolder, String folderName, + IProgressMonitor monitor) throws CoreException + { + monitor.beginTask( + AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task + folderName, 100); + + try + { + monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Verifying_Directory_Task); + if (folderName.length() > 0) + { + monitor.worked(10); + IFolder folder = project.getFolder(parentFolder + folderName); + monitor.worked(10); + if (!folder.exists()) + { + monitor.worked(10); + if (FileUtil.canWrite(folder.getLocation().toFile())) + { + monitor.worked(10); + monitor.setTaskName(AndroidNLS.UI_ProjectCreationSupport_Creating_Directory_Task); + folder.create(true, true, new SubProgressMonitor(monitor, 60)); + } + else + { + String errMsg = + NLS.bind( + AndroidNLS.EXC_ProjectCreationSupport_CannotCreateFolderReadOnlyWorkspace, + folder.getLocation().toFile().toString()); + IStatus status = new AndroidStatus(IStatus.ERROR, errMsg); + throw new CoreException(status); + } + } + } + } + finally + { + monitor.done(); + } + } + + /** + * Validate new Project Location. + * @param destination + * @param display + * @return + */ + public static boolean validateNewProjectLocationIsEmpty(IPath destination) + { + File f = new File(destination.toOSString()); + if (f.isDirectory() && (f.list().length > 0)) + { + // EclipseUtils.showErrorDialog( + // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestionDialogTitle, + // AndroidNLS.UI_ProjectCreationSupport_NonEmptyFolderQuestion); + return false; + } + return true; + } + + /** + * Run the operation in async thread. + * @param op + * @param container + * + * @return true if no errors occur during the operation or false otherwise + */ + private static boolean runAsyncOperation(WorkspaceModifyOperation op, IWizardContainer container) + { + boolean created = false; + + try + { + container.run(true, true, op); + created = true; + } + catch (InvocationTargetException ite) + { + Throwable t = ite.getTargetException(); + if (t instanceof CoreException) + { + CoreException core = (CoreException) t; + if (core.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) + { + MessageDialog.openError(container.getShell(), + AndroidNLS.UI_GenericErrorDialogTitle, + AndroidNLS.ERR_ProjectCreationSupport_CaseVariantExistsError); + } + else + { + ErrorDialog.openError(container.getShell(), + AndroidNLS.UI_GenericErrorDialogTitle, null, core.getStatus()); + } + } + else + { + MessageDialog.openError(container.getShell(), + AndroidNLS.UI_GenericErrorDialogTitle, t.getMessage()); + } + } + catch (InterruptedException e) + { + StudioLogger.error(ProjectCreationSupport.class, "Error creating project.", e); //$NON-NLS-1$ + } + + return created; + } + + /** + * Checks if a project can be created on workspace + * + * @param root The workspace root + * @param projectName The project name + * + * @return true if the project can be created or false otherwise + */ + private static boolean canCreateProject(IWorkspaceRoot root, String projectName) + { + File rootFolder = root.getLocation().toFile(); + File projectFolder = new File(rootFolder, projectName); + + return FileUtil.canWrite(projectFolder); + } + + /** + * Undoes a project creation. Removes all files created by the project creation process + * and keeps any other previous files + * + * @param project The failed project + * @param existingResources A set containing the path of pre-existing resources before creating + * the project + */ + private static void undoProjectCreation(IProject project) + { + File projectPath = + new File(project.getWorkspace().getRoot().getLocation().toFile(), project.getName()); + Set<String> existingResources = getExistingResources(projectPath); + + try + { + project.delete(false, true, new NullProgressMonitor()); + } + catch (CoreException e1) + { + // Do nothing + StudioLogger.error(ProjectCreationSupport.class, e1.getLocalizedMessage(), e1); + } + + if (existingResources.isEmpty()) + { + try + { + FileUtil.deleteDirRecursively(project.getLocation().toFile()); + } + catch (IOException e) + { + // Do nothing + StudioLogger.error(ProjectCreationSupport.class, e.getLocalizedMessage(), e); + } + } + else + { + File root = + new File(project.getWorkspace().getRoot().getLocation().toFile(), + project.getName()); + removeCreatedResources(root, existingResources); + } + } + + /** + * Retrieves a list of existing sub-resources from a folder + * + * @param folder the File object representing the folder + * + * @return a list of existing sub-resources from the folder + */ + private static Set<String> getExistingResources(File folder) + { + Set<String> existing = new HashSet<String>(); + + if ((folder != null) && folder.exists() && folder.isDirectory()) + { + existing.add(folder.toString()); + + File[] children = folder.listFiles(); + + if (children != null) + { + for (File child : children) + { + if (child.isDirectory()) + { + existing.addAll(getExistingResources(child)); + } + else + { + existing.add(child.toString()); + } + } + } + } + + return existing; + } + + /** + * Removes the created resources by a failed project creation process + * + * @param startingPoint The project root folder (File object) + * @param existingResources The set containing the previous existing resources in the project root folder + */ + private static void removeCreatedResources(File startingPoint, Set<String> existingResources) + { + File[] members = startingPoint.listFiles(); + + if (members != null) + { + for (File child : members) + { + if (child.isFile()) + { + if (!existingResources.contains(child.toString())) + { + child.delete(); + } + } + else + { + removeCreatedResources(child, existingResources); + } + } + } + + if (!existingResources.contains(startingPoint.toString())) + { + startingPoint.delete(); + } + } +} |