1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.eclipse.org/org/documents/epl-v10.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.ide.eclipse.adt.internal.refactorings.renamepackage;
18
19import com.android.ide.common.xml.ManifestData;
20import com.android.ide.eclipse.adt.AdtPlugin;
21import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
22
23import org.eclipse.core.resources.IProject;
24import org.eclipse.core.runtime.IAdaptable;
25import org.eclipse.core.runtime.Status;
26import org.eclipse.jdt.core.dom.AST;
27import org.eclipse.jdt.core.dom.Name;
28import org.eclipse.jdt.ui.refactoring.RefactoringSaveHelper;
29import org.eclipse.jface.action.IAction;
30import org.eclipse.jface.dialogs.IInputValidator;
31import org.eclipse.jface.dialogs.InputDialog;
32import org.eclipse.jface.viewers.ISelection;
33import org.eclipse.jface.viewers.IStructuredSelection;
34import org.eclipse.jface.window.Window;
35import org.eclipse.ltk.core.refactoring.Refactoring;
36import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation;
37import org.eclipse.ui.IObjectActionDelegate;
38import org.eclipse.ui.IWorkbenchPart;
39import org.eclipse.ui.IWorkbenchWindow;
40import org.eclipse.ui.IWorkbenchWindowActionDelegate;
41
42import java.util.Iterator;
43
44/**
45 * Refactoring steps:
46 * <ol>
47 * <li>Update the "package" attribute of the &lt;manifest&gt; tag with the new
48 * name.</li>
49 * <li>Replace all values for the "android:name" attribute in the
50 * &lt;application&gt; and "component class" (&lt;activity&gt;, &lt;service&gt;,
51 * &lt;receiver&gt;, and &lt;provider&gt;) tags with the non-shorthand version
52 * of the class name</li>
53 * <li>Replace package resource imports (*.R) in .java files</li>
54 * <li>Update package name in the namespace declarations (e.g. "xmlns:app")
55 * used for custom styleable attributes in layout resource files</li>
56 * </ol>
57 * Caveat: Sometimes it is necessary to perform a project-wide
58 * "Organize Imports" afterwards. (CTRL+SHIFT+O when a project has active
59 * selection)
60 */
61public class RenamePackageAction implements IObjectActionDelegate {
62
63    private ISelection mSelection;
64    @SuppressWarnings("unused") private IWorkbenchPart mTargetPart; // TODO cleanup
65
66    /**
67     * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
68     */
69    @Override
70    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
71        mTargetPart = targetPart;
72    }
73
74    @Override
75    public void selectionChanged(IAction action, ISelection selection) {
76        mSelection = selection;
77    }
78
79    /**
80     * @see IWorkbenchWindowActionDelegate#init
81     */
82    public void init(IWorkbenchWindow window) {
83        // pass
84    }
85
86    @Override
87    public void run(IAction action) {
88
89        // Prompt for refactoring on the selected project
90        if (mSelection instanceof IStructuredSelection) {
91            for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator(); it.hasNext();) {
92                Object element = it.next();
93                IProject project = null;
94                if (element instanceof IProject) {
95                    project = (IProject) element;
96                } else if (element instanceof IAdaptable) {
97                    project = (IProject) ((IAdaptable) element).getAdapter(IProject.class);
98                }
99                if (project != null) {
100                    // It is advisable that the user saves before proceeding,
101                    // revealing any compilation errors. The following lines
102                    // enforce a save as a convenience.
103                    RefactoringSaveHelper save_helper = new RefactoringSaveHelper(
104                            RefactoringSaveHelper.SAVE_ALL_ALWAYS_ASK);
105                    if (save_helper.saveEditors(AdtPlugin.getDisplay().getActiveShell())) {
106                        promptNewName(project);
107                    }
108                }
109            }
110        }
111    }
112
113    /*
114     * Validate the new package name and start the refactoring wizard
115     */
116    private void promptNewName(final IProject project) {
117
118        ManifestData manifestData = AndroidManifestHelper.parseForData(project);
119        if (manifestData == null) {
120            return;
121        }
122
123        final String oldPackageNameString = manifestData.getPackage();
124
125        final AST astValidator = AST.newAST(AST.JLS3);
126        Name oldPackageName = astValidator.newName(oldPackageNameString);
127
128        IInputValidator validator = new IInputValidator() {
129
130            @Override
131            public String isValid(String newText) {
132                try {
133                    astValidator.newName(newText);
134                } catch (IllegalArgumentException e) {
135                    return "Illegal package name.";
136                }
137
138                if (newText.equals(oldPackageNameString))
139                    return "No change.";
140                else
141                    return null;
142            }
143        };
144
145        InputDialog dialog = new InputDialog(AdtPlugin.getDisplay().getActiveShell(),
146                "Rename Application Package", "Enter new package name:", oldPackageNameString,
147                validator);
148
149        if (dialog.open() == Window.OK) {
150            Name newPackageName = astValidator.newName(dialog.getValue());
151            initiateAndroidPackageRefactoring(project, oldPackageName, newPackageName);
152        }
153    }
154
155
156    private void initiateAndroidPackageRefactoring(
157            final IProject project,
158            Name oldPackageName,
159            Name newPackageName) {
160
161        Refactoring package_name_refactoring =
162            new ApplicationPackageNameRefactoring(project, oldPackageName, newPackageName);
163
164        ApplicationPackageNameRefactoringWizard wizard =
165            new ApplicationPackageNameRefactoringWizard(package_name_refactoring);
166        RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard);
167        try {
168            op.run(AdtPlugin.getDisplay().getActiveShell(), package_name_refactoring.getName());
169        } catch (InterruptedException e) {
170            Status s = new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, e.getMessage(), e);
171            AdtPlugin.getDefault().getLog().log(s);
172        }
173    }
174}
175