OSDN Git Service

Add newline inside new root elements, and some move code around
authorTor Norbye <tnorbye@google.com>
Fri, 18 Mar 2011 02:59:38 +0000 (19:59 -0700)
committerTor Norbye <tnorbye@google.com>
Mon, 21 Mar 2011 19:24:15 +0000 (12:24 -0700)
Tweak the "New XML File" creation code to insert a new line inside the
created root element, indent, and place the cursor there.  This means
that you end up with:

    <LinearLayout>
        |
    </LinearLayout>

instead of (where | denotes the initial caret position):

   |<LinearLayout>
    </LinearLayout>

This means you can instantly press Ctrl-Space to complete on the
elements, etc., and is similar to how Eclipse creates new method
bodies from templates, adding newlines such that you can instantly
write code there instead of having to "open them up".

All the diffs for this are in NewXmlFileWizard.java; the other
modifications in this changeset are simple move refactoring
operations as described next:

The second change in this changeset is moving some code around such
that common utility methods live in more natural places rather than
where they were first needed.

In particular:
Hyperlinks.openUrl                             => AdtPlugin
Hyperlinks.openJavaClass                       => AdtPlugin
Hyperlinks.openFile                            => AdtPlugin
AndroidContentAssist.getAndroidXmlEditor       => AndroidXmlEditor
ResourceChooser.canCreateResource              => ResourceHelper
ResourceChooser.createResource                 => ResourceHelper
Hyperlinks.parseResource                       => ResourceHelper
ResourceNameValidator.isValueBasedResourceType => ResourceHelper
ResourceNameValidator.isFileBasedResourceType  => ResourceHelper

Change-Id: If7690df9ce59c709c1d909ffd9f74c2684f3dd46

14 files changed:
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/AaptQuickFix.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidContentAssist.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlEditor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/xml/Hyperlinks.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidator.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newxmlfile/NewXmlFileWizard.java
eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/quickfix1-expected-quickFix3.xml
eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java
eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceNameValidatorTest.java

index 36eeb04..4cd5e8a 100644 (file)
@@ -23,11 +23,13 @@ import com.android.ide.common.resources.ResourceFile;
 import com.android.ide.common.resources.ResourceFolder;
 import com.android.ide.common.sdk.LoadStatus;
 import com.android.ide.eclipse.adt.internal.VersionCheck;
+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.layout.LayoutEditor;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder;
 import com.android.ide.eclipse.adt.internal.editors.menu.MenuEditor;
 import com.android.ide.eclipse.adt.internal.editors.resources.ResourcesEditor;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
 import com.android.ide.eclipse.adt.internal.editors.xml.XmlEditor;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs.BuildVerbosity;
@@ -64,10 +66,14 @@ import org.eclipse.core.runtime.SubMonitor;
 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.JavaUI;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.PropertyChangeEvent;
 import org.eclipse.swt.graphics.Color;
@@ -79,7 +85,10 @@ import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.browser.IWebBrowser;
+import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
 import org.eclipse.ui.console.ConsolePlugin;
 import org.eclipse.ui.console.IConsole;
 import org.eclipse.ui.console.IConsoleConstants;
@@ -1752,4 +1761,74 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger {
     public void warning(String format, Object... args) {
         log(IStatus.WARNING, format, args);
     }
+
+    /**
+     * Opens the given URL in a browser tab
+     *
+     * @param url the URL to open in a browser
+     */
+    public static void openUrl(URL url) {
+        IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
+        IWebBrowser browser;
+        try {
+            browser = support.createBrowser(PLUGIN_ID);
+            browser.openURL(url);
+        } catch (PartInitException e) {
+            log(e, null);
+        }
+    }
+
+    /**
+     * Opens a Java class for the given fully qualified class name
+     *
+     * @param project the project containing the class
+     * @param fqcn the fully qualified class name of the class to be opened
+     * @return true if the class was opened, false otherwise
+     */
+    public static boolean openJavaClass(IProject project, String fqcn) {
+        if (fqcn == null) {
+            return false;
+        }
+
+        // Handle inner classes
+        if (fqcn.indexOf('$') != -1) {
+            fqcn = fqcn.replaceAll("\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        try {
+            if (project.hasNature(JavaCore.NATURE_ID)) {
+                IJavaProject javaProject = JavaCore.create(project);
+                IJavaElement result = javaProject.findType(fqcn);
+                if (result != null) {
+                    return JavaUI.openInEditor(result) != null;
+                }
+            }
+        } catch (Throwable e) {
+            log(e, "Can't open class %1$s", fqcn); //$NON-NLS-1$
+        }
+
+        return false;
+    }
+
+    /**
+     * Opens the given file and shows the given (optional) region
+     *
+     * @param file the file to be opened
+     * @param region an optional region which if set will be selected and shown to the
+     *            user
+     * @throws PartInitException if something goes wrong
+     */
+    public static void openFile(IFile file, IRegion region) throws PartInitException {
+        IEditorPart sourceEditor = Hyperlinks.getEditor();
+        IWorkbenchPage page = sourceEditor.getEditorSite().getPage();
+        IEditorPart targetEditor = IDE.openEditor(page, file, true);
+        if (targetEditor instanceof AndroidXmlEditor) {
+            AndroidXmlEditor editor = (AndroidXmlEditor) targetEditor;
+            if (region != null) {
+                editor.show(region.getOffset(), region.getLength());
+            } else {
+                editor.setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
+            }
+        }
+    }
 }
index 55de6b1..436fcac 100644 (file)
@@ -18,10 +18,8 @@ package com.android.ide.eclipse.adt.internal.build;
 
 import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
-import com.android.ide.eclipse.adt.internal.ui.ResourceChooser;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
 import com.android.resources.ResourceType;
 import com.android.util.Pair;
 
@@ -89,7 +87,7 @@ public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistPr
                 provider.connect(markerResource);
                 IDocument document = provider.getDocument(markerResource);
                 String resource = document.get(start, length);
-                if (ResourceChooser.canCreateResource(resource)) {
+                if (ResourceHelper.canCreateResource(resource)) {
                     return new IMarkerResolution[] {
                         new CreateResourceProposal(project, resource)
                     };
@@ -128,7 +126,7 @@ public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistPr
         // we'll make sure that that editor has the same sourceViewer such that
         // we are indeed looking at the right file:
         ISourceViewer sourceViewer = invocationContext.getSourceViewer();
-        AndroidXmlEditor editor = AndroidContentAssist.getAndroidXmlEditor(sourceViewer);
+        AndroidXmlEditor editor = AndroidXmlEditor.getAndroidXmlEditor(sourceViewer);
         if (editor != null) {
             IFile file = editor.getInputFile();
 
@@ -153,7 +151,7 @@ public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistPr
                             String resource = document.get(start, length);
                             // Can only offer create value for non-framework value
                             // resources
-                            if (ResourceChooser.canCreateResource(resource)) {
+                            if (ResourceHelper.canCreateResource(resource)) {
                                 IProject project = editor.getProject();
                                 return new ICompletionProposal[] {
                                     new CreateResourceProposal(project, resource)
@@ -188,7 +186,7 @@ public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistPr
         }
 
         private void perform() {
-            Pair<ResourceType,String> resource = Hyperlinks.parseResource(mResource);
+            Pair<ResourceType,String> resource = ResourceHelper.parseResource(mResource);
             ResourceType type = resource.getFirst();
             String name = resource.getSecond();
             String value = ""; //$NON-NLS-1$
@@ -206,12 +204,12 @@ public class AaptQuickFix implements IMarkerResolutionGenerator2, IQuickAssistPr
             }
 
             Pair<IFile, IRegion> location =
-                ResourceChooser.createResource(mProject, type, name, value);
+                ResourceHelper.createResource(mProject, type, name, value);
             if (location != null) {
                 IFile file = location.getFirst();
                 IRegion region = location.getSecond();
                 try {
-                    Hyperlinks.openFile(file, region);
+                    AdtPlugin.openFile(file, region);
                 } catch (PartInitException e) {
                     AdtPlugin.log(e, "Can't open file %1$s", file.getName());
                 }
index bffcd3f..b75b703 100644 (file)
@@ -49,13 +49,8 @@ import org.eclipse.jface.text.contentassist.ICompletionProposal;
 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
 import org.eclipse.jface.text.contentassist.IContextInformation;
 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
-import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.swt.graphics.Image;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
-import org.eclipse.ui.PlatformUI;
 import org.w3c.dom.Element;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
@@ -126,7 +121,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
     public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
 
         if (mEditor == null) {
-            mEditor = getAndroidXmlEditor(viewer);
+            mEditor = AndroidXmlEditor.getAndroidXmlEditor(viewer);
             if (mEditor == null) {
                 // This should not happen. Duck and forget.
                 AdtPlugin.log(IStatus.ERROR, "Editor not found during completion");
@@ -903,28 +898,6 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
     }
 
     /**
-     * Returns the active {@link AndroidXmlEditor} matching this source viewer.
-     */
-    public static AndroidXmlEditor getAndroidXmlEditor(ITextViewer viewer) {
-        IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
-        if (wwin != null) {
-            IWorkbenchPage page = wwin.getActivePage();
-            if (page != null) {
-                IEditorPart editor = page.getActiveEditor();
-                if (editor instanceof AndroidXmlEditor) {
-                    ISourceViewer ssviewer =
-                        ((AndroidXmlEditor) editor).getStructuredSourceViewer();
-                    if (ssviewer == viewer) {
-                        return (AndroidXmlEditor) editor;
-                    }
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
      * Fixed list of dimension units, along with user documentation, for use by
      * {@link #completeSuffix}.
      */
index c4f5ef6..e72cb41 100644 (file)
@@ -40,6 +40,7 @@ import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.source.ISourceViewer;
 import org.eclipse.swt.custom.StyledText;
 import org.eclipse.swt.widgets.Display;
@@ -49,7 +50,9 @@ import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
 import org.eclipse.ui.forms.IManagedForm;
@@ -616,7 +619,7 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
                         getEditorInput() == null ? "null" : getEditorInput().toString()
                         );
 
-                org.eclipse.core.runtime.IAdaptable adaptable= (org.eclipse.core.runtime.IAdaptable) getEditorInput();
+                org.eclipse.core.runtime.IAdaptable adaptable= getEditorInput();
                 IFile file1 = (IFile)adaptable.getAdapter(IFile.class);
                 org.eclipse.core.runtime.IPath location= file1.getFullPath();
                 org.eclipse.core.resources.IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
@@ -1196,6 +1199,32 @@ public abstract class AndroidXmlEditor extends FormEditor implements IResourceCh
     }
 
     /**
+     * Returns the active {@link AndroidXmlEditor}, provided it matches the given source
+     * viewer
+     *
+     * @param viewer the source viewer to ensure the active editor is associated with
+     * @return the active editor provided it matches the given source viewer
+     */
+    public static AndroidXmlEditor getAndroidXmlEditor(ITextViewer viewer) {
+        IWorkbenchWindow wwin = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        if (wwin != null) {
+            IWorkbenchPage page = wwin.getActivePage();
+            if (page != null) {
+                IEditorPart editor = page.getActiveEditor();
+                if (editor instanceof AndroidXmlEditor) {
+                    ISourceViewer ssviewer =
+                        ((AndroidXmlEditor) editor).getStructuredSourceViewer();
+                    if (ssviewer == viewer) {
+                        return (AndroidXmlEditor) editor;
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
      * Listen to changes in the underlying XML model in the structured editor.
      */
     private class XmlModelStateListener implements IModelStateListener {
index 72ac188..07eb7ef 100644 (file)
@@ -58,7 +58,6 @@ import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
 import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -2054,7 +2053,7 @@ public class GraphicalEditorPart extends EditorPart
 
             if (r instanceof ClassLinkStyleRange) {
                 String fqcn = mErrorLabel.getText(r.start, r.start + r.length - 1);
-                if (!Hyperlinks.openJavaClass(getProject(), fqcn)) {
+                if (!AdtPlugin.openJavaClass(getProject(), fqcn)) {
                     createNewClass(fqcn);
                 }
             }
index 0353d1f..e4b1890 100755 (executable)
@@ -31,6 +31,7 @@ import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.ViewInfo;
 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
@@ -50,7 +51,6 @@ import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
 import com.android.ide.eclipse.adt.internal.editors.ui.IDecorContent;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
@@ -571,7 +571,7 @@ public class PaletteControl extends Composite {
                     @Override
                     public void mouseDown(MouseEvent e) {
                         if ((e.stateMask & SWT.MOD1) != 0) {
-                            Hyperlinks.openJavaClass(mEditor.getProject(), fqcn);
+                            AdtPlugin.openJavaClass(mEditor.getProject(), fqcn);
                         }
                     }
                 });
index b0628bd..37647e8 100644 (file)
@@ -40,12 +40,11 @@ import com.android.ide.common.resources.ResourceFolder;
 import com.android.ide.common.resources.ResourceRepository;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart;
 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor;
 import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
-import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
@@ -119,8 +118,6 @@ import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.browser.IWebBrowser;
-import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
 import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.part.FileEditorInput;
 import org.eclipse.ui.part.MultiPageEditorPart;
@@ -216,7 +213,7 @@ public class Hyperlinks {
             return false;
         }
 
-        Pair<ResourceType,String> resource = parseResource(value);
+        Pair<ResourceType,String> resource = ResourceHelper.parseResource(value);
         if (resource != null) {
             ResourceType type = resource.getFirst();
             if (type != null) {
@@ -275,10 +272,10 @@ public class Hyperlinks {
     private static boolean openManifestName(IProject project, XmlContext context) {
         if (isActivity(context)) {
             String fqcn = getActivityClassFqcn(context);
-            return openJavaClass(project, fqcn);
+            return AdtPlugin.openJavaClass(project, fqcn);
         } else if (isService(context)) {
             String fqcn = getServiceClassFqcn(context);
-            return openJavaClass(project, fqcn);
+            return AdtPlugin.openJavaClass(project, fqcn);
         } else if (isBuiltinPermission(context)) {
             String permission = context.getAttribute().getValue();
             // Mutate something like android.permission.ACCESS_CHECKIN_PROPERTIES
@@ -289,7 +286,7 @@ public class Hyperlinks {
 
             URL url = getDocUrl(relative);
             if (url != null) {
-                openUrl(url);
+                AdtPlugin.openUrl(url);
                 return true;
             } else {
                 return false;
@@ -310,7 +307,7 @@ public class Hyperlinks {
             }
             URL url = getDocUrl(relative);
             if (url != null) {
-                openUrl(url);
+                AdtPlugin.openUrl(url);
                 return true;
             } else {
                 return false;
@@ -403,18 +400,6 @@ public class Hyperlinks {
         }
     }
 
-    /** Opens the given URL in a browser tab */
-    private static void openUrl(URL url) {
-        IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
-        IWebBrowser browser;
-        try {
-            browser = support.createBrowser(AdtPlugin.PLUGIN_ID);
-            browser.openURL(url);
-        } catch (PartInitException e) {
-            AdtPlugin.log(e, null);
-        }
-    }
-
     /** Returns true if the context is pointing to a permission name reference */
     private static boolean isBuiltinPermission(XmlContext context) {
         Attr attribute = context.getAttribute();
@@ -520,7 +505,7 @@ public class Hyperlinks {
         if (isManifestName(context)) {
             return openManifestName(project, context);
         } else if (isClassElement(context) || isClassAttribute(context)) {
-            return openJavaClass(project, getClassFqcn(context));
+            return AdtPlugin.openJavaClass(project, getClassFqcn(context));
         } else if (isOnClickAttribute(context)) {
             return openOnClickMethod(project, context.getAttribute().getValue());
         } else {
@@ -528,28 +513,6 @@ public class Hyperlinks {
         }
     }
 
-    /**
-     * Opens the given file and shows the given (optional) region
-     *
-     * @param file the file to be opened
-     * @param region an optional region which if set will be selected and shown to the
-     *            user
-     * @throws PartInitException if something goes wrong
-     */
-    public static void openFile(IFile file, IRegion region) throws PartInitException {
-        IEditorPart sourceEditor = getEditor();
-        IWorkbenchPage page = sourceEditor.getEditorSite().getPage();
-        IEditorPart targetEditor = IDE.openEditor(page, file, true);
-        if (targetEditor instanceof AndroidXmlEditor) {
-            AndroidXmlEditor editor = (AndroidXmlEditor) targetEditor;
-            if (region != null) {
-                editor.show(region.getOffset(), region.getLength());
-            } else {
-                editor.setActivePage(AndroidXmlEditor.TEXT_EDITOR_ID);
-            }
-        }
-    }
-
     /** Opens a path (which may not be in the workspace) */
     private static void openPath(IPath filePath, IRegion region, int offset) {
         IEditorPart sourceEditor = getEditor();
@@ -561,7 +524,7 @@ public class Hyperlinks {
             IResource file = workspace.findMember(relativePath);
             if (file instanceof IFile) {
                 try {
-                    openFile((IFile)file, region);
+                    AdtPlugin.openFile((IFile) file, region);
                     return;
                 } catch (PartInitException ex) {
                     AdtPlugin.log(ex, "Can't open %$1s", filePath); //$NON-NLS-1$
@@ -610,38 +573,6 @@ public class Hyperlinks {
     }
 
     /**
-     * Opens a Java class for the given fully qualified class name
-     *
-     * @param project the project containing the class
-     * @param fqcn the fully qualified class name of the class to be opened
-     * @return true if the class was opened, false otherwise
-     */
-    public static boolean openJavaClass(IProject project, String fqcn) {
-        if (fqcn == null) {
-            return false;
-        }
-
-        // Handle inner classes
-        if (fqcn.indexOf('$') != -1) {
-            fqcn = fqcn.replaceAll("\\$", "."); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-
-        try {
-            if (project.hasNature(JavaCore.NATURE_ID)) {
-                IJavaProject javaProject = JavaCore.create(project);
-                IJavaElement result = javaProject.findType(fqcn);
-                if (result != null) {
-                    return JavaUI.openInEditor(result) != null;
-                }
-            }
-        } catch (Throwable e) {
-            AdtPlugin.log(e, "Can't open class %1$s", fqcn); //$NON-NLS-1$
-        }
-
-        return false;
-    }
-
-    /**
      * Opens a Java method referenced by the given on click attribute method name
      *
      * @param project the project containing the click handler
@@ -1035,34 +966,6 @@ public class Hyperlinks {
         return null;
     }
 
-    /** Return the resource type of the given url, and the resource name */
-    public static Pair<ResourceType,String> parseResource(String url) {
-        if (!url.startsWith("@")) { //$NON-NLS-1$
-            return null;
-        }
-        int typeEnd = url.indexOf('/', 1);
-        if (typeEnd == -1) {
-            return null;
-        }
-        int nameBegin = typeEnd + 1;
-
-        // Skip @ and @+
-        int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$
-
-        int colon = url.lastIndexOf(':', typeEnd);
-        if (colon != -1) {
-            typeBegin = colon + 1;
-        }
-        String typeName = url.substring(typeBegin, typeEnd);
-        ResourceType type = ResourceType.getEnum(typeName);
-        if (type == null) {
-            return null;
-        }
-        String name = url.substring(nameBegin);
-
-        return Pair.of(type, name);
-    }
-
     /** Parses the given file and locates a definition of the given resource */
     private static Pair<File, Integer> findValueInXml(ResourceType type, String name, File file) {
         // We can't use the StructureModelManager on files outside projects
@@ -1127,7 +1030,7 @@ public class Hyperlinks {
         IProject project = Hyperlinks.getProject();
         FolderConfiguration configuration = getConfiguration();
 
-        Pair<ResourceType,String> resource = parseResource(url);
+        Pair<ResourceType,String> resource = ResourceHelper.parseResource(url);
         if (resource == null || resource.getFirst() == null) {
             return null;
         }
@@ -1171,7 +1074,7 @@ public class Hyperlinks {
                 });
 
                 // Is this something found in a values/ folder?
-                boolean valueResource = ResourceNameValidator.isValueBasedResourceType(type);
+                boolean valueResource = ResourceHelper.isValueBasedResourceType(type);
 
                 for (ResourceFile file : matches) {
                     String folderName = file.getFolder().getFolder().getName();
@@ -1482,7 +1385,7 @@ public class Hyperlinks {
                 Pair<IFile,IRegion> def = findIdDefinition(project, mName);
                 if (def != null) {
                     try {
-                        openFile(def.getFirst(), def.getSecond());
+                        AdtPlugin.openFile(def.getFirst(), def.getSecond());
                     } catch (PartInitException e) {
                         AdtPlugin.log(e, null);
                     }
@@ -1499,13 +1402,14 @@ public class Hyperlinks {
                 try {
                     // Lazily search for the target?
                     IRegion region = null;
-                    if (mType != null && mName != null && EXT_XML.equals(file.getFileExtension())) {
+                    String extension = file.getFileExtension();
+                    if (mType != null && mName != null && EXT_XML.equals(extension)) {
                         Pair<IFile, IRegion> target = findValueInXml(mType, mName, file);
                         if (target != null) {
                             region = target.getSecond();
                         }
                     }
-                    openFile(file, region);
+                    AdtPlugin.openFile(file, region);
                 } catch (PartInitException e) {
                     AdtPlugin.log(e, null);
                 }
index 73d3c93..428dbbf 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.resources;
 
+import static com.android.AndroidConstants.FD_RES_VALUES;
+import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
+import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
+import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
+import static com.android.sdklib.SdkConstants.FD_RESOURCES;
+
 import com.android.ide.common.resources.ResourceDeltaKind;
 import com.android.ide.common.resources.configuration.CountryCodeQualifier;
 import com.android.ide.common.resources.configuration.DockModeQualifier;
@@ -36,17 +43,52 @@ import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
 import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
 import com.android.ide.common.resources.configuration.TouchScreenQualifier;
 import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.ide.eclipse.adt.AdtPlugin;
+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.layout.refactoring.VisualRefactoring;
+import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
+import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
+import com.android.resources.FolderTypeRelationship;
+import com.android.resources.ResourceFolderType;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
 
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
 import org.eclipse.swt.graphics.Image;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Helper class to deal with SWT specifics for the resources.
  */
+@SuppressWarnings("restriction") // XML model
 public class ResourceHelper {
 
     private final static Map<Class<?>, Image> sIconMap = new HashMap<Class<?>, Image>(
@@ -101,4 +143,270 @@ public class ResourceHelper {
 
         return null;
     }
+
+    /**
+     * Return the resource type of the given url, and the resource name
+     *
+     * @param url the resource url to be parsed
+     * @return a pair of the resource type and the resource name
+     */
+    public static Pair<ResourceType,String> parseResource(String url) {
+        if (!url.startsWith("@")) { //$NON-NLS-1$
+            return null;
+        }
+        int typeEnd = url.indexOf('/', 1);
+        if (typeEnd == -1) {
+            return null;
+        }
+        int nameBegin = typeEnd + 1;
+
+        // Skip @ and @+
+        int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$
+
+        int colon = url.lastIndexOf(':', typeEnd);
+        if (colon != -1) {
+            typeBegin = colon + 1;
+        }
+        String typeName = url.substring(typeBegin, typeEnd);
+        ResourceType type = ResourceType.getEnum(typeName);
+        if (type == null) {
+            return null;
+        }
+        String name = url.substring(nameBegin);
+
+        return Pair.of(type, name);
+    }
+
+    /**
+     * Is this a resource that can be defined in any file within the "values" folder?
+     * <p>
+     * Some resource types can be defined <b>both</b> as a separate XML file as well
+     * as defined within a value XML file. This method will return true for these types
+     * as well. In other words, a ResourceType can return true for both
+     * {@link #isValueBasedResourceType} and {@link #isFileBasedResourceType}.
+     *
+     * @param type the resource type to check
+     * @return true if the given resource type can be represented as a value under the
+     *         values/ folder
+     */
+    public static boolean isValueBasedResourceType(ResourceType type) {
+        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+        for (ResourceFolderType folderType : folderTypes) {
+            if (folderType == ResourceFolderType.VALUES) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Is this a resource that is defined in a file named by the resource plus the XML
+     * extension?
+     * <p>
+     * Some resource types can be defined <b>both</b> as a separate XML file as well as
+     * defined within a value XML file along with other properties. This method will
+     * return true for these resource types as well. In other words, a ResourceType can
+     * return true for both {@link #isValueBasedResourceType} and
+     * {@link #isFileBasedResourceType}.
+     *
+     * @param type the resource type to check
+     * @return true if the given resource type is stored in a file named by the resource
+     */
+    public static boolean isFileBasedResourceType(ResourceType type) {
+        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
+        for (ResourceFolderType folderType : folderTypes) {
+            if (folderType != ResourceFolderType.VALUES) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns true if this class can create the given resource
+     *
+     * @param resource the resource to be created
+     * @return true if the {@link #createResource} method can create this resource
+     */
+    public static boolean canCreateResource(String resource) {
+        // Cannot create framework resources
+        if (resource.startsWith('@' + ANDROID_PKG + ':')) {
+            return false;
+        }
+
+        Pair<ResourceType,String> parsed = parseResource(resource);
+        if (parsed != null) {
+            ResourceType type = parsed.getFirst();
+            String name = parsed.getSecond();
+
+            // Make sure the name is valid
+            ResourceNameValidator validator =
+                ResourceNameValidator.create(false, (Set<String>) null /* existing */, type);
+            if (validator.isValid(name) != null) {
+                return false;
+            }
+
+            // We can create all value types
+            if (isValueBasedResourceType(type)) {
+                return true;
+            }
+
+            // We can create -some- file-based types - those supported by the New XML wizard:
+            for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) {
+                if (NewXmlFileWizard.canCreateXmlFile(folderType)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /** Creates a file-based resource, like a layout. Used by {@link #createResource} */
+    private static Pair<IFile,IRegion> createFileResource(IProject project, ResourceType type,
+            String name) {
+
+        ResourceFolderType folderType = null;
+        for (ResourceFolderType f : FolderTypeRelationship.getRelatedFolders(type)) {
+            if (NewXmlFileWizard.canCreateXmlFile(f)) {
+                folderType = f;
+                break;
+            }
+        }
+        if (folderType == null) {
+            return null;
+        }
+
+        // Find "dimens.xml" file in res/values/ (or corresponding name for other
+        // value types)
+        IPath projectPath = new Path(FD_RESOURCES + WS_SEP + folderType.getName() + WS_SEP
+            + name + '.' + EXT_XML);
+        IFile file = project.getFile(projectPath);
+        return NewXmlFileWizard.createXmlFile(project, file, folderType);
+    }
+
+    /**
+     * Creates a resource of a given type, name and (if applicable) value
+     *
+     * @param project the project to contain the resource
+     * @param type the type of resource
+     * @param name the name of the resource
+     * @param value the value of the resource, if it is a value-type resource
+     * @return a pair of the file containing the resource and a region where the value
+     *         appears
+     */
+    public static Pair<IFile,IRegion> createResource(IProject project, ResourceType type,
+            String name, String value) {
+        if (!isValueBasedResourceType(type)) {
+            return createFileResource(project, type, name);
+        }
+
+        // Find "dimens.xml" file in res/values/ (or corresponding name for other
+        // value types)
+        String fileName = type.getName() + 's';
+        String projectPath = FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
+            + fileName + '.' + EXT_XML;
+        Object editRequester = project;
+        IResource member = project.findMember(projectPath);
+        if (member != null) {
+            if (member instanceof IFile) {
+                IFile file = (IFile) member;
+                // File exists: Must add item to the XML
+                IModelManager manager = StructuredModelManager.getModelManager();
+                IStructuredModel model = null;
+                try {
+                    model = manager.getExistingModelForEdit(file);
+                    if (model == null) {
+                        model = manager.getModelForEdit(file);
+                    }
+                    if (model instanceof IDOMModel) {
+                        model.beginRecording(editRequester, String.format("Add %1$s",
+                                type.getDisplayName()));
+                        IDOMModel domModel = (IDOMModel) model;
+                        Document document = domModel.getDocument();
+                        Element root = document.getDocumentElement();
+                        IStructuredDocument structuredDocument = model.getStructuredDocument();
+                        Node lastElement = null;
+                        NodeList childNodes = root.getChildNodes();
+                        String indent = null;
+                        for (int i = childNodes.getLength() - 1; i >= 0; i--) {
+                            Node node = childNodes.item(i);
+                            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                                lastElement = node;
+                                indent = AndroidXmlEditor.getIndent(structuredDocument, node);
+                                break;
+                            }
+                        }
+                        if (indent == null || indent.length() == 0) {
+                            indent = "    "; //$NON-NLS-1$
+                        }
+                        Node nextChild = lastElement != null ? lastElement.getNextSibling() : null;
+                        Text indentNode = document.createTextNode('\n' + indent);
+                        root.insertBefore(indentNode, nextChild);
+                        Element element = document.createElement(Hyperlinks.getTagName(type));
+                        element.setAttribute(NAME_ATTR, name);
+                        root.insertBefore(element, nextChild);
+                        Text valueNode = document.createTextNode(value);
+                        element.appendChild(valueNode);
+                        model.save();
+                        IndexedRegion domRegion = VisualRefactoring.getRegion(valueNode);
+                        int startOffset = domRegion.getStartOffset();
+                        int length = domRegion.getLength();
+                        IRegion region = new Region(startOffset, length);
+                        return Pair.of(file, region);
+                    }
+                } catch (Exception e) {
+                    AdtPlugin.log(e, "Cannot access XML value model");
+                } finally {
+                    if (model != null) {
+                        model.endRecording(editRequester);
+                        model.releaseFromEdit();
+                    }
+                }
+            }
+
+            return null;
+        } else {
+            // No such file exists: just create it
+            String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
+            StringBuilder sb = new StringBuilder(prolog);
+
+            String root = ResourcesDescriptors.ROOT_ELEMENT;
+            sb.append('<').append(root).append('>').append('\n');
+            sb.append("    "); //$NON-NLS-1$
+            sb.append('<');
+            sb.append(type.getName());
+            sb.append(" name=\""); //$NON-NLS-1$
+            sb.append(name);
+            sb.append('"');
+            sb.append('>');
+            int start = sb.length();
+            sb.append(value);
+            int end = sb.length();
+            sb.append('<').append('/');
+            sb.append(type.getName());
+            sb.append(">\n");                            //$NON-NLS-1$
+            sb.append('<').append('/').append(root).append('>').append('\n');
+            String result = sb.toString();
+            String error = null;
+            try {
+                byte[] buf = result.getBytes("UTF8");    //$NON-NLS-1$
+                InputStream stream = new ByteArrayInputStream(buf);
+                IFile file = project.getFile(new Path(projectPath));
+                file.create(stream, true /*force*/, null /*progress*/);
+                IRegion region = new Region(start, end - start);
+                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;
+    }
 }
index b19f7b1..4c1127a 100644 (file)
@@ -23,7 +23,6 @@ import com.android.ide.eclipse.adt.AdtConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
-import com.android.resources.FolderTypeRelationship;
 import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 
@@ -34,7 +33,6 @@ import org.eclipse.jface.dialogs.IInputValidator;
 
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -143,7 +141,7 @@ public class ResourceNameValidator implements IInputValidator {
      */
     public static ResourceNameValidator create(boolean allowXmlExtension, Set<String> existing,
             ResourceType type) {
-        boolean isFileType = isFileBasedResourceType(type);
+        boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
         return new ResourceNameValidator(allowXmlExtension, existing, isFileType);
     }
 
@@ -166,54 +164,7 @@ public class ResourceNameValidator implements IInputValidator {
             existing.add(item.getName());
         }
 
-        boolean isFileType = isFileBasedResourceType(type);
+        boolean isFileType = ResourceHelper.isFileBasedResourceType(type);
         return new ResourceNameValidator(allowXmlExtension, existing, isFileType);
     }
-
-    /**
-     * Is this a resource that is defined in a file named by the resource plus the XML
-     * extension?
-     * <p>
-     * Some resource types can be defined <b>both</b> as a separate XML file as well as
-     * defined within a value XML file along with other properties. This method will
-     * return true for these resource types as well. In other words, a ResourceType can
-     * return true for both {@link #isValueBasedResourceType} and
-     * {@link #isFileBasedResourceType}.
-     *
-     * @param type the resource type to check
-     * @return true if the given resource type is stored in a file named by the resource
-     */
-    public static boolean isFileBasedResourceType(ResourceType type) {
-        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-        for (ResourceFolderType folderType : folderTypes) {
-            if (folderType != ResourceFolderType.VALUES) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Is this a resource that can be defined in any file within the "values" folder?
-     * <p>
-     * Some resource types can be defined <b>both</b> as a separate XML file as well
-     * as defined within a value XML file. This method will return true for these types
-     * as well. In other words, a ResourceType can return true for both
-     * {@link #isValueBasedResourceType} and {@link #isFileBasedResourceType}.
-     *
-     * @param type the resource type to check
-     * @return true if the given resource type can be represented as a value under the
-     *         values/ folder
-     */
-    public static boolean isValueBasedResourceType(ResourceType type) {
-        List<ResourceFolderType> folderTypes = FolderTypeRelationship.getRelatedFolders(type);
-        for (ResourceFolderType folderType : folderTypes) {
-            if (folderType == ResourceFolderType.VALUES) {
-                return true;
-            }
-        }
-
-        return false;
-    }
 }
index 9404a39..129b4e9 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.ui;
 
-import static com.android.AndroidConstants.FD_RES_VALUES;
-import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
-import static com.android.ide.eclipse.adt.AdtConstants.EXT_XML;
-import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
-import static com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors.NAME_ATTR;
-import static com.android.sdklib.SdkConstants.FD_RESOURCES;
 
 import com.android.ide.common.resources.ResourceItem;
 import com.android.ide.common.resources.ResourceRepository;
 import com.android.ide.eclipse.adt.AdtPlugin;
-import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
-import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.VisualRefactoring;
-import com.android.ide.eclipse.adt.internal.editors.resources.descriptors.ResourcesDescriptors;
-import com.android.ide.eclipse.adt.internal.editors.xml.Hyperlinks;
 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring;
 import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard;
+import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
 import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator;
-import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
-import com.android.resources.FolderTypeRelationship;
-import com.android.resources.ResourceFolderType;
 import com.android.resources.ResourceType;
 import com.android.util.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.core.runtime.Status;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.IInputValidator;
 import org.eclipse.jface.text.IRegion;
-import org.eclipse.jface.text.Region;
 import org.eclipse.jface.window.Window;
 import org.eclipse.ltk.ui.refactoring.RefactoringWizard;
 import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
@@ -70,21 +53,7 @@ import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
 import org.eclipse.ui.dialogs.SelectionStatusDialog;
-import org.eclipse.wst.sse.core.StructuredModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
-import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
-import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
-import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Text;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -94,7 +63,6 @@ import java.util.regex.Pattern;
 /**
  * A dialog to let the user select a resource based on a resource type.
  */
-@SuppressWarnings("restriction") // XML model
 public class ResourceChooser extends AbstractElementListSelectionDialog {
 
     private Pattern mProjectResourcePattern;
@@ -226,7 +194,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
         mNewButton.setText(title);
 
         // We only support adding new values right now
-        mNewButton.setEnabled(ResourceNameValidator.isValueBasedResourceType(mResourceType));
+        mNewButton.setEnabled(ResourceHelper.isValueBasedResourceType(mResourceType));
 
         mNewButton.addSelectionListener(new SelectionAdapter() {
             @Override
@@ -236,7 +204,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
                 if (mResourceType == ResourceType.STRING) {
                     createNewString();
                 } else {
-                    assert ResourceNameValidator.isValueBasedResourceType(mResourceType);
+                    assert ResourceHelper.isValueBasedResourceType(mResourceType);
                     String newName = createNewValue(mResourceType);
                     if (newName != null) {
                         // Recompute the "current resource" to select the new id
@@ -285,7 +253,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
             return null;
         }
 
-        Pair<IFile, IRegion> resource = createResource(mProject, type, name, value);
+        Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, value);
         if (resource != null) {
             return name;
         }
@@ -293,185 +261,6 @@ public class ResourceChooser extends AbstractElementListSelectionDialog {
         return null;
     }
 
-    /**
-     * Returns true if this class can create the given resource
-     *
-     * @param resource the resource to be created
-     * @return true if the {@link #createResource} method can create this resource
-     */
-    public static boolean canCreateResource(String resource) {
-        // Cannot create framework resources
-        if (resource.startsWith('@' + ANDROID_PKG + ':')) {
-            return false;
-        }
-
-        Pair<ResourceType,String> parsed = Hyperlinks.parseResource(resource);
-        if (parsed != null) {
-            // We can create all value types
-            ResourceType type = parsed.getFirst();
-            if (ResourceNameValidator.isValueBasedResourceType(type)) {
-                return true;
-            }
-
-            // We can create -some- file-based types - those supported by the New XML wizard:
-
-            for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) {
-                if (NewXmlFileWizard.canCreateXmlFile(folderType)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /** Creates a file-based resource, like a layout */
-    private static Pair<IFile,IRegion> createFileResource(IProject project, ResourceType type,
-            String name) {
-
-        ResourceFolderType folderType = null;
-        for (ResourceFolderType f : FolderTypeRelationship.getRelatedFolders(type)) {
-            if (NewXmlFileWizard.canCreateXmlFile(f)) {
-                folderType = f;
-                break;
-            }
-        }
-        if (folderType == null) {
-            return null;
-        }
-
-        // Find "dimens.xml" file in res/values/ (or corresponding name for other
-        // value types)
-        IPath projectPath = new Path(FD_RESOURCES + WS_SEP + folderType.getName() + WS_SEP
-            + name + '.' + EXT_XML);
-        IFile file = project.getFile(projectPath);
-        IRegion region = new Region(0, 0);
-        return Pair.of(NewXmlFileWizard.createXmlFile(project, file, folderType), region);
-    }
-
-    /**
-     * Creates a resource of a given type, name and (if applicable) value
-     *
-     * @param project the project to contain the resource
-     * @param type the type of resource
-     * @param name the name of the resource
-     * @param value the value of the resource, if it is a value-type resource
-     * @return a pair of the file containing the resource and a region where the value
-     *         appears
-     */
-    public static Pair<IFile,IRegion> createResource(IProject project, ResourceType type,
-            String name, String value) {
-        if (!ResourceNameValidator.isValueBasedResourceType(type)) {
-            return createFileResource(project, type, name);
-        }
-
-        // Find "dimens.xml" file in res/values/ (or corresponding name for other
-        // value types)
-        String fileName = type.getName() + 's';
-        String projectPath = FD_RESOURCES + WS_SEP + FD_RES_VALUES + WS_SEP
-            + fileName + '.' + EXT_XML;
-        Object editRequester = project;
-        IResource member = project.findMember(projectPath);
-        if (member != null) {
-            if (member instanceof IFile) {
-                IFile file = (IFile) member;
-                // File exists: Must add item to the XML
-                IModelManager manager = StructuredModelManager.getModelManager();
-                IStructuredModel model = null;
-                try {
-                    model = manager.getExistingModelForEdit(file);
-                    if (model == null) {
-                        model = manager.getModelForEdit(file);
-                    }
-                    if (model instanceof IDOMModel) {
-                        model.beginRecording(editRequester, String.format("Add %1$s",
-                                type.getDisplayName()));
-                        IDOMModel domModel = (IDOMModel) model;
-                        Document document = domModel.getDocument();
-                        Element root = document.getDocumentElement();
-                        IStructuredDocument structuredDocument = model.getStructuredDocument();
-                        Node lastElement = null;
-                        NodeList childNodes = root.getChildNodes();
-                        String indent = null;
-                        for (int i = childNodes.getLength() - 1; i >= 0; i--) {
-                            Node node = childNodes.item(i);
-                            if (node.getNodeType() == Node.ELEMENT_NODE) {
-                                lastElement = node;
-                                indent = AndroidXmlEditor.getIndent(structuredDocument, node);
-                                break;
-                            }
-                        }
-                        if (indent == null || indent.length() == 0) {
-                            indent = "    "; //$NON-NLS-1$
-                        }
-                        Node nextChild = lastElement != null ? lastElement.getNextSibling() : null;
-                        Text indentNode = document.createTextNode('\n' + indent);
-                        root.insertBefore(indentNode, nextChild);
-                        Element element = document.createElement(Hyperlinks.getTagName(type));
-                        element.setAttribute(NAME_ATTR, name);
-                        root.insertBefore(element, nextChild);
-                        Text valueNode = document.createTextNode(value);
-                        element.appendChild(valueNode);
-                        model.save();
-                        IndexedRegion domRegion = VisualRefactoring.getRegion(valueNode);
-                        int startOffset = domRegion.getStartOffset();
-                        int length = domRegion.getLength();
-                        IRegion region = new Region(startOffset, length);
-                        return Pair.of(file, region);
-                    }
-                } catch (Exception e) {
-                    AdtPlugin.log(e, "Cannot access XML value model");
-                } finally {
-                    if (model != null) {
-                        model.endRecording(editRequester);
-                        model.releaseFromEdit();
-                    }
-                }
-            }
-
-            return null;
-        } else {
-            // No such file exists: just create it
-            String prolog = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; //$NON-NLS-1$
-            StringBuilder sb = new StringBuilder(prolog);
-
-            String root = ResourcesDescriptors.ROOT_ELEMENT;
-            sb.append('<').append(root).append('>').append('\n');
-            sb.append("    "); //$NON-NLS-1$
-            sb.append('<');
-            sb.append(type.getName());
-            sb.append(" name=\""); //$NON-NLS-1$
-            sb.append(name);
-            sb.append('"');
-            sb.append('>');
-            int start = sb.length();
-            sb.append(value);
-            int end = sb.length();
-            sb.append('<').append('/');
-            sb.append(type.getName());
-            sb.append(">\n");                            //$NON-NLS-1$
-            sb.append('<').append('/').append(root).append('>').append('\n');
-            String result = sb.toString();
-            String error = null;
-            try {
-                byte[] buf = result.getBytes("UTF8");    //$NON-NLS-1$
-                InputStream stream = new ByteArrayInputStream(buf);
-                IFile file = project.getFile(new Path(projectPath));
-                file.create(stream, true /*force*/, null /*progress*/);
-                IRegion region = new Region(start, end - start);
-                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;
-    }
-
     private void createNewString() {
         ExtractStringRefactoring ref = new ExtractStringRefactoring(
                 mProject, true /*enforceNew*/);
index 6bce6fb..9c0d31c 100644 (file)
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
 import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
 import com.android.resources.ResourceFolderType;
+import com.android.util.Pair;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
@@ -31,15 +32,13 @@ import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IStatus;
 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.INewWizard;
 import org.eclipse.ui.IWorkbench;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PartInitException;
-import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.ide.IDE;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
@@ -107,22 +106,19 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
      */
     @Override
     public boolean performFinish() {
-        IFile file = createXmlFile();
-        if (file == null) {
+        Pair<IFile, IRegion> created = createXmlFile();
+        if (created == null) {
             return false;
         } else {
+            IFile file = created.getFirst();
+            IRegion region = created.getSecond();
+
             // Open the file in an editor
-            IWorkbenchWindow win = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
-            if (win != null) {
-                IWorkbenchPage page = win.getActivePage();
-                if (page != null) {
-                    try {
-                        IDE.openEditor(page, file);
-                    } catch (PartInitException e) {
-                        AdtPlugin.log(e, "Failed to create %1$s: missing type",  //$NON-NLS-1$
-                                file.getFullPath().toString());
-                    }
-                }
+            try {
+                AdtPlugin.openFile(file, region);
+            } catch (PartInitException e) {
+                AdtPlugin.log(e, "Failed to create %1$s: missing type",  //$NON-NLS-1$
+                        file.getFullPath().toString());
             }
             return true;
         }
@@ -130,7 +126,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
 
     // -- Custom Methods --
 
-    private IFile createXmlFile() {
+    private Pair<IFile, IRegion> createXmlFile() {
         IFile file = mMainPage.getDestinationFile();
         TypeInfo type = mMainPage.getSelectedType();
         if (type == null) {
@@ -154,7 +150,7 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
     }
 
     /** Creates a new file using the given root element, namespace and root attributes */
-    private static IFile createXmlFile(IFile file, String xmlns,
+    private static Pair<IFile, IRegion> createXmlFile(IFile file, String xmlns,
             String root, String rootAttributes) {
         String name = file.getFullPath().toString();
         boolean need_delete = false;
@@ -183,6 +179,12 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
         }
 
         sb.append(">\n");                            //$NON-NLS-1$
+
+        // The insertion line
+        sb.append("    ");                           //$NON-NLS-1$
+        int caretOffset = sb.length();
+        sb.append("\n");                             //$NON-NLS-1$
+
         sb.append("</").append(root).append(">\n");  //$NON-NLS-1$ //$NON-NLS-2$
 
         String result = sb.toString();
@@ -194,7 +196,8 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
                 file.delete(IResource.KEEP_HISTORY | IResource.FORCE, null /*monitor*/);
             }
             file.create(stream, true /*force*/, null /*progress*/);
-            return file;
+            IRegion region = new Region(caretOffset, 0);
+            return Pair.of(file, region);
         } catch (UnsupportedEncodingException e) {
             error = e.getMessage();
         } catch (CoreException e) {
@@ -227,7 +230,8 @@ public class NewXmlFileWizard extends Wizard implements INewWizard {
      * @param folderType the type of folder to look up a template for
      * @return the created file
      */
-    public static IFile createXmlFile(IProject project, IFile file, ResourceFolderType folderType) {
+    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();
index 6dc09cf..1a69847 100644 (file)
 
 package com.android.ide.eclipse.adt.internal.resources;
 
+import static com.android.resources.ResourceType.DIMEN;
+import static com.android.resources.ResourceType.LAYOUT;
+
 import com.android.ide.common.resources.ResourceDeltaKind;
 import com.android.ide.common.resources.configuration.FolderConfiguration;
 import com.android.ide.common.resources.configuration.ResourceQualifier;
+import com.android.resources.ResourceType;
 
 import org.eclipse.core.resources.IResourceDelta;
 
 import junit.framework.TestCase;
 
+
 /**
  * Test ResourceHelper
  */
@@ -112,4 +117,57 @@ public class ResourceHelperTest extends TestCase {
 
         assertNull(ResourceHelper.getResourceDeltaKind(IResourceDelta.ADDED_PHANTOM));
     }
+
+    public void testParseResource() {
+        assertNull(ResourceHelper.parseResource(""));
+        assertNull(ResourceHelper.parseResource("not_a_resource"));
+
+        assertEquals(LAYOUT, ResourceHelper.parseResource("@layout/foo").getFirst());
+        assertEquals(DIMEN, ResourceHelper.parseResource("@dimen/foo").getFirst());
+        assertEquals(DIMEN, ResourceHelper.parseResource("@android:dimen/foo").getFirst());
+        assertEquals("foo", ResourceHelper.parseResource("@layout/foo").getSecond());
+        assertEquals("foo", ResourceHelper.parseResource("@dimen/foo").getSecond());
+        assertEquals("foo", ResourceHelper.parseResource("@android:dimen/foo").getSecond());
+    }
+
+
+    public void testIsFileBasedResourceType() throws Exception {
+        assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.ANIMATOR));
+        assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.LAYOUT));
+
+        assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.STRING));
+        assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.DIMEN));
+        assertFalse(ResourceHelper.isFileBasedResourceType(ResourceType.ID));
+
+        // Both:
+        assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.DRAWABLE));
+        assertTrue(ResourceHelper.isFileBasedResourceType(ResourceType.COLOR));
+    }
+
+    public void testIsValueBasedResourceType() throws Exception {
+        assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.STRING));
+        assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DIMEN));
+        assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.ID));
+
+        assertFalse(ResourceHelper.isValueBasedResourceType(ResourceType.LAYOUT));
+
+        // These can be both:
+        assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.DRAWABLE));
+        assertTrue(ResourceHelper.isValueBasedResourceType(ResourceType.COLOR));
+    }
+
+    public void testCanCreateResource() throws Exception {
+        assertTrue(ResourceHelper.canCreateResource("@layout/foo"));
+        assertTrue(ResourceHelper.canCreateResource("@string/foo"));
+        assertTrue(ResourceHelper.canCreateResource("@dimen/foo"));
+        assertTrue(ResourceHelper.canCreateResource("@color/foo"));
+
+        assertFalse(ResourceHelper.canCreateResource("@typo/foo")); // nonexistent type
+        assertFalse(ResourceHelper.canCreateResource("@layout/foo bar")); // space
+        assertFalse(ResourceHelper.canCreateResource("@layout/new")); // keyword
+        assertFalse(ResourceHelper.canCreateResource("@animator/foo")); // unsupported file type
+        assertFalse(ResourceHelper.canCreateResource("@android:string/foo")); // framework
+        assertFalse(ResourceHelper.canCreateResource("@android:dimen/foo"));
+        assertFalse(ResourceHelper.canCreateResource("@android:color/foo"));
+    }
 }
index 2c7c75c..b771667 100644 (file)
@@ -48,29 +48,4 @@ public class ResourceNameValidatorTest extends TestCase {
         assertTrue(ResourceNameValidator.create(true, ResourceFolderType.LAYOUT)
                 .isValid("foo123_") == null);
     }
-
-    public void testIsFileBasedResourceType() throws Exception {
-        assertTrue(ResourceNameValidator.isFileBasedResourceType(ResourceType.ANIMATOR));
-        assertTrue(ResourceNameValidator.isFileBasedResourceType(ResourceType.LAYOUT));
-
-        assertFalse(ResourceNameValidator.isFileBasedResourceType(ResourceType.STRING));
-        assertFalse(ResourceNameValidator.isFileBasedResourceType(ResourceType.DIMEN));
-        assertFalse(ResourceNameValidator.isFileBasedResourceType(ResourceType.ID));
-
-        // Both:
-        assertTrue(ResourceNameValidator.isFileBasedResourceType(ResourceType.DRAWABLE));
-        assertTrue(ResourceNameValidator.isFileBasedResourceType(ResourceType.COLOR));
-    }
-
-    public void testIsValueBasedResourceType() throws Exception {
-        assertTrue(ResourceNameValidator.isValueBasedResourceType(ResourceType.STRING));
-        assertTrue(ResourceNameValidator.isValueBasedResourceType(ResourceType.DIMEN));
-        assertTrue(ResourceNameValidator.isValueBasedResourceType(ResourceType.ID));
-
-        assertFalse(ResourceNameValidator.isValueBasedResourceType(ResourceType.LAYOUT));
-
-        // These can be both:
-        assertTrue(ResourceNameValidator.isValueBasedResourceType(ResourceType.DRAWABLE));
-        assertTrue(ResourceNameValidator.isValueBasedResourceType(ResourceType.COLOR));
-    }
 }