]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 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.util.ArrayList; | |
14 | import java.util.Iterator; | |
15 | import java.util.List; | |
16 | ||
17 | import org.eclipse.core.runtime.CoreException; | |
18 | ||
19 | import org.eclipse.core.resources.IResource; | |
20 | ||
21 | import org.eclipse.core.filebuffers.FileBuffers; | |
22 | import org.eclipse.core.filebuffers.LocationKind; | |
23 | ||
24 | import org.eclipse.jface.text.IDocument; | |
25 | import org.eclipse.jface.text.ISynchronizable; | |
26 | import org.eclipse.jface.text.source.IAnnotationModel; | |
27 | ||
28 | import org.eclipse.ui.IEditorInput; | |
29 | import org.eclipse.ui.IFileEditorInput; | |
30 | ||
31 | import org.eclipse.ui.editors.text.FileDocumentProvider; | |
32 | ||
33 | import org.eclipse.jdt.core.ElementChangedEvent; | |
34 | import org.eclipse.jdt.core.IClassFile; | |
35 | import org.eclipse.jdt.core.IElementChangedListener; | |
36 | import org.eclipse.jdt.core.IJavaElement; | |
37 | import org.eclipse.jdt.core.IJavaElementDelta; | |
38 | import org.eclipse.jdt.core.IJavaProject; | |
39 | import org.eclipse.jdt.core.IPackageFragmentRoot; | |
40 | import org.eclipse.jdt.core.IType; | |
41 | import org.eclipse.jdt.core.JavaCore; | |
42 | import org.eclipse.jdt.core.JavaModelException; | |
43 | ||
44 | import org.eclipse.jdt.ui.text.IJavaPartitions; | |
45 | import org.eclipse.jdt.ui.text.JavaTextTools; | |
46 | ||
47 | import org.eclipse.jdt.internal.ui.IResourceLocator; | |
48 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
49 | ||
50 | ||
51 | /** | |
52 | * A document provider for class files. Class files can be either inside | |
53 | */ | |
54 | public class ClassFileDocumentProvider extends FileDocumentProvider { | |
55 | ||
56 | /** | |
57 | * An input change listener to request the editor to reread the input. | |
58 | */ | |
59 | public interface InputChangeListener { | |
60 | void inputChanged(IClassFileEditorInput input); | |
61 | } | |
62 | ||
63 | /** | |
64 | * Synchronizes the document with external resource changes. | |
65 | */ | |
66 | protected class ClassFileSynchronizer implements IElementChangedListener { | |
67 | ||
68 | protected IClassFileEditorInput fInput; | |
69 | protected IPackageFragmentRoot fPackageFragmentRoot; | |
70 | ||
71 | /** | |
72 | * Default constructor. | |
73 | * | |
74 | * @param input the class file editor input | |
75 | */ | |
76 | public ClassFileSynchronizer(IClassFileEditorInput input) { | |
77 | ||
78 | fInput= input; | |
79 | ||
80 | IJavaElement parent= fInput.getClassFile().getParent(); | |
81 | while (parent != null && !(parent instanceof IPackageFragmentRoot)) { | |
82 | parent= parent.getParent(); | |
83 | } | |
84 | fPackageFragmentRoot= (IPackageFragmentRoot) parent; | |
85 | } | |
86 | ||
87 | /** | |
88 | * Installs the synchronizer. | |
89 | */ | |
90 | public void install() { | |
91 | JavaCore.addElementChangedListener(this); | |
92 | } | |
93 | ||
94 | /** | |
95 | * Uninstalls the synchronizer. | |
96 | */ | |
97 | public void uninstall() { | |
98 | JavaCore.removeElementChangedListener(this); | |
99 | } | |
100 | ||
101 | /* | |
102 | * @see IElementChangedListener#elementChanged | |
103 | */ | |
104 | public void elementChanged(ElementChangedEvent e) { | |
105 | check(fPackageFragmentRoot, e.getDelta()); | |
106 | } | |
107 | ||
108 | /** | |
109 | * Recursively check whether the class file has been deleted. | |
110 | * | |
111 | * @param input the package fragment root | |
112 | * @param delta the Java element delta | |
113 | * @return <code>true</code> if delta processing can be stopped | |
114 | */ | |
115 | protected boolean check(IPackageFragmentRoot input, IJavaElementDelta delta) { | |
116 | IJavaElement element= delta.getElement(); | |
117 | ||
118 | if ((delta.getKind() & IJavaElementDelta.REMOVED) != 0 || (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0) { | |
119 | // http://dev.eclipse.org/bugs/show_bug.cgi?id=19023 | |
120 | if (element.equals(input.getJavaProject()) || element.equals(input)) { | |
121 | handleDeleted(fInput); | |
122 | return true; | |
123 | } | |
124 | } | |
125 | ||
126 | if (((delta.getFlags() & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0) && input.equals(element)) { | |
127 | handleDeleted(fInput); | |
128 | return true; | |
129 | } | |
130 | ||
131 | if (((delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0) && input.equals(element)) { | |
132 | handleDeleted(fInput); | |
133 | return true; | |
134 | } | |
135 | ||
136 | IJavaElementDelta[] subdeltas= delta.getAffectedChildren(); | |
137 | for (int i= 0; i < subdeltas.length; i++) { | |
138 | if (check(input, subdeltas[i])) | |
139 | return true; | |
140 | } | |
141 | ||
142 | if ((delta.getFlags() & IJavaElementDelta.F_SOURCEDETACHED) != 0 || | |
143 | (delta.getFlags() & IJavaElementDelta.F_SOURCEATTACHED) != 0) | |
144 | { | |
145 | IClassFile file= fInput != null ? fInput.getClassFile() : null; | |
146 | IJavaProject project= input != null ? input.getJavaProject() : null; | |
147 | ||
148 | boolean isOnClasspath= false; | |
149 | if (file != null && project != null) | |
150 | isOnClasspath= project.isOnClasspath(file); | |
151 | ||
152 | if (isOnClasspath) { | |
153 | fireInputChanged(fInput); | |
154 | return false; | |
155 | } else { | |
156 | handleDeleted(fInput); | |
157 | return true; | |
158 | } | |
159 | } | |
160 | ||
161 | return false; | |
162 | } | |
163 | } | |
164 | ||
165 | /** | |
166 | * Correcting the visibility of <code>FileSynchronizer</code>. | |
167 | */ | |
168 | protected class _FileSynchronizer extends FileSynchronizer { | |
169 | public _FileSynchronizer(IFileEditorInput fileEditorInput) { | |
170 | super(fileEditorInput); | |
171 | } | |
172 | } | |
173 | ||
174 | /** | |
175 | * Bundle of all required informations. | |
176 | */ | |
177 | protected class ClassFileInfo extends FileInfo { | |
178 | ||
179 | ClassFileSynchronizer fClassFileSynchronizer= null; | |
180 | ||
181 | ClassFileInfo(IDocument document, IAnnotationModel model, _FileSynchronizer fileSynchronizer) { | |
182 | super(document, model, fileSynchronizer); | |
183 | } | |
184 | ||
185 | ClassFileInfo(IDocument document, IAnnotationModel model, ClassFileSynchronizer classFileSynchronizer) { | |
186 | super(document, model, null); | |
187 | fClassFileSynchronizer= classFileSynchronizer; | |
188 | } | |
189 | } | |
190 | ||
191 | /** Input change listeners. */ | |
192 | private List<InputChangeListener> fInputListeners= new ArrayList<InputChangeListener>(); | |
193 | ||
194 | /** | |
195 | * Creates a new document provider. | |
196 | */ | |
197 | public ClassFileDocumentProvider() { | |
198 | super(); | |
199 | } | |
200 | ||
201 | /* | |
202 | * @see StorageDocumentProvider#setDocumentContent(IDocument, IEditorInput) | |
203 | */ | |
204 | @Override | |
205 | protected boolean setDocumentContent(IDocument document, IEditorInput editorInput, String encoding) throws CoreException { | |
206 | if (editorInput instanceof IClassFileEditorInput) { | |
207 | IClassFile classFile= ((IClassFileEditorInput) editorInput).getClassFile(); | |
208 | String source= classFile.getSource(); | |
209 | if (source == null) | |
210 | source= ""; //$NON-NLS-1$ | |
211 | document.set(source); | |
212 | return true; | |
213 | } | |
214 | return super.setDocumentContent(document, editorInput, encoding); | |
215 | } | |
216 | ||
217 | /** | |
218 | * Creates an annotation model derived from the given class file editor input. | |
219 | * | |
220 | * @param classFileEditorInput the editor input from which to query the annotations | |
221 | * @return the created annotation model | |
222 | * @exception CoreException if the editor input could not be accessed | |
223 | */ | |
224 | protected IAnnotationModel createClassFileAnnotationModel(IClassFileEditorInput classFileEditorInput) throws CoreException { | |
225 | IResource resource= null; | |
226 | IClassFile classFile= classFileEditorInput.getClassFile(); | |
227 | ||
228 | IResourceLocator locator= (IResourceLocator) classFile.getAdapter(IResourceLocator.class); | |
229 | if (locator != null) | |
230 | resource= locator.getContainingResource(classFile); | |
231 | ||
232 | if (resource != null) { | |
233 | ClassFileMarkerAnnotationModel model= new ClassFileMarkerAnnotationModel(resource); | |
234 | model.setClassFile(classFile); | |
235 | return model; | |
236 | } | |
237 | ||
238 | return null; | |
239 | } | |
240 | ||
241 | /* | |
242 | * @see org.eclipse.ui.editors.text.StorageDocumentProvider#createEmptyDocument() | |
243 | * @since 3.1 | |
244 | */ | |
245 | @Override | |
246 | protected IDocument createEmptyDocument() { | |
247 | IDocument document= FileBuffers.getTextFileBufferManager().createEmptyDocument(null, LocationKind.IFILE); | |
248 | if (document instanceof ISynchronizable) | |
249 | ((ISynchronizable)document).setLockObject(new Object()); | |
250 | return document; | |
251 | } | |
252 | ||
253 | /* | |
254 | * @see AbstractDocumentProvider#createDocument(Object) | |
255 | */ | |
256 | @Override | |
257 | protected IDocument createDocument(Object element) throws CoreException { | |
258 | IDocument document= super.createDocument(element); | |
259 | if (document != null) { | |
260 | JavaTextTools tools= JavaPlugin.getDefault().getJavaTextTools(); | |
261 | tools.setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING); | |
262 | } | |
263 | return document; | |
264 | } | |
265 | ||
266 | /* | |
267 | * @see AbstractDocumentProvider#createElementInfo(Object) | |
268 | */ | |
269 | @Override | |
270 | protected ElementInfo createElementInfo(Object element) throws CoreException { | |
271 | ||
272 | if (element instanceof IClassFileEditorInput) { | |
273 | ||
274 | IClassFileEditorInput input = (IClassFileEditorInput) element; | |
275 | ExternalClassFileEditorInput external= null; | |
276 | if (input instanceof ExternalClassFileEditorInput) | |
277 | external= (ExternalClassFileEditorInput) input; | |
278 | ||
279 | if (external != null) { | |
280 | try { | |
281 | refreshFile(external.getFile()); | |
282 | } catch (CoreException x) { | |
283 | handleCoreException(x, JavaEditorMessages.ClassFileDocumentProvider_error_createElementInfo); | |
284 | } | |
285 | } | |
286 | ||
287 | IDocument d= createDocument(input); | |
288 | IAnnotationModel m= createClassFileAnnotationModel(input); | |
289 | ||
290 | if (external != null) { | |
291 | ClassFileInfo info= new ClassFileInfo(d, m, (_FileSynchronizer) null); | |
292 | info.fModificationStamp= computeModificationStamp(external.getFile()); | |
293 | info.fEncoding= getPersistedEncoding(element); | |
294 | return info; | |
295 | } else if (input instanceof InternalClassFileEditorInput) { | |
296 | ClassFileSynchronizer s= new ClassFileSynchronizer(input); | |
297 | s.install(); | |
298 | ClassFileInfo info= new ClassFileInfo(d, m, s); | |
299 | info.fEncoding= getPersistedEncoding(element); | |
300 | return info; | |
301 | } | |
302 | } | |
303 | ||
304 | return null; | |
305 | } | |
306 | ||
307 | /* | |
308 | * @see FileDocumentProvider#disposeElementInfo(Object, ElementInfo) | |
309 | */ | |
310 | @Override | |
311 | protected void disposeElementInfo(Object element, ElementInfo info) { | |
312 | ClassFileInfo classFileInfo= (ClassFileInfo) info; | |
313 | if (classFileInfo.fClassFileSynchronizer != null) { | |
314 | classFileInfo.fClassFileSynchronizer.uninstall(); | |
315 | classFileInfo.fClassFileSynchronizer= null; | |
316 | } | |
317 | ||
318 | super.disposeElementInfo(element, info); | |
319 | } | |
320 | ||
321 | /* | |
322 | * @see org.eclipse.ui.texteditor.IDocumentProviderExtension3#isSynchronized(java.lang.Object) | |
323 | * @since 3.0 | |
324 | */ | |
325 | @Override | |
326 | public boolean isSynchronized(Object element) { | |
327 | Object elementInfo= getElementInfo(element); | |
328 | if (elementInfo instanceof ClassFileInfo) { | |
329 | IClassFileEditorInput input= (IClassFileEditorInput)element; | |
330 | IResource resource; | |
331 | try { | |
332 | resource= input.getClassFile().getUnderlyingResource(); | |
333 | } catch (JavaModelException e) { | |
334 | return true; | |
335 | } | |
336 | return resource == null || resource.isSynchronized(IResource.DEPTH_ZERO); | |
337 | } | |
338 | return false; | |
339 | } | |
340 | ||
341 | /** | |
342 | * Handles the deletion of the element underlying the given class file editor input. | |
343 | * @param input the editor input | |
344 | */ | |
345 | protected void handleDeleted(IClassFileEditorInput input) { | |
346 | if (input == null) { | |
347 | fireElementDeleted(input); | |
348 | return; | |
349 | } | |
350 | ||
351 | if (input.exists()) | |
352 | return; | |
353 | ||
354 | IClassFile cf= input.getClassFile(); | |
355 | try { | |
356 | /* | |
357 | * Let's try to find the class file - maybe the JAR changed | |
358 | */ | |
359 | IType type= cf.getType(); | |
360 | IJavaProject project= cf.getJavaProject(); | |
361 | if (project != null) { | |
362 | type= project.findType(type.getFullyQualifiedName()); | |
363 | if (type != null) { | |
364 | IEditorInput editorInput= EditorUtility.getEditorInput(type.getParent()); | |
365 | if (editorInput instanceof IClassFileEditorInput) { | |
366 | fireInputChanged((IClassFileEditorInput)editorInput); | |
367 | return; | |
368 | } | |
369 | } | |
370 | } | |
371 | } catch (JavaModelException x) { | |
372 | // Don't log and fall through: element deleted | |
373 | } | |
374 | ||
375 | fireElementDeleted(input); | |
376 | ||
377 | } | |
378 | ||
379 | /** | |
380 | * Fires input changes to input change listeners. | |
381 | * | |
382 | * @param input the class file editor input | |
383 | */ | |
384 | protected void fireInputChanged(IClassFileEditorInput input) { | |
385 | List<InputChangeListener> list= new ArrayList<InputChangeListener>(fInputListeners); | |
386 | for (Iterator<InputChangeListener> i = list.iterator(); i.hasNext();) | |
387 | i.next().inputChanged(input); | |
388 | } | |
389 | ||
390 | /** | |
391 | * Adds an input change listener. | |
392 | * | |
393 | * @param listener the input change listener | |
394 | */ | |
395 | public void addInputChangeListener(InputChangeListener listener) { | |
396 | fInputListeners.add(listener); | |
397 | } | |
398 | ||
399 | /** | |
400 | * Removes an input change listener. | |
401 | * | |
402 | * @param listener the input change listener | |
403 | */ | |
404 | public void removeInputChangeListener(InputChangeListener listener) { | |
405 | fInputListeners.remove(listener); | |
406 | } | |
407 | ||
408 | } |