]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2012 IBM Corporation and others. | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * IBM Corporation - initial API and implementation | |
10 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.ui.javaeditor; | |
12 | ||
13 | import java.lang.reflect.InvocationTargetException; | |
14 | import java.util.ArrayList; | |
15 | import java.util.Iterator; | |
16 | import java.util.List; | |
17 | ||
18 | import org.eclipse.swt.SWT; | |
19 | import org.eclipse.swt.custom.StackLayout; | |
20 | import org.eclipse.swt.custom.StyledText; | |
21 | import org.eclipse.swt.events.DisposeEvent; | |
22 | import org.eclipse.swt.events.DisposeListener; | |
23 | import org.eclipse.swt.events.SelectionEvent; | |
24 | import org.eclipse.swt.events.SelectionListener; | |
25 | import org.eclipse.swt.graphics.Color; | |
26 | import org.eclipse.swt.graphics.Font; | |
27 | import org.eclipse.swt.layout.FillLayout; | |
28 | import org.eclipse.swt.layout.GridData; | |
29 | import org.eclipse.swt.layout.GridLayout; | |
30 | import org.eclipse.swt.widgets.Button; | |
31 | import org.eclipse.swt.widgets.Composite; | |
32 | import org.eclipse.swt.widgets.Control; | |
33 | import org.eclipse.swt.widgets.Display; | |
34 | import org.eclipse.swt.widgets.Label; | |
35 | import org.eclipse.swt.widgets.Shell; | |
36 | ||
37 | import org.eclipse.core.runtime.Assert; | |
38 | import org.eclipse.core.runtime.CoreException; | |
39 | import org.eclipse.core.runtime.IPath; | |
40 | import org.eclipse.core.runtime.IProgressMonitor; | |
41 | import org.eclipse.core.runtime.IStatus; | |
42 | import org.eclipse.core.runtime.Status; | |
43 | import org.eclipse.core.runtime.jobs.Job; | |
44 | ||
45 | import org.eclipse.core.resources.IFile; | |
46 | ||
47 | import org.eclipse.jface.action.Action; | |
48 | import org.eclipse.jface.action.IAction; | |
49 | import org.eclipse.jface.action.IMenuManager; | |
50 | import org.eclipse.jface.operation.IRunnableWithProgress; | |
51 | import org.eclipse.jface.preference.IPreferenceStore; | |
52 | import org.eclipse.jface.resource.JFaceResources; | |
53 | import org.eclipse.jface.util.IPropertyChangeListener; | |
54 | import org.eclipse.jface.util.PropertyChangeEvent; | |
55 | ||
56 | import org.eclipse.jface.text.IWidgetTokenKeeper; | |
57 | import org.eclipse.jface.text.source.ISourceViewer; | |
58 | import org.eclipse.jface.text.source.IVerticalRuler; | |
59 | ||
60 | import org.eclipse.ui.IActionBars; | |
61 | import org.eclipse.ui.IEditorInput; | |
62 | import org.eclipse.ui.IFileEditorInput; | |
63 | import org.eclipse.ui.IWorkbenchCommandConstants; | |
64 | import org.eclipse.ui.PlatformUI; | |
65 | import org.eclipse.ui.actions.ActionContext; | |
66 | import org.eclipse.ui.actions.ActionGroup; | |
67 | ||
68 | import org.eclipse.ui.texteditor.IDocumentProvider; | |
69 | import org.eclipse.ui.texteditor.ITextEditorActionConstants; | |
70 | ||
71 | import org.eclipse.jdt.core.ClasspathContainerInitializer; | |
72 | import org.eclipse.jdt.core.IClassFile; | |
73 | import org.eclipse.jdt.core.IClasspathAttribute; | |
74 | import org.eclipse.jdt.core.IClasspathContainer; | |
75 | import org.eclipse.jdt.core.IClasspathEntry; | |
76 | import org.eclipse.jdt.core.IJavaElement; | |
77 | import org.eclipse.jdt.core.IJavaModelStatusConstants; | |
78 | import org.eclipse.jdt.core.IJavaProject; | |
79 | import org.eclipse.jdt.core.IPackageFragmentRoot; | |
80 | import org.eclipse.jdt.core.JavaCore; | |
81 | import org.eclipse.jdt.core.JavaModelException; | |
82 | import org.eclipse.jdt.core.ToolFactory; | |
83 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
84 | import org.eclipse.jdt.core.util.ClassFileBytesDisassembler; | |
85 | import org.eclipse.jdt.core.util.ClassFormatException; | |
86 | ||
87 | import org.eclipse.jdt.internal.corext.util.JavaModelUtil; | |
88 | import org.eclipse.jdt.internal.corext.util.Messages; | |
89 | ||
90 | import org.eclipse.jdt.ui.JavaElementLabels; | |
91 | import org.eclipse.jdt.ui.SharedASTProvider; | |
92 | import org.eclipse.jdt.ui.actions.RefactorActionGroup; | |
93 | import org.eclipse.jdt.ui.wizards.BuildPathDialogAccess; | |
94 | ||
95 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
96 | import org.eclipse.jdt.internal.ui.JavaUIStatus; | |
97 | import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup; | |
98 | import org.eclipse.jdt.internal.ui.util.ExceptionHandler; | |
99 | import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; | |
100 | import org.eclipse.jdt.internal.ui.wizards.buildpaths.SourceAttachmentBlock; | |
101 | ||
102 | ||
103 | /** | |
104 | * Java specific text editor. | |
105 | */ | |
106 | public class ClassFileEditor extends JavaEditor implements ClassFileDocumentProvider.InputChangeListener { | |
107 | ||
108 | ||
109 | /** | |
110 | * A form to attach source to a class file. | |
111 | */ | |
112 | private class SourceAttachmentForm implements IPropertyChangeListener { | |
113 | ||
114 | private final IClassFile fFile; | |
115 | private Composite fComposite; | |
116 | private Color fBackgroundColor; | |
117 | private Color fForegroundColor; | |
118 | private Color fSeparatorColor; | |
119 | private List<Label> fBannerLabels= new ArrayList<Label>(); | |
120 | private List<Label> fHeaderLabels= new ArrayList<Label>(); | |
121 | private Font fFont; | |
122 | ||
123 | /** | |
124 | * Creates a source attachment form for a class file. | |
125 | * | |
126 | * @param file the class file | |
127 | */ | |
128 | public SourceAttachmentForm(IClassFile file) { | |
129 | fFile= file; | |
130 | } | |
131 | ||
132 | /** | |
133 | * Returns the package fragment root of this file. | |
134 | * | |
135 | * @param file the class file | |
136 | * @return the package fragment root of the given class file | |
137 | */ | |
138 | private IPackageFragmentRoot getPackageFragmentRoot(IClassFile file) { | |
139 | ||
140 | IJavaElement element= file.getParent(); | |
141 | while (element != null && element.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) | |
142 | element= element.getParent(); | |
143 | ||
144 | return (IPackageFragmentRoot) element; | |
145 | } | |
146 | ||
147 | /** | |
148 | * Creates the control of the source attachment form. | |
149 | * | |
150 | * @param parent the parent composite | |
151 | * @return the creates source attachment form | |
152 | */ | |
153 | public Control createControl(Composite parent) { | |
154 | ||
155 | Display display= parent.getDisplay(); | |
156 | fBackgroundColor= display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); | |
157 | fForegroundColor= display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); | |
158 | fSeparatorColor= new Color(display, 152, 170, 203); | |
159 | ||
160 | JFaceResources.getFontRegistry().addListener(this); | |
161 | ||
162 | fComposite= createComposite(parent); | |
163 | fComposite.setLayout(new GridLayout()); | |
164 | fComposite.addDisposeListener(new DisposeListener() { | |
165 | public void widgetDisposed(DisposeEvent e) { | |
166 | JFaceResources.getFontRegistry().removeListener(SourceAttachmentForm.this); | |
167 | fComposite= null; | |
168 | fSeparatorColor.dispose(); | |
169 | fSeparatorColor= null; | |
170 | fBannerLabels.clear(); | |
171 | fHeaderLabels.clear(); | |
172 | if (fFont != null) { | |
173 | fFont.dispose(); | |
174 | fFont= null; | |
175 | } | |
176 | } | |
177 | }); | |
178 | ||
179 | createTitleLabel(fComposite, JavaEditorMessages.SourceAttachmentForm_title); | |
180 | createLabel(fComposite, null); | |
181 | createLabel(fComposite, null); | |
182 | ||
183 | createHeadingLabel(fComposite, JavaEditorMessages.SourceAttachmentForm_heading); | |
184 | ||
185 | Composite separator= createCompositeSeparator(fComposite); | |
186 | GridData data= new GridData(GridData.FILL_HORIZONTAL); | |
187 | data.heightHint= 2; | |
188 | separator.setLayoutData(data); | |
189 | ||
190 | try { | |
191 | IPackageFragmentRoot root= getPackageFragmentRoot(fFile); | |
192 | if (root != null) { | |
193 | createSourceAttachmentControls(fComposite, root); | |
194 | } | |
195 | } catch (JavaModelException e) { | |
196 | String title= JavaEditorMessages.SourceAttachmentForm_error_title; | |
197 | String message= JavaEditorMessages.SourceAttachmentForm_error_message; | |
198 | ExceptionHandler.handle(e, fComposite.getShell(), title, message); | |
199 | } | |
200 | ||
201 | separator= createCompositeSeparator(fComposite); | |
202 | data= new GridData(GridData.FILL_HORIZONTAL); | |
203 | data.heightHint= 2; | |
204 | separator.setLayoutData(data); | |
205 | ||
206 | fNoSourceTextWidget= createCodeView(fComposite); | |
207 | data= new GridData(GridData.FILL_BOTH); | |
208 | fNoSourceTextWidget.setLayoutData(data); | |
209 | ||
210 | updateCodeView(fNoSourceTextWidget, fFile); | |
211 | ||
212 | return fComposite; | |
213 | } | |
214 | ||
215 | private void createSourceAttachmentControls(Composite composite, IPackageFragmentRoot root) throws JavaModelException { | |
216 | IClasspathEntry entry; | |
217 | try { | |
218 | entry= JavaModelUtil.getClasspathEntry(root); | |
219 | } catch (JavaModelException ex) { | |
220 | if (ex.isDoesNotExist()) | |
221 | entry= null; | |
222 | else | |
223 | throw ex; | |
224 | } | |
225 | IPath containerPath= null; | |
226 | ||
227 | if (entry == null || root.getKind() != IPackageFragmentRoot.K_BINARY) { | |
228 | createLabel(composite, Messages.format(JavaEditorMessages.SourceAttachmentForm_message_noSource, BasicElementLabels.getFileName( fFile))); | |
229 | return; | |
230 | } | |
231 | ||
232 | IJavaProject jproject= root.getJavaProject(); | |
233 | boolean canEditEncoding= true; | |
234 | if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { | |
235 | containerPath= entry.getPath(); | |
236 | ClasspathContainerInitializer initializer= JavaCore.getClasspathContainerInitializer(containerPath.segment(0)); | |
237 | IClasspathContainer container= JavaCore.getClasspathContainer(containerPath, jproject); | |
238 | if (initializer == null || container == null) { | |
239 | createLabel(composite, Messages.format(JavaEditorMessages.ClassFileEditor_SourceAttachmentForm_cannotconfigure, BasicElementLabels.getPathLabel(containerPath, false))); | |
240 | return; | |
241 | } | |
242 | String containerName= container.getDescription(); | |
243 | IStatus status= initializer.getSourceAttachmentStatus(containerPath, jproject); | |
244 | if (status.getCode() == ClasspathContainerInitializer.ATTRIBUTE_NOT_SUPPORTED) { | |
245 | createLabel(composite, Messages.format(JavaEditorMessages.ClassFileEditor_SourceAttachmentForm_notsupported, containerName)); | |
246 | return; | |
247 | } | |
248 | if (status.getCode() == ClasspathContainerInitializer.ATTRIBUTE_READ_ONLY) { | |
249 | createLabel(composite, Messages.format(JavaEditorMessages.ClassFileEditor_SourceAttachmentForm_readonly, containerName)); | |
250 | return; | |
251 | } | |
252 | IStatus attributeStatus= initializer.getAttributeStatus(containerPath, jproject, IClasspathAttribute.SOURCE_ATTACHMENT_ENCODING); | |
253 | canEditEncoding= !(attributeStatus.getCode() == ClasspathContainerInitializer.ATTRIBUTE_NOT_SUPPORTED || attributeStatus.getCode() == ClasspathContainerInitializer.ATTRIBUTE_READ_ONLY); | |
254 | entry= JavaModelUtil.findEntryInContainer(container, root.getPath()); | |
255 | Assert.isNotNull(entry); | |
256 | } | |
257 | ||
258 | ||
259 | Button button; | |
260 | ||
261 | IPath path= entry.getSourceAttachmentPath(); | |
262 | if (path == null || path.isEmpty()) { | |
263 | String rootLabel= JavaElementLabels.getElementLabel(root, JavaElementLabels.ALL_DEFAULT); | |
264 | createLabel(composite, Messages.format(JavaEditorMessages.SourceAttachmentForm_message_noSourceAttachment, rootLabel)); | |
265 | createLabel(composite, JavaEditorMessages.SourceAttachmentForm_message_pressButtonToAttach); | |
266 | createLabel(composite, null); | |
267 | ||
268 | button= createButton(composite, JavaEditorMessages.SourceAttachmentForm_button_attachSource); | |
269 | ||
270 | } else { | |
271 | createLabel(composite, Messages.format(JavaEditorMessages.SourceAttachmentForm_message_noSourceInAttachment, BasicElementLabels.getFileName(fFile))); | |
272 | createLabel(composite, JavaEditorMessages.SourceAttachmentForm_message_pressButtonToChange); | |
273 | createLabel(composite, null); | |
274 | ||
275 | button= createButton(composite, JavaEditorMessages.SourceAttachmentForm_button_changeAttachedSource); | |
276 | } | |
277 | ||
278 | button.addSelectionListener(getButtonListener(entry, containerPath, jproject, canEditEncoding)); | |
279 | } | |
280 | ||
281 | private SelectionListener getButtonListener(final IClasspathEntry entry, final IPath containerPath, final IJavaProject jproject, final boolean canEditEncoding) { | |
282 | return new SelectionListener() { | |
283 | public void widgetSelected(SelectionEvent event) { | |
284 | Shell shell= getSite().getShell(); | |
285 | try { | |
286 | IClasspathEntry result= BuildPathDialogAccess.configureSourceAttachment(shell, entry, canEditEncoding); | |
287 | if (result != null) { | |
288 | applySourceAttachment(shell, result, jproject, containerPath, entry.getReferencingEntry() != null); | |
289 | verifyInput(getEditorInput()); | |
290 | } | |
291 | } catch (CoreException e) { | |
292 | String title= JavaEditorMessages.SourceAttachmentForm_error_title; | |
293 | String message= JavaEditorMessages.SourceAttachmentForm_error_message; | |
294 | ExceptionHandler.handle(e, shell, title, message); | |
295 | } | |
296 | } | |
297 | ||
298 | public void widgetDefaultSelected(SelectionEvent e) {} | |
299 | }; | |
300 | } | |
301 | ||
302 | protected void applySourceAttachment(Shell shell, IClasspathEntry newEntry, IJavaProject project, IPath containerPath, boolean isReferencedEntry) { | |
303 | try { | |
304 | IRunnableWithProgress runnable= SourceAttachmentBlock.getRunnable(shell, newEntry, project, containerPath, isReferencedEntry); | |
305 | PlatformUI.getWorkbench().getProgressService().run(true, true, runnable); | |
306 | ||
307 | } catch (InvocationTargetException e) { | |
308 | String title= JavaEditorMessages.SourceAttachmentForm_attach_error_title; | |
309 | String message= JavaEditorMessages.SourceAttachmentForm_attach_error_message; | |
310 | ExceptionHandler.handle(e, shell, title, message); | |
311 | ||
312 | } catch (InterruptedException e) { | |
313 | // cancelled | |
314 | } | |
315 | } | |
316 | ||
317 | /* | |
318 | * @see IPropertyChangeListener#propertyChange(PropertyChangeEvent) | |
319 | */ | |
320 | public void propertyChange(PropertyChangeEvent event) { | |
321 | ||
322 | for (Iterator<Label> iterator = fBannerLabels.iterator(); iterator.hasNext();) { | |
323 | Label label = iterator.next(); | |
324 | label.setFont(JFaceResources.getBannerFont()); | |
325 | } | |
326 | ||
327 | for (Iterator<Label> iterator = fHeaderLabels.iterator(); iterator.hasNext();) { | |
328 | Label label = iterator.next(); | |
329 | label.setFont(JFaceResources.getHeaderFont()); | |
330 | } | |
331 | ||
332 | fComposite.layout(true); | |
333 | fComposite.redraw(); | |
334 | } | |
335 | ||
336 | // --- copied from org.eclipse.update.ui.forms.internal.FormWidgetFactory | |
337 | ||
338 | private Composite createComposite(Composite parent) { | |
339 | Composite composite = new Composite(parent, SWT.NONE); | |
340 | composite.setBackground(fBackgroundColor); | |
341 | // composite.addMouseListener(new MouseAdapter() { | |
342 | // public void mousePressed(MouseEvent e) { | |
343 | // ((Control) e.widget).setFocus(); | |
344 | // } | |
345 | // }); | |
346 | return composite; | |
347 | } | |
348 | ||
349 | private Composite createCompositeSeparator(Composite parent) { | |
350 | Composite composite = new Composite(parent, SWT.NONE); | |
351 | composite.setBackground(fSeparatorColor); | |
352 | return composite; | |
353 | } | |
354 | ||
355 | private StyledText createCodeView(Composite parent) { | |
356 | int styles= SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.FULL_SELECTION; | |
357 | StyledText styledText= new StyledText(parent, styles); | |
358 | styledText.setBackground(fBackgroundColor); | |
359 | styledText.setForeground(fForegroundColor); | |
360 | styledText.setEditable(false); | |
361 | return styledText; | |
362 | } | |
363 | ||
364 | private Label createLabel(Composite parent, String text) { | |
365 | Label label= new Label(parent, SWT.WRAP); | |
366 | if (text != null) | |
367 | label.setText(text); | |
368 | label.setBackground(fBackgroundColor); | |
369 | label.setForeground(fForegroundColor); | |
370 | GridData gd= new GridData(SWT.FILL, SWT.FILL, true, false); | |
371 | label.setLayoutData(gd); | |
372 | return label; | |
373 | } | |
374 | ||
375 | private Label createTitleLabel(Composite parent, String text) { | |
376 | Label label = new Label(parent, SWT.NONE); | |
377 | if (text != null) | |
378 | label.setText(text); | |
379 | label.setBackground(fBackgroundColor); | |
380 | label.setForeground(fForegroundColor); | |
381 | label.setFont(JFaceResources.getHeaderFont()); | |
382 | fHeaderLabels.add(label); | |
383 | return label; | |
384 | } | |
385 | ||
386 | private Label createHeadingLabel(Composite parent, String text) { | |
387 | Label label = new Label(parent, SWT.NONE); | |
388 | if (text != null) | |
389 | label.setText(text); | |
390 | label.setBackground(fBackgroundColor); | |
391 | label.setForeground(fForegroundColor); | |
392 | label.setFont(JFaceResources.getBannerFont()); | |
393 | fBannerLabels.add(label); | |
394 | return label; | |
395 | } | |
396 | ||
397 | private Button createButton(Composite parent, String text) { | |
398 | Button button = new Button(parent, SWT.FLAT); | |
399 | button.setBackground(fBackgroundColor); | |
400 | button.setForeground(fForegroundColor); | |
401 | if (text != null) | |
402 | button.setText(text); | |
403 | return button; | |
404 | } | |
405 | ||
406 | private void updateCodeView(StyledText styledText, IClassFile classFile) { | |
407 | String content= null; | |
408 | ClassFileBytesDisassembler disassembler= ToolFactory.createDefaultClassFileBytesDisassembler(); | |
409 | try { | |
410 | content= disassembler.disassemble(classFile.getBytes(), "\n", ClassFileBytesDisassembler.DETAILED); //$NON-NLS-1$ | |
411 | } catch (JavaModelException ex) { | |
412 | JavaPlugin.log(ex.getStatus()); | |
413 | } catch (ClassFormatException ex) { | |
414 | JavaPlugin.log(ex); | |
415 | } | |
416 | styledText.setText(content == null ? "" : content); //$NON-NLS-1$ | |
417 | } | |
418 | } | |
419 | ||
420 | /** | |
421 | * Updater that takes care of minimizing changes of the editor input. | |
422 | */ | |
423 | private class InputUpdater implements Runnable { | |
424 | ||
425 | /** Has the runnable already been posted? */ | |
426 | private boolean fPosted= false; | |
427 | /** Editor input */ | |
428 | private IClassFileEditorInput fClassFileEditorInput; | |
429 | ||
430 | ||
431 | public InputUpdater() { | |
432 | } | |
433 | ||
434 | /* | |
435 | * @see Runnable#run() | |
436 | */ | |
437 | public void run() { | |
438 | ||
439 | IClassFileEditorInput input; | |
440 | synchronized (this) { | |
441 | input= fClassFileEditorInput; | |
442 | } | |
443 | ||
444 | try { | |
445 | ||
446 | if (getSourceViewer() != null) | |
447 | setInput(input); | |
448 | ||
449 | } finally { | |
450 | synchronized (this) { | |
451 | fPosted= false; | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | /** | |
457 | * Posts this runnable into the event queue if not already there. | |
458 | * | |
459 | * @param input the input to be set when executed | |
460 | */ | |
461 | public void post(IClassFileEditorInput input) { | |
462 | ||
463 | synchronized(this) { | |
464 | if (fPosted) { | |
465 | if (isEqualInput(input, fClassFileEditorInput)) | |
466 | fClassFileEditorInput= input; | |
467 | return; | |
468 | } | |
469 | } | |
470 | ||
471 | if (isEqualInput(input, getEditorInput())) { | |
472 | ISourceViewer viewer= getSourceViewer(); | |
473 | if (viewer != null) { | |
474 | StyledText textWidget= viewer.getTextWidget(); | |
475 | if (textWidget != null && !textWidget.isDisposed()) { | |
476 | synchronized (this) { | |
477 | fPosted= true; | |
478 | fClassFileEditorInput= input; | |
479 | } | |
480 | textWidget.getDisplay().asyncExec(this); | |
481 | } | |
482 | } | |
483 | } | |
484 | } | |
485 | ||
486 | private boolean isEqualInput(IEditorInput input1, IEditorInput input2) { | |
487 | return input1 != null && input1.equals(input2); | |
488 | } | |
489 | } | |
490 | ||
491 | ||
492 | private StackLayout fStackLayout; | |
493 | private Composite fParent; | |
494 | ||
495 | private Composite fViewerComposite; | |
496 | private Control fSourceAttachmentForm; | |
497 | ||
498 | private CompositeActionGroup fContextMenuGroup; | |
499 | ||
500 | private InputUpdater fInputUpdater= new InputUpdater(); | |
501 | ||
502 | /** | |
503 | * The copy action used when there's attached source. | |
504 | * @since 3.3 | |
505 | */ | |
506 | private IAction fSourceCopyAction; | |
507 | /** | |
508 | * The Select All action used when there's attached source. | |
509 | * @since 3.3 | |
510 | */ | |
511 | private IAction fSelectAllAction; | |
512 | ||
513 | /** | |
514 | * StyledText widget used to show the disassembled code. | |
515 | * if there's no source. | |
516 | * | |
517 | * @since 3.3 | |
518 | */ | |
519 | private StyledText fNoSourceTextWidget; | |
520 | ||
521 | ||
522 | /** | |
523 | * Default constructor. | |
524 | */ | |
525 | public ClassFileEditor() { | |
526 | setDocumentProvider(JavaPlugin.getDefault().getClassFileDocumentProvider()); | |
527 | setEditorContextMenuId("#ClassFileEditorContext"); //$NON-NLS-1$ | |
528 | setRulerContextMenuId("#ClassFileRulerContext"); //$NON-NLS-1$ | |
529 | setOutlinerContextMenuId("#ClassFileOutlinerContext"); //$NON-NLS-1$ | |
530 | // don't set help contextId, we install our own help context | |
531 | } | |
532 | ||
533 | /* | |
534 | * @see AbstractTextEditor#createActions() | |
535 | */ | |
536 | @Override | |
537 | protected void createActions() { | |
538 | super.createActions(); | |
539 | ||
540 | setAction(ITextEditorActionConstants.SAVE, null); | |
541 | setAction(ITextEditorActionConstants.REVERT_TO_SAVED, null); | |
542 | ||
543 | fSourceCopyAction= getAction(ITextEditorActionConstants.COPY); | |
544 | fSelectAllAction= getAction(ITextEditorActionConstants.SELECT_ALL); | |
545 | ||
546 | final ActionGroup group= new RefactorActionGroup(this, ITextEditorActionConstants.GROUP_EDIT, true); | |
547 | fActionGroups.addGroup(group); | |
548 | fContextMenuGroup= new CompositeActionGroup(new ActionGroup[] {group}); | |
549 | ||
550 | /* | |
551 | * 1GF82PL: ITPJUI:ALL - Need to be able to add bookmark to class file | |
552 | * | |
553 | * // replace default action with class file specific ones | |
554 | * | |
555 | * setAction(ITextEditorActionConstants.BOOKMARK, new AddClassFileMarkerAction("AddBookmark.", this, IMarker.BOOKMARK, true)); //$NON-NLS-1$ | |
556 | * setAction(ITextEditorActionConstants.ADD_TASK, new AddClassFileMarkerAction("AddTask.", this, IMarker.TASK, false)); //$NON-NLS-1$ | |
557 | * setAction(ITextEditorActionConstants.RULER_MANAGE_BOOKMARKS, new ClassFileMarkerRulerAction("ManageBookmarks.", getVerticalRuler(), this, IMarker.BOOKMARK, true)); //$NON-NLS-1$ | |
558 | * setAction(ITextEditorActionConstants.RULER_MANAGE_TASKS, new ClassFileMarkerRulerAction("ManageTasks.", getVerticalRuler(), this, IMarker.TASK, true)); //$NON-NLS-1$ | |
559 | */ | |
560 | } | |
561 | ||
562 | /* | |
563 | * @see AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager) | |
564 | */ | |
565 | @Override | |
566 | public void editorContextMenuAboutToShow(IMenuManager menu) { | |
567 | super.editorContextMenuAboutToShow(menu); | |
568 | ||
569 | ActionContext context= new ActionContext(getSelectionProvider().getSelection()); | |
570 | fContextMenuGroup.setContext(context); | |
571 | fContextMenuGroup.fillContextMenu(menu); | |
572 | fContextMenuGroup.setContext(null); | |
573 | } | |
574 | ||
575 | /* | |
576 | * @see JavaEditor#getElementAt(int) | |
577 | */ | |
578 | @Override | |
579 | protected IJavaElement getElementAt(int offset) { | |
580 | if (getEditorInput() instanceof IClassFileEditorInput) { | |
581 | try { | |
582 | IClassFileEditorInput input= (IClassFileEditorInput) getEditorInput(); | |
583 | return input.getClassFile().getElementAt(offset); | |
584 | } catch (JavaModelException x) { | |
585 | } | |
586 | } | |
587 | return null; | |
588 | } | |
589 | ||
590 | /* | |
591 | * @see JavaEditor#getCorrespondingElement(IJavaElement) | |
592 | */ | |
593 | @Override | |
594 | protected IJavaElement getCorrespondingElement(IJavaElement element) { | |
595 | if (getEditorInput() instanceof IClassFileEditorInput) { | |
596 | IClassFileEditorInput input= (IClassFileEditorInput) getEditorInput(); | |
597 | IJavaElement parent= element.getAncestor(IJavaElement.CLASS_FILE); | |
598 | if (input.getClassFile().equals(parent)) | |
599 | return element; | |
600 | } | |
601 | return null; | |
602 | } | |
603 | ||
604 | /* | |
605 | * 1GEPKT5: ITPJUI:Linux - Source in editor for external classes is editable | |
606 | * Removed methods isSaveOnClosedNeeded and isDirty. | |
607 | * Added method isEditable. | |
608 | */ | |
609 | /* | |
610 | * @see org.eclipse.ui.texteditor.AbstractTextEditor#isEditable() | |
611 | */ | |
612 | @Override | |
613 | public boolean isEditable() { | |
614 | return false; | |
615 | } | |
616 | ||
617 | /* | |
618 | * @see org.eclipse.ui.texteditor.AbstractTextEditor#isEditorInputReadOnly() | |
619 | * @since 3.2 | |
620 | */ | |
621 | @Override | |
622 | public boolean isEditorInputReadOnly() { | |
623 | return true; | |
624 | } | |
625 | ||
626 | /** | |
627 | * Translates the given editor input into an <code>ExternalClassFileEditorInput</code> | |
628 | * if it is a file editor input representing an external class file. | |
629 | * | |
630 | * @param input the editor input to be transformed if necessary | |
631 | * @return the transformed editor input | |
632 | */ | |
633 | protected IEditorInput transformEditorInput(IEditorInput input) { | |
634 | ||
635 | if (input instanceof IFileEditorInput) { | |
636 | IFile file= ((IFileEditorInput) input).getFile(); | |
637 | IClassFileEditorInput classFileInput= new ExternalClassFileEditorInput(file); | |
638 | if (classFileInput.getClassFile() != null) | |
639 | input= classFileInput; | |
640 | } | |
641 | ||
642 | return input; | |
643 | } | |
644 | ||
645 | /* | |
646 | * @see AbstractTextEditor#doSetInput(IEditorInput) | |
647 | */ | |
648 | @Override | |
649 | protected void doSetInput(IEditorInput input) throws CoreException { | |
650 | uninstallOccurrencesFinder(); | |
651 | ||
652 | input= transformEditorInput(input); | |
653 | if (!(input instanceof IClassFileEditorInput)) { | |
654 | String inputClassName= input != null ? input.getClass().getName() : "null"; //$NON-NLS-1$ | |
655 | String message= Messages.format(JavaEditorMessages.ClassFileEditor_error_invalid_input_message, inputClassName); | |
656 | throw new CoreException(JavaUIStatus.createError( | |
657 | IJavaModelStatusConstants.INVALID_RESOURCE_TYPE, | |
658 | message, | |
659 | null)); | |
660 | } | |
661 | ||
662 | JavaModelException e= probeInputForSource(input); | |
663 | if (e != null) { | |
664 | IClassFileEditorInput classFileEditorInput= (IClassFileEditorInput) input; | |
665 | IClassFile file= classFileEditorInput.getClassFile(); | |
666 | IJavaProject javaProject= file.getJavaProject(); | |
667 | if (!javaProject.exists() || !javaProject.isOnClasspath(file)) { | |
668 | throw new CoreException(JavaUIStatus.createError( | |
669 | IJavaModelStatusConstants.INVALID_RESOURCE, | |
670 | JavaEditorMessages.ClassFileEditor_error_classfile_not_on_classpath, | |
671 | null)); | |
672 | } else { | |
673 | throw e; | |
674 | } | |
675 | } | |
676 | ||
677 | IDocumentProvider documentProvider= getDocumentProvider(); | |
678 | if (documentProvider instanceof ClassFileDocumentProvider) | |
679 | ((ClassFileDocumentProvider) documentProvider).removeInputChangeListener(this); | |
680 | ||
681 | super.doSetInput(input); | |
682 | ||
683 | documentProvider= getDocumentProvider(); | |
684 | if (documentProvider instanceof ClassFileDocumentProvider) | |
685 | ((ClassFileDocumentProvider) documentProvider).addInputChangeListener(this); | |
686 | ||
687 | verifyInput(getEditorInput()); | |
688 | ||
689 | JavaPlugin.getDefault().getASTProvider().activeJavaEditorChanged(this); | |
690 | ||
691 | if (fSemanticManager != null) | |
692 | installSemanticHighlighting(); | |
693 | ||
694 | } | |
695 | ||
696 | /* | |
697 | * @see org.eclipse.jdt.internal.ui.javaeditor.JavaEditor#installSemanticHighlighting() | |
698 | * @since 3.7 | |
699 | */ | |
700 | @Override | |
701 | protected void installSemanticHighlighting() { | |
702 | super.installSemanticHighlighting(); | |
703 | Job job= new Job(JavaEditorMessages.OverrideIndicatorManager_intallJob) { | |
704 | /* | |
705 | * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) | |
706 | * @since 3.0 | |
707 | */ | |
708 | @Override | |
709 | protected IStatus run(IProgressMonitor monitor) { | |
710 | CompilationUnit ast= SharedASTProvider.getAST(getInputJavaElement(), SharedASTProvider.WAIT_YES, null); | |
711 | if (fOverrideIndicatorManager != null) | |
712 | fOverrideIndicatorManager.reconciled(ast, true, monitor); | |
713 | if (fSemanticManager != null) { | |
714 | SemanticHighlightingReconciler reconciler= fSemanticManager.getReconciler(); | |
715 | if (reconciler != null) | |
716 | reconciler.reconciled(ast, false, monitor); | |
717 | } | |
718 | if (isMarkingOccurrences()) | |
719 | installOccurrencesFinder(false); | |
720 | return Status.OK_STATUS; | |
721 | } | |
722 | }; | |
723 | job.setPriority(Job.DECORATE); | |
724 | job.setSystem(true); | |
725 | job.schedule(); | |
726 | } | |
727 | ||
728 | /* | |
729 | * @see IWorkbenchPart#createPartControl(Composite) | |
730 | */ | |
731 | @Override | |
732 | public void createPartControl(Composite parent) { | |
733 | ||
734 | fParent= new Composite(parent, SWT.NONE); | |
735 | fStackLayout= new StackLayout(); | |
736 | fParent.setLayout(fStackLayout); | |
737 | ||
738 | fViewerComposite= new Composite(fParent, SWT.NONE); | |
739 | fViewerComposite.setLayout(new FillLayout()); | |
740 | ||
741 | super.createPartControl(fViewerComposite); | |
742 | ||
743 | fStackLayout.topControl= fViewerComposite; | |
744 | fParent.layout(); | |
745 | ||
746 | try { | |
747 | verifyInput(getEditorInput()); | |
748 | } catch (CoreException e) { | |
749 | String title= JavaEditorMessages.ClassFileEditor_error_title; | |
750 | String message= JavaEditorMessages.ClassFileEditor_error_message; | |
751 | ExceptionHandler.handle(e, fParent.getShell(), title, message); | |
752 | } | |
753 | } | |
754 | ||
755 | private JavaModelException probeInputForSource(IEditorInput input) { | |
756 | if (input == null) | |
757 | return null; | |
758 | ||
759 | IClassFileEditorInput classFileEditorInput= (IClassFileEditorInput) input; | |
760 | IClassFile file= classFileEditorInput.getClassFile(); | |
761 | ||
762 | try { | |
763 | file.getSourceRange(); | |
764 | } catch (JavaModelException e) { | |
765 | return e; | |
766 | } | |
767 | ||
768 | return null; | |
769 | } | |
770 | ||
771 | /** | |
772 | * Checks if the class file input has no source attached. If so, a source attachment form is shown. | |
773 | * | |
774 | * @param input the editor input | |
775 | * @throws JavaModelException if an exception occurs while accessing its corresponding resource | |
776 | */ | |
777 | private void verifyInput(IEditorInput input) throws JavaModelException { | |
778 | ||
779 | if (fParent == null || input == null) | |
780 | return; | |
781 | ||
782 | IClassFileEditorInput classFileEditorInput= (IClassFileEditorInput) input; | |
783 | IClassFile file= classFileEditorInput.getClassFile(); | |
784 | ||
785 | IAction copyQualifiedName= getAction(IJavaEditorActionConstants.COPY_QUALIFIED_NAME); | |
786 | ||
787 | boolean wasUsingSourceCopyAction= fSourceCopyAction == getAction(ITextEditorActionConstants.COPY); | |
788 | ||
789 | // show source attachment form if no source found | |
790 | if (file.getSourceRange() == null) { | |
791 | ||
792 | // dispose old source attachment form | |
793 | if (fSourceAttachmentForm != null) | |
794 | fSourceAttachmentForm.dispose(); | |
795 | ||
796 | SourceAttachmentForm form= new SourceAttachmentForm(file); | |
797 | fSourceAttachmentForm= form.createControl(fParent); | |
798 | ||
799 | fStackLayout.topControl= fSourceAttachmentForm; | |
800 | fParent.layout(); | |
801 | ||
802 | if (fNoSourceTextWidget != null) { | |
803 | // Copy action for the no attached source case | |
804 | final IAction copyAction= new Action() { | |
805 | @Override | |
806 | public void run() { | |
807 | fNoSourceTextWidget.copy(); | |
808 | } | |
809 | }; | |
810 | copyAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY); | |
811 | setAction(ITextEditorActionConstants.COPY, copyAction); | |
812 | copyAction.setEnabled(fNoSourceTextWidget.getSelectionText().length() > 0); | |
813 | fNoSourceTextWidget.addSelectionListener(new SelectionListener() { | |
814 | public void widgetSelected(SelectionEvent e) { | |
815 | copyAction.setEnabled(fNoSourceTextWidget.getSelectionText().length() > 0); | |
816 | } | |
817 | public void widgetDefaultSelected(SelectionEvent e) { | |
818 | } | |
819 | }); | |
820 | ||
821 | // Select All action for the no attached source case | |
822 | final IAction selectAllAction= new Action() { | |
823 | @Override | |
824 | public void run() { | |
825 | fNoSourceTextWidget.selectAll(); | |
826 | copyAction.setEnabled(true); | |
827 | } | |
828 | }; | |
829 | selectAllAction.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_SELECT_ALL); | |
830 | setAction(ITextEditorActionConstants.SELECT_ALL, selectAllAction); | |
831 | copyAction.setEnabled(fNoSourceTextWidget.getSelectionText().length() > 0); | |
832 | copyQualifiedName.setEnabled(false); | |
833 | ||
834 | ||
835 | } | |
836 | ||
837 | } else { // show source viewer | |
838 | ||
839 | if (fSourceAttachmentForm != null) { | |
840 | fSourceAttachmentForm.dispose(); | |
841 | fSourceAttachmentForm= null; | |
842 | ||
843 | fStackLayout.topControl= fViewerComposite; | |
844 | fParent.layout(); | |
845 | } | |
846 | ||
847 | setAction(ITextEditorActionConstants.COPY, fSourceCopyAction); | |
848 | setAction(ITextEditorActionConstants.SELECT_ALL, fSelectAllAction); | |
849 | copyQualifiedName.setEnabled(true); | |
850 | ||
851 | } | |
852 | ||
853 | IAction currentCopyAction= getAction(ITextEditorActionConstants.COPY); | |
854 | boolean isUsingSourceCopyAction= fSourceCopyAction == currentCopyAction; | |
855 | if (wasUsingSourceCopyAction != isUsingSourceCopyAction) { | |
856 | IActionBars actionBars= getEditorSite().getActionBars(); | |
857 | ||
858 | if (isUsingSourceCopyAction) { | |
859 | createNavigationActions(); | |
860 | } else { | |
861 | for (int i= 0; i < ACTION_MAP.length; i++) { | |
862 | IdMapEntry entry= ACTION_MAP[i]; | |
863 | actionBars.setGlobalActionHandler(entry.getActionId(), null); | |
864 | setAction(entry.getActionId(), null); | |
865 | } | |
866 | } | |
867 | ||
868 | actionBars.setGlobalActionHandler(ITextEditorActionConstants.COPY, currentCopyAction); | |
869 | actionBars.setGlobalActionHandler(ITextEditorActionConstants.SELECT_ALL, getAction(ITextEditorActionConstants.SELECT_ALL)); | |
870 | actionBars.updateActionBars(); | |
871 | } | |
872 | ||
873 | } | |
874 | ||
875 | /* | |
876 | * @see ClassFileDocumentProvider.InputChangeListener#inputChanged(IClassFileEditorInput) | |
877 | */ | |
878 | public void inputChanged(IClassFileEditorInput input) { | |
879 | fInputUpdater.post(input); | |
880 | } | |
881 | ||
882 | /* | |
883 | * @see JavaEditor#createJavaSourceViewer(Composite, IVerticalRuler, int) | |
884 | */ | |
885 | protected ISourceViewer createJavaSourceViewer(Composite parent, IVerticalRuler ruler, int styles, IPreferenceStore store) { | |
886 | return new JavaSourceViewer(parent, ruler, null, false, styles, store) { | |
887 | ||
888 | @Override | |
889 | public boolean requestWidgetToken(IWidgetTokenKeeper requester) { | |
890 | if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) | |
891 | return false; | |
892 | return super.requestWidgetToken(requester); | |
893 | } | |
894 | ||
895 | @Override | |
896 | public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) { | |
897 | if (PlatformUI.getWorkbench().getHelpSystem().isContextHelpDisplayed()) | |
898 | return false; | |
899 | return super.requestWidgetToken(requester, priority); | |
900 | } | |
901 | }; | |
902 | } | |
903 | ||
904 | /* | |
905 | * @see org.eclipse.ui.IWorkbenchPart#dispose() | |
906 | */ | |
907 | @Override | |
908 | public void dispose() { | |
909 | // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18510 | |
910 | IDocumentProvider documentProvider= getDocumentProvider(); | |
911 | if (documentProvider instanceof ClassFileDocumentProvider) | |
912 | ((ClassFileDocumentProvider) documentProvider).removeInputChangeListener(this); | |
913 | super.dispose(); | |
914 | } | |
915 | ||
916 | /* | |
917 | * @see org.eclipse.ui.IWorkbenchPart#setFocus() | |
918 | */ | |
919 | @Override | |
920 | public void setFocus() { | |
921 | super.setFocus(); | |
922 | ||
923 | if (fSourceAttachmentForm != null && !fSourceAttachmentForm.isDisposed()) | |
924 | fSourceAttachmentForm.setFocus(); | |
925 | } | |
926 | ||
927 | } |