1 /*******************************************************************************
2  * Copyright (c) 2000, 2012 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.debug.core.refactoring;
15 
16 import java.util.ArrayList;
17 import java.util.List;
18 
19 import org.eclipse.core.resources.IFile;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.debug.core.DebugPlugin;
22 import org.eclipse.debug.core.ILaunchConfiguration;
23 import org.eclipse.jdt.core.IJavaElement;
24 import org.eclipse.jdt.core.IJavaProject;
25 import org.eclipse.jdt.core.IPackageFragment;
26 import org.eclipse.jdt.core.IPackageFragmentRoot;
27 import org.eclipse.jdt.core.IType;
28 import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
29 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
30 import org.eclipse.ltk.core.refactoring.Change;
31 import org.eclipse.ltk.core.refactoring.CompositeChange;
32 
33 /**
34  *
35  * provides methods to create refactoring changes
36  */
37 public class JDTDebugRefactoringUtil {
38 
39 	/**
40 	 * Take a list of Changes, and return a unique Change, a CompositeChange, or null.
41 	 */
createChangeFromList(List<Change> changes, String changeLabel)42 	public static Change createChangeFromList(List<Change> changes, String changeLabel) {
43 		int nbChanges= changes.size();
44 		switch (nbChanges) {
45 		case 0:
46 			return null;
47 		case 1:
48 			return changes.get(0);
49 		default:
50 			return new CompositeChange(changeLabel, changes.toArray(new Change[changes.size()]));
51 		}
52 	}
53 
54 	/**
55 	 * Returns the new container name for the given project and launch configuration
56 	 * @param javaProject the java to get the new container name for
57 	 * @param launchConfiguration the associated launch configuration
58 	 * @return the new container name
59 	 * @since 3.2
60 	 */
computeNewContainerName(ILaunchConfiguration launchConfiguration)61 	protected static String computeNewContainerName(ILaunchConfiguration launchConfiguration) {
62         IFile file = launchConfiguration.getFile();
63         if (file != null) {
64             return file.getParent().getProjectRelativePath().toString();
65         }
66         return null;
67     }
68 
69 	/**
70 	 * Returns a change for the given launch configuration if the launch configuration needs to
71 	 * be updated for this IType change. It specifically looks to see if the main type of the launch configuration
72 	 * is an inner type of the given IType.
73 	 * @param config the launch configuration
74 	 * @param type the type to check for
75 	 * @param newfqname the new fully qualified name
76 	 * @param pname the new project name
77 	 * @return the <code>Change</code> for this outer type
78 	 * @throws CoreException
79 	 * @since 3.2
80 	 */
createChangesForOuterTypeChange(ILaunchConfiguration config, IType type, String newfqname, String pname)81 	protected static Change createChangesForOuterTypeChange(ILaunchConfiguration config, IType type, String newfqname, String pname) throws CoreException {
82 		IType[] innerTypes = type.getTypes();
83 		if(innerTypes.length == 0) {
84 			return null;
85 		}
86 		Change change = null;
87 		String mtname = config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String)null);
88 		for (int i= 0; i < innerTypes.length; i++) {
89 			String newTypeName = newfqname + '$' + innerTypes[i].getElementName();
90 			// if it matches, check the type
91 			if (innerTypes[i].getFullyQualifiedName().equals(mtname)) {
92 				return new LaunchConfigurationProjectMainTypeChange(config, newTypeName, pname);
93 			}
94 			// if it's not the type, check the inner types
95 			change = createChangesForOuterTypeChange(config, innerTypes[i], newTypeName, pname);
96 		}
97 		return change;
98 	}
99 
100 	/**
101 	 * Provides a public mechanism for creating the <code>Change</code> for moving a package
102 	 * @param packageFragment the fragment to move
103 	 * @param destination the destination to move it to
104 	 * @return the <code>Change</code> for moving the package
105 	 * @throws CoreException
106 	 * @since 3.2
107 	 */
createChangesForPackageMove(IPackageFragment pfragment, IPackageFragmentRoot destination)108 	public static Change createChangesForPackageMove(IPackageFragment pfragment, IPackageFragmentRoot destination) throws CoreException {
109 		List<Change> changes = new ArrayList<>();
110 		ILaunchConfiguration[] configs = getJavaTypeLaunchConfigurations(pfragment.getJavaProject().getElementName());
111 		String mtname = null;
112 		for (int i= 0; i < configs.length; i++) {
113 			mtname = configs[i].getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String)null);
114 			if(mtname != null) {
115 				if(mtname.lastIndexOf(pfragment.getElementName()) > -1) {
116 					changes.add(new LaunchConfigurationProjectMainTypeChange(configs[i], null, destination.getJavaProject().getElementName()));
117 				}
118 			}
119 		}
120 		return JDTDebugRefactoringUtil.createChangeFromList(changes, RefactoringMessages.LaunchConfigurationProjectMainTypeChange_7);
121 	}
122 
123 	/**
124 	 * Provides a public mechanism for creating the <code>Change</code> for renaming a package
125 	 * @param packageFragment the fragment to rename
126 	 * @param newName the new name for the fragment
127 	 * @return the Change for the renaming
128 	 * @throws CoreException
129 	 * @since 3.2
130 	 */
createChangesForPackageRename(IPackageFragment pfragment, String newname)131 	public static Change createChangesForPackageRename(IPackageFragment pfragment, String newname) throws CoreException {
132 		List<Change> changes = new ArrayList<>();
133 		ILaunchConfiguration[] configs = getJavaTypeLaunchConfigurations(pfragment.getJavaProject().getElementName());
134 		String mtname;
135 		for (int i= 0; i < configs.length; i++) {
136 			mtname = configs[i].getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String)null);
137 			if(mtname != null) {
138 				String pkname = ""; //$NON-NLS-1$
139 				int index = mtname.lastIndexOf('.');
140 				if(index > 0) {
141 					pkname = mtname.substring(0, index);
142 				}
143 				if (pfragment.getElementName().equals(pkname)) {
144 					String ntname = newname + '.' + mtname.substring(index + 1);
145 					changes.add(new LaunchConfigurationProjectMainTypeChange(configs[i], ntname, null));
146 				}
147 			}
148 			else {
149 				changes.add(new LaunchConfigurationProjectMainTypeChange(configs[i], null, null));
150 			}
151 		}
152 		return JDTDebugRefactoringUtil.createChangeFromList(changes, RefactoringMessages.LaunchConfigurationProjectMainTypeChange_7);
153 	}
154 
155 	/**
156 	 * Provides a public mechanism for creating the <code>Change</code> for renaming a project
157 	 * @param javaProject the project to rename
158 	 * @param newProjectName the new name for the project
159 	 * @return the Change for the project rename
160 	 * @throws CoreException
161 	 * @since 3.2
162 	 */
createChangesForProjectRename(IJavaProject project, String newname)163 	public static Change createChangesForProjectRename(IJavaProject project, String newname) throws CoreException {
164 		List<Change> changes = new ArrayList<>();
165 		ILaunchConfiguration[] configs = getJavaTypeLaunchConfigurations(project.getElementName());
166 		LaunchConfigurationProjectMainTypeChange change = null;
167 		for (int i= 0; i < configs.length; i++) {
168 			change = new LaunchConfigurationProjectMainTypeChange(configs[i], null, newname);
169             String newcname = computeNewContainerName(configs[i]);
170             if (newcname != null) {
171                 change.setNewContainerName(newcname);
172             }
173 			changes.add(change);
174 		}
175 		return JDTDebugRefactoringUtil.createChangeFromList(changes, RefactoringMessages.LaunchConfigurationProjectMainTypeChange_7);
176 	}
177 
178 	/**
179 	 * Creates a <code>Change</code> for a type change
180 	 * @param type the type that is changing
181 	 * @param newfqname the new fully qualified name
182 	 * @param pname the project name
183 	 * @return the <code>Change</code> for changing the specified type
184 	 * @throws CoreException
185 	 * @since 3.2
186 	 */
createChangesForTypeChange(IType type, String newfqname, String pname)187 	protected static Change createChangesForTypeChange(IType type, String newfqname, String pname) throws CoreException {
188 		List<Change> changes = new ArrayList<>();
189 		String typename = type.getFullyQualifiedName();
190 		ILaunchConfiguration[] configs = getJavaTypeLaunchConfigurations(type.getJavaProject().getElementName());
191 		String mtname;
192 		for (int i= 0; i < configs.length; i++) {
193 			mtname = configs[i].getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String)null);
194 			if (typename.equals(mtname)) {
195 				changes.add(new LaunchConfigurationProjectMainTypeChange(configs[i], newfqname, pname));
196 			}
197 			else {
198 				Change change = createChangesForOuterTypeChange(configs[i], type, newfqname, pname);
199 				if (change != null) {
200 					changes.add(change);
201 				}
202 			}
203 		}
204 		return JDTDebugRefactoringUtil.createChangeFromList(changes, RefactoringMessages.LaunchConfigurationProjectMainTypeChange_7);
205 	}
206 
207 	/**
208 	 * Provides a public mechanism for creating the <code>Change</code> for moving a type
209 	 * @param type the type being moved
210 	 * @param destination the destination to move the type to
211 	 * @return the <code>Change</code> for the type move
212 	 * @throws CoreException
213 	 * @since 3.2
214 	 */
createChangesForTypeMove(IType type, IJavaElement destination)215 	public static Change createChangesForTypeMove(IType type, IJavaElement destination) throws CoreException {
216 		IJavaProject pdestination = destination.getJavaProject();
217 		String newpname = null;
218 		if (!type.getJavaProject().equals(pdestination)) {
219 			newpname = pdestination.getElementName();
220 		}
221 		String newfqname = type.getElementName();
222 		if (destination instanceof IType) {
223 			newfqname = ((IType)destination).getFullyQualifiedName() + '$' + type.getElementName();
224 		}
225 		else if (destination instanceof IPackageFragment) {
226 			if (!((IPackageFragment) destination).isDefaultPackage()) {
227 				newfqname = destination.getElementName() + '.' + type.getElementName();
228 			}
229 		}
230 		return createChangesForTypeChange(type, newfqname, newpname);
231 	}
232 
233 	/**
234 	 * Provides a public mechanism for creating the <code>Change</code> for renaming a type
235 	 * @param type the type to rename
236 	 * @param newname the new name for the type
237 	 * @return the <code>Change</code> for the type rename
238 	 * @throws CoreException
239 	 * @since 3.2
240 	 */
createChangesForTypeRename(IType type, String newname)241 	public static Change createChangesForTypeRename(IType type, String newname) throws CoreException {
242 		IType dtype = type.getDeclaringType();
243 		String newfqname = newname;
244 		if (dtype == null) {
245 			IPackageFragment packageFragment = type.getPackageFragment();
246 			if (!packageFragment.isDefaultPackage()) {
247 				newfqname = packageFragment.getElementName() + '.' + newname;
248 			}
249 		}
250 		else {
251 			newfqname = dtype.getFullyQualifiedName() + '$' + newname;
252 		}
253 		return createChangesForTypeChange(type, newfqname, null);
254 	}
255 
256 	/**
257 	 * Returns a listing of configurations that have a specific project name attribute in them
258 	 * @param pname the project attribute to compare against
259 	 * @return the list of java type launch configurations that have the specified project attribute
260 	 * @since 3.2
261 	 */
getJavaTypeLaunchConfigurations(String pname)262 	protected static ILaunchConfiguration[] getJavaTypeLaunchConfigurations(String pname) {
263 		try {
264 			ILaunchConfiguration[] configs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations();
265 			ArrayList<ILaunchConfiguration> list = new ArrayList<>();
266 			String attrib;
267 			for(int i = 0; i < configs.length; i++) {
268 				attrib = configs[i].getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String)null);
269 				if(attrib != null) {
270 					if(attrib.equals(pname)) {
271 						list.add(configs[i]);
272 					}
273 				}
274 			}
275 			return list.toArray(new ILaunchConfiguration[list.size()]);
276 		}
277 		catch(CoreException e) {JDIDebugPlugin.log(e);}
278 		return new ILaunchConfiguration[0];
279 	}
280 
281 }
282