1 /*******************************************************************************
2  * Copyright (c) 2000, 2014 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.ui.actions;
15 
16 import java.util.ArrayList;
17 import java.util.Iterator;
18 import java.util.List;
19 
20 import org.eclipse.osgi.util.TextProcessor;
21 
22 import org.eclipse.swt.SWTError;
23 import org.eclipse.swt.dnd.Clipboard;
24 import org.eclipse.swt.dnd.DND;
25 import org.eclipse.swt.dnd.FileTransfer;
26 import org.eclipse.swt.dnd.TextTransfer;
27 import org.eclipse.swt.dnd.Transfer;
28 import org.eclipse.swt.graphics.Point;
29 
30 import org.eclipse.core.runtime.IPath;
31 
32 import org.eclipse.core.resources.IResource;
33 
34 import org.eclipse.jface.dialogs.MessageDialog;
35 import org.eclipse.jface.viewers.ISelection;
36 import org.eclipse.jface.viewers.IStructuredSelection;
37 
38 import org.eclipse.jface.text.ITextSelection;
39 import org.eclipse.jface.text.source.ISourceViewer;
40 
41 import org.eclipse.ui.IWorkbenchSite;
42 import org.eclipse.ui.PlatformUI;
43 import org.eclipse.ui.part.ResourceTransfer;
44 
45 import org.eclipse.jdt.core.IClassFile;
46 import org.eclipse.jdt.core.ICompilationUnit;
47 import org.eclipse.jdt.core.IImportDeclaration;
48 import org.eclipse.jdt.core.IJarEntryResource;
49 import org.eclipse.jdt.core.IJavaElement;
50 import org.eclipse.jdt.core.IJavaProject;
51 import org.eclipse.jdt.core.IMember;
52 import org.eclipse.jdt.core.IPackageDeclaration;
53 import org.eclipse.jdt.core.IPackageFragment;
54 import org.eclipse.jdt.core.IPackageFragmentRoot;
55 import org.eclipse.jdt.core.ITypeRoot;
56 import org.eclipse.jdt.core.JavaModelException;
57 import org.eclipse.jdt.core.dom.ASTNode;
58 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
59 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
60 import org.eclipse.jdt.core.dom.CompilationUnit;
61 import org.eclipse.jdt.core.dom.Expression;
62 import org.eclipse.jdt.core.dom.IBinding;
63 import org.eclipse.jdt.core.dom.ImportDeclaration;
64 import org.eclipse.jdt.core.dom.MemberRef;
65 import org.eclipse.jdt.core.dom.MemberValuePair;
66 import org.eclipse.jdt.core.dom.MethodDeclaration;
67 import org.eclipse.jdt.core.dom.MethodInvocation;
68 import org.eclipse.jdt.core.dom.Name;
69 import org.eclipse.jdt.core.dom.NodeFinder;
70 import org.eclipse.jdt.core.dom.PackageDeclaration;
71 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
72 import org.eclipse.jdt.core.dom.Type;
73 import org.eclipse.jdt.core.dom.TypeDeclaration;
74 import org.eclipse.jdt.core.dom.TypeParameter;
75 import org.eclipse.jdt.core.dom.VariableDeclaration;
76 import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
77 
78 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
79 
80 import org.eclipse.jdt.ui.JavaElementLabels;
81 import org.eclipse.jdt.ui.JavaUI;
82 import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
83 
84 import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
85 import org.eclipse.jdt.internal.ui.JavaPlugin;
86 import org.eclipse.jdt.internal.ui.JavaPluginImages;
87 import org.eclipse.jdt.internal.ui.browsing.LogicalPackage;
88 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
89 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
90 
91 
92 public class CopyQualifiedNameAction extends SelectionDispatchAction {
93 
94 	private static final long LABEL_FLAGS= Long.valueOf(JavaElementLabels.F_FULLY_QUALIFIED | JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.I_FULLY_QUALIFIED
95 			| JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED | JavaElementLabels.T_TYPE_PARAMETERS | JavaElementLabels.CU_QUALIFIED
96 			| JavaElementLabels.CF_QUALIFIED).longValue();
97 
98 	//TODO: Make API
99 	public static final String ACTION_DEFINITION_ID= "org.eclipse.jdt.ui.edit.text.java.copy.qualified.name"; //$NON-NLS-1$
100 
101 	//TODO: Make API
102 	public static final String ACTION_HANDLER_ID= "org.eclipse.jdt.ui.actions.CopyQualifiedName"; //$NON-NLS-1$
103 
104 	private JavaEditor fEditor;
105 
CopyQualifiedNameAction(JavaEditor editor)106 	public CopyQualifiedNameAction(JavaEditor editor) {
107 		this(editor.getSite());
108 		fEditor= editor;
109 		setEnabled(true);
110 	}
111 
CopyQualifiedNameAction(IWorkbenchSite site)112 	public CopyQualifiedNameAction(IWorkbenchSite site) {
113 		super(site);
114 
115 		setText(ActionMessages.CopyQualifiedNameAction_ActionName);
116 		setToolTipText(ActionMessages.CopyQualifiedNameAction_ToolTipText);
117 		setDisabledImageDescriptor(JavaPluginImages.DESC_DLCL_COPY_QUALIFIED_NAME);
118 		setImageDescriptor(JavaPluginImages.DESC_ELCL_COPY_QUALIFIED_NAME);
119 		PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.COPY_QUALIFIED_NAME_ACTION);
120 	}
121 
122 	@Override
selectionChanged(IStructuredSelection selection)123 	public void selectionChanged(IStructuredSelection selection) {
124 		setEnabled(canEnable(selection.toArray()));
125 	}
126 
127 	@Override
selectionChanged(ITextSelection selection)128 	public void selectionChanged(ITextSelection selection) {
129 		//Must not create an AST
130 	}
131 
canEnable(Object[] objects)132 	private boolean canEnable(Object[] objects) {
133 		for (Object element : objects) {
134 			if (isValidElement(element))
135 				return true;
136 		}
137 
138 		return false;
139 	}
140 
isValidElement(Object element)141 	private boolean isValidElement(Object element) {
142 		if (element instanceof IMember)
143 			return true;
144 
145 		if (element instanceof IClassFile)
146 			return true;
147 
148 		if (element instanceof ICompilationUnit)
149 			return true;
150 
151 		if (element instanceof IPackageDeclaration)
152 			return true;
153 
154 		if (element instanceof IImportDeclaration)
155 			return true;
156 
157 		if (element instanceof IPackageFragment)
158 			return true;
159 
160 		if (element instanceof IPackageFragmentRoot)
161 			return true;
162 
163 		if (element instanceof IJavaProject)
164 			return true;
165 
166 		if (element instanceof IJarEntryResource)
167 			return true;
168 
169 		if (element instanceof IResource)
170 			return true;
171 
172 		if (element instanceof LogicalPackage)
173 			return true;
174 
175 		return false;
176 	}
177 
178 	@Override
run()179 	public void run() {
180 
181 		try {
182 			Object[] elements= getSelectedElements();
183 			if (elements == null) {
184 				MessageDialog.openInformation(getShell(), ActionMessages.CopyQualifiedNameAction_InfoDialogTitel, ActionMessages.CopyQualifiedNameAction_NoElementToQualify);
185 				return;
186 			}
187 
188 			Object[] data= null;
189 			Transfer[] dataTypes= null;
190 
191 			if (elements.length == 1) {
192 				Object element= elements[0];
193 				String qualifiedName= getQualifiedName(element);
194 				IResource resource= null;
195 				if (element instanceof IJavaElement) {
196 					IJavaElement je= ((IJavaElement)element);
197 					if (je.exists())
198 						resource= je.getCorrespondingResource();
199 				} else if (element instanceof IResource)
200 					resource= (IResource)element;
201 
202 				if (resource != null) {
203 					IPath location= resource.getLocation();
204 					if (location != null) {
205 						data= new Object[] { qualifiedName, resource, new String[] { location.toOSString() } };
206 						dataTypes= new Transfer[] { TextTransfer.getInstance(), ResourceTransfer.getInstance(), FileTransfer.getInstance() };
207 					} else {
208 						data= new Object[] { qualifiedName, resource };
209 						dataTypes= new Transfer[] { TextTransfer.getInstance(), ResourceTransfer.getInstance() };
210 					}
211 				} else {
212 					data= new Object[] { qualifiedName };
213 					dataTypes= new Transfer[] { TextTransfer.getInstance() };
214 				}
215 			} else {
216 				StringBuilder buf= new StringBuilder();
217 				buf.append(getQualifiedName(elements[0]));
218 				for (int i= 1; i < elements.length; i++) {
219 					String qualifiedName= getQualifiedName(elements[i]);
220 					buf.append(System.lineSeparator()).append(qualifiedName);
221 				}
222 				data= new Object[] { buf.toString() };
223 				dataTypes= new Transfer[] { TextTransfer.getInstance() };
224 			}
225 
226 			Clipboard clipboard= new Clipboard(getShell().getDisplay());
227 			try {
228 				clipboard.setContents(data, dataTypes);
229 			} catch (SWTError e) {
230 				if (e.code != DND.ERROR_CANNOT_SET_CLIPBOARD) {
231 					throw e;
232 				}
233 				if (MessageDialog.openQuestion(getShell(), ActionMessages.CopyQualifiedNameAction_ErrorTitle, ActionMessages.CopyQualifiedNameAction_ErrorDescription)) {
234 					clipboard.setContents(data, dataTypes);
235 				}
236 			} finally {
237 				clipboard.dispose();
238 			}
239 		} catch (JavaModelException e) {
240 			JavaPlugin.log(e);
241 		}
242 	}
243 
getQualifiedName(Object element)244 	private String getQualifiedName(Object element) throws JavaModelException {
245 		if (element instanceof IResource)
246 			return ((IResource)element).getFullPath().toString();
247 
248 		if (element instanceof IJarEntryResource)
249 			return ((IJarEntryResource)element).getFullPath().toString();
250 
251 		if (element instanceof LogicalPackage)
252 			return ((LogicalPackage)element).getElementName();
253 
254 		if (element instanceof IJavaProject || element instanceof IPackageFragmentRoot || element instanceof ITypeRoot) {
255 			IResource resource= ((IJavaElement)element).getCorrespondingResource();
256 			if (resource != null)
257 				return getQualifiedName(resource);
258 		}
259 
260 		if (element instanceof IBinding)
261 			return BindingLabelProvider.getBindingLabel((IBinding)element, LABEL_FLAGS);
262 
263 		return TextProcessor.deprocess(JavaElementLabels.getTextLabel(element, LABEL_FLAGS));
264 	}
265 
getSelectedElements()266 	private Object[] getSelectedElements() {
267 		if (fEditor != null) {
268 			Object element= getSelectedElement(fEditor);
269 			if (element == null)
270 				return null;
271 
272 			return new Object[] { element };
273 		}
274 
275 		ISelection selection= getSelection();
276 		if (!(selection instanceof IStructuredSelection))
277 			return null;
278 
279 		List<Object> result= new ArrayList<>();
280 		for (Iterator<?> iter= ((IStructuredSelection)selection).iterator(); iter.hasNext();) {
281 			Object element= iter.next();
282 			if (isValidElement(element))
283 				result.add(element);
284 		}
285 		if (result.isEmpty())
286 			return null;
287 
288 		return result.toArray(new Object[result.size()]);
289 	}
290 
getSelectedElement(JavaEditor editor)291 	private Object getSelectedElement(JavaEditor editor) {
292 		ISourceViewer viewer= editor.getViewer();
293 		if (viewer == null)
294 			return null;
295 
296 		Point selectedRange= viewer.getSelectedRange();
297 		int length= selectedRange.y;
298 		int offset= selectedRange.x;
299 
300 		ITypeRoot element= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
301 		if (element == null)
302 			return null;
303 
304 		CompilationUnit ast= SharedASTProviderCore.getAST(element, SharedASTProviderCore.WAIT_YES, null);
305 		if (ast == null)
306 			return null;
307 
308 		NodeFinder finder= new NodeFinder(ast, offset, length);
309 		ASTNode node= finder.getCoveringNode();
310 
311 		IBinding binding= null;
312 		if (node instanceof Name) {
313 			binding= getConstructorBindingIfAvailable((Name)node);
314 			if (binding != null)
315 				return binding;
316 			binding= ((Name)node).resolveBinding();
317 		} else if (node instanceof MethodInvocation) {
318 			binding= ((MethodInvocation)node).resolveMethodBinding();
319 		} else if (node instanceof MethodDeclaration) {
320 			binding= ((MethodDeclaration)node).resolveBinding();
321 		} else if (node instanceof Type) {
322 			binding= ((Type)node).resolveBinding();
323 		} else if (node instanceof AnonymousClassDeclaration) {
324 			binding= ((AnonymousClassDeclaration)node).resolveBinding();
325 		} else if (node instanceof TypeDeclaration) {
326 			binding= ((TypeDeclaration)node).resolveBinding();
327 		} else if (node instanceof CompilationUnit) {
328 			return ((CompilationUnit)node).getJavaElement();
329 		} else if (node instanceof Expression) {
330 			binding= ((Expression)node).resolveTypeBinding();
331 		} else if (node instanceof ImportDeclaration) {
332 			binding= ((ImportDeclaration)node).resolveBinding();
333 		} else if (node instanceof MemberRef) {
334 			binding= ((MemberRef)node).resolveBinding();
335 		} else if (node instanceof MemberValuePair) {
336 			binding= ((MemberValuePair)node).resolveMemberValuePairBinding();
337 		} else if (node instanceof PackageDeclaration) {
338 			binding= ((PackageDeclaration)node).resolveBinding();
339 		} else if (node instanceof TypeParameter) {
340 			binding= ((TypeParameter)node).resolveBinding();
341 		} else if (node instanceof VariableDeclaration) {
342 			binding= ((VariableDeclaration)node).resolveBinding();
343 		}
344 
345 		if (binding != null)
346 			return binding.getJavaElement();
347 
348 		return null;
349 	}
350 
351 	/**
352 	 * Checks whether the given name belongs to a {@link ClassInstanceCreation} and if so, returns
353 	 * its constructor binding.
354 	 *
355 	 * @param nameNode the name node
356 	 * @return the constructor binding or <code>null</code> if not found
357 	 * @since 3.7
358 	 */
getConstructorBindingIfAvailable(Name nameNode)359 	private IBinding getConstructorBindingIfAvailable(Name nameNode) {
360 		ASTNode type= ASTNodes.getNormalizedNode(nameNode);
361 		StructuralPropertyDescriptor loc= type.getLocationInParent();
362 		if (loc == ClassInstanceCreation.TYPE_PROPERTY) {
363 			return ((ClassInstanceCreation) type.getParent()).resolveConstructorBinding();
364 		}
365 		return null;
366 	}
367 
368 }
369