aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java431
1 files changed, 431 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
new file mode 100644
index 000000000..16cd7b355
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2008 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.wizards.newxmlfile;
+
+import static com.android.SdkConstants.FQCN_GRID_LAYOUT;
+import static com.android.SdkConstants.GRID_LAYOUT;
+
+import com.android.SdkConstants;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.xml.XmlFormatStyle;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtUtils;
+import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlFormatPreferences;
+import com.android.ide.eclipse.adt.internal.editors.formatting.EclipseXmlPrettyPrinter;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewManager;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.project.SupportLibraryHelper;
+import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
+import com.android.resources.ResourceFolderType;
+import com.android.utils.Pair;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.PartInitException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * The "New Android XML File Wizard" provides the ability to create skeleton XML
+ * resources files for Android projects.
+ * <p/>
+ * The wizard has one page, {@link NewXmlFileCreationPage}, used to select the project,
+ * the resource folder, resource type and file name. It then creates the XML file.
+ */
+public class NewXmlFileWizard extends Wizard implements INewWizard {
+ /** The XML header to write at the top of the XML file */
+ public static final String XML_HEADER_LINE = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
+
+ private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$
+
+ protected static final String MAIN_PAGE_NAME = "newAndroidXmlFilePage"; //$NON-NLS-1$
+
+ private NewXmlFileCreationPage mMainPage;
+ private ChooseConfigurationPage mConfigPage;
+ private Values mValues;
+
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ setHelpAvailable(false); // TODO have help
+ setWindowTitle("New Android XML File");
+ setImageDescriptor();
+
+ mValues = new Values();
+ mMainPage = createMainPage(mValues);
+ mMainPage.setTitle("New Android XML File");
+ mMainPage.setDescription("Creates a new Android XML file.");
+ mMainPage.setInitialSelection(selection);
+
+ mConfigPage = new ChooseConfigurationPage(mValues);
+
+ // Trigger a check to see if the SDK needs to be reloaded (which will
+ // invoke onSdkLoaded asynchronously as needed).
+ AdtPlugin.getDefault().refreshSdk();
+ }
+
+ /**
+ * 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 NewXmlFileCreationPage createMainPage(NewXmlFileWizard.Values values) {
+ return new NewXmlFileCreationPage(MAIN_PAGE_NAME, values);
+ }
+
+ // -- 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);
+ addPage(mConfigPage);
+
+ }
+
+ /**
+ * 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() {
+ final Pair<IFile, IRegion> created = createXmlFile();
+ if (created == null) {
+ return false;
+ } else {
+ // Open the file
+ // This has to be delayed in order for focus handling to work correctly
+ AdtPlugin.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ IFile file = created.getFirst();
+ IRegion region = created.getSecond();
+ try {
+ IEditorPart editor = AdtPlugin.openFile(file, null,
+ false /*showEditorTab*/);
+ if (editor instanceof AndroidXmlEditor) {
+ final AndroidXmlEditor xmlEditor = (AndroidXmlEditor)editor;
+ if (!xmlEditor.hasMultiplePages()) {
+ xmlEditor.show(region.getOffset(), region.getLength(),
+ true /* showEditorTab */);
+ }
+ }
+ } catch (PartInitException e) {
+ AdtPlugin.log(e, "Failed to create %1$s: missing type", //$NON-NLS-1$
+ file.getFullPath().toString());
+ }
+ }});
+
+ return true;
+ }
+ }
+
+ // -- Custom Methods --
+
+ private Pair<IFile, IRegion> createXmlFile() {
+ IFile file = mValues.getDestinationFile();
+ TypeInfo type = mValues.type;
+ if (type == null) {
+ // this is not expected to happen
+ String name = file.getFullPath().toString();
+ AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing type", name); //$NON-NLS-1$
+ return null;
+ }
+ String xmlns = type.getXmlns();
+ String root = mMainPage.getRootElement();
+ if (root == null) {
+ // this is not expected to happen
+ AdtPlugin.log(IStatus.ERROR, "Failed to create %1$s: missing root element", //$NON-NLS-1$
+ file.toString());
+ return null;
+ }
+
+ String attrs = type.getDefaultAttrs(mValues.project, root);
+ String child = type.getChild(mValues.project, root);
+ return createXmlFile(file, xmlns, root, attrs, child, type.getResFolderType());
+ }
+
+ /** Creates a new file using the given root element, namespace and root attributes */
+ private static Pair<IFile, IRegion> createXmlFile(IFile file, String xmlns,
+ String root, String rootAttributes, String child, ResourceFolderType folderType) {
+ String name = file.getFullPath().toString();
+ boolean need_delete = false;
+
+ if (file.exists()) {
+ if (!AdtPlugin.displayPrompt("New Android XML File",
+ String.format("Do you want to overwrite the file %1$s ?", name))) {
+ // abort if user selects cancel.
+ return null;
+ }
+ need_delete = true;
+ } else {
+ AdtUtils.createWsParentDirectory(file.getParent());
+ }
+
+ StringBuilder sb = new StringBuilder(XML_HEADER_LINE);
+
+ if (folderType == ResourceFolderType.LAYOUT && root.equals(GRID_LAYOUT)) {
+ IProject project = file.getParent().getProject();
+ int minSdk = ManifestInfo.get(project).getMinSdkVersion();
+ if (minSdk < 14) {
+ root = SupportLibraryHelper.getTagFor(project, FQCN_GRID_LAYOUT);
+ if (root.equals(FQCN_GRID_LAYOUT)) {
+ root = GRID_LAYOUT;
+ }
+ }
+ }
+
+ sb.append('<').append(root);
+ if (xmlns != null) {
+ sb.append('\n').append(" xmlns:android=\"").append(xmlns).append('"'); //$NON-NLS-1$
+ }
+
+ if (rootAttributes != null) {
+ sb.append("\n "); //$NON-NLS-1$
+ sb.append(rootAttributes.replace("\n", "\n ")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ sb.append(">\n"); //$NON-NLS-1$
+
+ if (child != null) {
+ sb.append(child);
+ }
+
+ boolean autoFormat = AdtPrefs.getPrefs().getUseCustomXmlFormatter();
+
+ // Insert an indented caret. Since the markup here will be reformatted, we need to
+ // insert text tokens that the formatter will preserve, which we can then turn back
+ // into indentation and a caret offset:
+ final String indentToken = "${indent}"; //$NON-NLS-1$
+ final String caretToken = "${caret}"; //$NON-NLS-1$
+ sb.append(indentToken);
+ sb.append(caretToken);
+ if (!autoFormat) {
+ sb.append('\n');
+ }
+
+ sb.append("</").append(root).append(">\n"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ EclipseXmlFormatPreferences formatPrefs = EclipseXmlFormatPreferences.create();
+ String fileContents;
+ if (!autoFormat) {
+ fileContents = sb.toString();
+ } else {
+ XmlFormatStyle style = EclipseXmlPrettyPrinter.getForFolderType(folderType);
+ fileContents = EclipseXmlPrettyPrinter.prettyPrint(sb.toString(), formatPrefs,
+ style, null /*lineSeparator*/);
+ }
+
+ // Remove marker tokens and replace them with whitespace
+ fileContents = fileContents.replace(indentToken, formatPrefs.getOneIndentUnit());
+ int caretOffset = fileContents.indexOf(caretToken);
+ if (caretOffset != -1) {
+ fileContents = fileContents.replace(caretToken, ""); //$NON-NLS-1$
+ }
+
+ String error = null;
+ try {
+ byte[] buf = fileContents.getBytes("UTF8"); //$NON-NLS-1$
+ InputStream stream = new ByteArrayInputStream(buf);
+ if (need_delete) {
+ file.delete(IResource.KEEP_HISTORY | IResource.FORCE, null /*monitor*/);
+ }
+ file.create(stream, true /*force*/, null /*progress*/);
+ IRegion region = caretOffset != -1 ? new Region(caretOffset, 0) : null;
+
+ // If you introduced a new locale, or new screen variations etc, ensure that
+ // the list of render previews is updated if necessary
+ if (file.getParent().getName().indexOf('-') != -1
+ && (folderType == ResourceFolderType.LAYOUT
+ || folderType == ResourceFolderType.VALUES)) {
+ RenderPreviewManager.bumpRevision();
+ }
+
+ return Pair.of(file, region);
+ } catch (UnsupportedEncodingException e) {
+ error = e.getMessage();
+ } catch (CoreException e) {
+ error = e.getMessage();
+ }
+
+ error = String.format("Failed to generate %1$s: %2$s", name, error);
+ AdtPlugin.displayError("New Android XML File", error);
+ return null;
+ }
+
+ /**
+ * Returns true if the New XML Wizard can create new files of the given
+ * {@link ResourceFolderType}
+ *
+ * @param folderType the folder type to create a file for
+ * @return true if this wizard can create new files for the given folder type
+ */
+ public static boolean canCreateXmlFile(ResourceFolderType folderType) {
+ TypeInfo typeInfo = NewXmlFileCreationPage.getTypeInfo(folderType);
+ return typeInfo != null && (typeInfo.getDefaultRoot(null /*project*/) != null ||
+ typeInfo.getRootSeed() instanceof String);
+ }
+
+ /**
+ * Creates a new XML file using the template according to the given folder type
+ *
+ * @param project the project to create the file in
+ * @param file the file to be created
+ * @param folderType the type of folder to look up a template for
+ * @return the created file
+ */
+ public static Pair<IFile, IRegion> createXmlFile(IProject project, IFile file,
+ ResourceFolderType folderType) {
+ TypeInfo type = NewXmlFileCreationPage.getTypeInfo(folderType);
+ String xmlns = type.getXmlns();
+ String root = type.getDefaultRoot(project);
+ if (root == null) {
+ root = type.getRootSeed().toString();
+ }
+ String attrs = type.getDefaultAttrs(project, root);
+ return createXmlFile(file, xmlns, root, attrs, null, folderType);
+ }
+
+ /**
+ * Returns an image descriptor for the wizard logo.
+ */
+ private void setImageDescriptor() {
+ ImageDescriptor desc = IconFactory.getInstance().getImageDescriptor(PROJECT_LOGO_LARGE);
+ setDefaultPageImageDescriptor(desc);
+ }
+
+ /**
+ * Specific New XML File wizard tied to the {@link ResourceFolderType#LAYOUT} type
+ */
+ public static class NewLayoutWizard extends NewXmlFileWizard {
+ /** Creates a new {@link NewLayoutWizard} */
+ public NewLayoutWizard() {
+ }
+
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ super.init(workbench, selection);
+ setWindowTitle("New Android Layout XML File");
+ super.mMainPage.setTitle("New Android Layout XML File");
+ super.mMainPage.setDescription("Creates a new Android Layout XML file.");
+ super.mMainPage.setInitialFolderType(ResourceFolderType.LAYOUT);
+ }
+ }
+
+ /**
+ * Specific New XML File wizard tied to the {@link ResourceFolderType#VALUES} type
+ */
+ public static class NewValuesWizard extends NewXmlFileWizard {
+ /** Creates a new {@link NewValuesWizard} */
+ public NewValuesWizard() {
+ }
+
+ @Override
+ public void init(IWorkbench workbench, IStructuredSelection selection) {
+ super.init(workbench, selection);
+ setWindowTitle("New Android Values XML File");
+ super.mMainPage.setTitle("New Android Values XML File");
+ super.mMainPage.setDescription("Creates a new Android Values XML file.");
+ super.mMainPage.setInitialFolderType(ResourceFolderType.VALUES);
+ }
+ }
+
+ /** Value object which holds the current state of the wizard pages */
+ public static class Values {
+ /** The currently selected project, or null */
+ public IProject project;
+ /** The root name of the XML file to create, or null */
+ public String name;
+ /** The type of XML file to create */
+ public TypeInfo type;
+ /** The path within the project to create the new file in */
+ public String folderPath;
+ /** The currently chosen configuration */
+ public FolderConfiguration configuration = new FolderConfiguration();
+
+ /**
+ * Returns the destination filename or an empty string.
+ *
+ * @return the filename, never null.
+ */
+ public String getFileName() {
+ String fileName;
+ if (name == null) {
+ fileName = ""; //$NON-NLS-1$
+ } else {
+ fileName = name.trim();
+ if (fileName.length() > 0 && fileName.indexOf('.') == -1) {
+ fileName = fileName + SdkConstants.DOT_XML;
+ }
+ }
+
+ return fileName;
+ }
+
+ /**
+ * Returns a {@link IFile} for the destination file.
+ * <p/>
+ * Returns null if the project, filename or folder are invalid and the
+ * destination file cannot be determined.
+ * <p/>
+ * The {@link IFile} is a resource. There might or might not be an
+ * actual real file.
+ *
+ * @return an {@link IFile} for the destination file
+ */
+ public IFile getDestinationFile() {
+ String fileName = getFileName();
+ if (project != null && folderPath != null && folderPath.length() > 0
+ && fileName.length() > 0) {
+ IPath dest = new Path(folderPath).append(fileName);
+ IFile file = project.getFile(dest);
+ return file;
+ }
+ return null;
+ }
+ }
+}