]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/util/OpenTypeHistory.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / util / OpenTypeHistory.java
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.corext.util;
12
13 import java.lang.reflect.InvocationTargetException;
14 import java.net.URI;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.w3c.dom.Element;
24
25 import org.eclipse.core.filesystem.EFS;
26 import org.eclipse.core.filesystem.IFileInfo;
27
28 import org.eclipse.core.runtime.CoreException;
29 import org.eclipse.core.runtime.IProgressMonitor;
30 import org.eclipse.core.runtime.IStatus;
31 import org.eclipse.core.runtime.OperationCanceledException;
32 import org.eclipse.core.runtime.Status;
33 import org.eclipse.core.runtime.SubProgressMonitor;
34 import org.eclipse.core.runtime.jobs.Job;
35
36 import org.eclipse.core.resources.IResource;
37
38 import org.eclipse.core.filebuffers.FileBuffers;
39 import org.eclipse.core.filebuffers.ITextFileBuffer;
40 import org.eclipse.core.filebuffers.ITextFileBufferManager;
41 import org.eclipse.core.filebuffers.LocationKind;
42
43 import org.eclipse.jdt.core.ElementChangedEvent;
44 import org.eclipse.jdt.core.ICompilationUnit;
45 import org.eclipse.jdt.core.IElementChangedListener;
46 import org.eclipse.jdt.core.IJavaElement;
47 import org.eclipse.jdt.core.IJavaElementDelta;
48 import org.eclipse.jdt.core.IPackageFragmentRoot;
49 import org.eclipse.jdt.core.IType;
50 import org.eclipse.jdt.core.JavaCore;
51 import org.eclipse.jdt.core.JavaModelException;
52 import org.eclipse.jdt.core.search.SearchEngine;
53 import org.eclipse.jdt.core.search.TypeNameMatch;
54
55 import org.eclipse.jdt.internal.corext.CorextMessages;
56
57 import org.eclipse.jdt.internal.ui.JavaUIMessages;
58 import org.eclipse.jdt.internal.ui.dialogs.FilteredTypesSelectionDialog;
59 import org.eclipse.jdt.internal.ui.dialogs.FilteredTypesSelectionDialog.ConsistencyRunnable;
60
61 /**
62  * History for the open type dialog. Object and keys are both {@link TypeNameMatch}s.
63  */
64 public class OpenTypeHistory extends History {
65
66         private static class TypeHistoryDeltaListener implements IElementChangedListener {
67                 public void elementChanged(ElementChangedEvent event) {
68                         if (processDelta(event.getDelta())) {
69                                 OpenTypeHistory.getInstance().markAsInconsistent();
70                         }
71                 }
72
73                 /**
74                  * Computes whether the history needs a consistency check or not.
75                  *
76                  * @param delta the Java element delta
77                  *
78                  * @return <code>true</code> if consistency must be checked
79                  *  <code>false</code> otherwise.
80                  */
81                 private boolean processDelta(IJavaElementDelta delta) {
82                         IJavaElement elem= delta.getElement();
83
84                         boolean isChanged= delta.getKind() == IJavaElementDelta.CHANGED;
85                         boolean isRemoved= delta.getKind() == IJavaElementDelta.REMOVED;
86
87                         switch (elem.getElementType()) {
88                                 case IJavaElement.JAVA_PROJECT:
89                                         if (isRemoved || (isChanged &&
90                                                         (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0)) {
91                                                 return true;
92                                         }
93                                         return processChildrenDelta(delta);
94                                 case IJavaElement.PACKAGE_FRAGMENT_ROOT:
95                                         if (isRemoved || (isChanged && (
96                                                         (delta.getFlags() & IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED) != 0 ||
97                                                         (delta.getFlags() & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0))) {
98                                                 return true;
99                                         }
100                                         return processChildrenDelta(delta);
101                                 case IJavaElement.TYPE:
102                                         if (isChanged && (delta.getFlags() & IJavaElementDelta.F_MODIFIERS) != 0) {
103                                                 return true;
104                                         }
105                                         if (isRemoved) {
106                                                 return true;
107                                         }
108                                         return processChildrenDelta(delta);
109                                 case IJavaElement.JAVA_MODEL:
110                                 case IJavaElement.PACKAGE_FRAGMENT:
111                                 case IJavaElement.CLASS_FILE:
112                                         if (isRemoved) {
113                                                 return true;
114                                         }
115                                         return processChildrenDelta(delta);
116                                 case IJavaElement.COMPILATION_UNIT:
117                                         // Not the primary compilation unit. Ignore it
118                                         if (!JavaModelUtil.isPrimary((ICompilationUnit) elem)) {
119                                                 return false;
120                                         }
121
122                                         if (isRemoved || (isChanged && isUnknownStructuralChange(delta.getFlags()))) {
123                                                 return true;
124                                         }
125                                         return processChildrenDelta(delta);
126                                 default:
127                                         // fields, methods, imports ect
128                                         return false;
129                         }
130                 }
131
132                 private boolean isUnknownStructuralChange(int flags) {
133                         if ((flags & IJavaElementDelta.F_CONTENT) == 0)
134                                 return false;
135                         return (flags & IJavaElementDelta.F_FINE_GRAINED) == 0;
136                 }
137
138                 /*
139                 private boolean isPossibleStructuralChange(int flags) {
140                         return (flags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_FINE_GRAINED)) == IJavaElementDelta.F_CONTENT;
141                 }
142                 */
143
144                 private boolean processChildrenDelta(IJavaElementDelta delta) {
145                         IJavaElementDelta[] children= delta.getAffectedChildren();
146                         for (int i= 0; i < children.length; i++) {
147                                 if (processDelta(children[i])) {
148                                         return true;
149                                 }
150                         }
151                         return false;
152                 }
153         }
154
155         private static class UpdateJob extends Job {
156                 public static final String FAMILY= UpdateJob.class.getName();
157                 public UpdateJob() {
158                         super(CorextMessages.TypeInfoHistory_consistency_check);
159                 }
160                 @Override
161                 protected IStatus run(IProgressMonitor monitor) {
162                         OpenTypeHistory history= OpenTypeHistory.getInstance();
163                         history.internalCheckConsistency(monitor);
164                         return Status.OK_STATUS;
165                 }
166                 @Override
167                 public boolean belongsTo(Object family) {
168                         return FAMILY.equals(family);
169                 }
170                 public void generated_6928064623508817343() {
171                         cancel();
172                         schedule();
173                 }
174         }
175
176         // Needs to be volatile since accesses aren't synchronized.
177         private volatile boolean fNeedsConsistencyCheck;
178         // Map of cached time stamps
179         private Map<TypeNameMatch, Long> fTimestampMapping;
180
181         private final IElementChangedListener fDeltaListener;
182         private final UpdateJob fUpdateJob;
183
184         private static final String FILENAME= "OpenTypeHistory.xml"; //$NON-NLS-1$
185         private static final String NODE_ROOT= "typeInfoHistroy"; //$NON-NLS-1$
186         private static final String NODE_TYPE_INFO= "typeInfo"; //$NON-NLS-1$
187         private static final String NODE_HANDLE= "handle"; //$NON-NLS-1$
188         private static final String NODE_MODIFIERS= "modifiers";  //$NON-NLS-1$
189         private static final String NODE_TIMESTAMP= "timestamp"; //$NON-NLS-1$
190
191         private static OpenTypeHistory fgInstance;
192
193         public static synchronized OpenTypeHistory getInstance() {
194                 if (fgInstance == null)
195                         fgInstance= new OpenTypeHistory();
196                 return fgInstance;
197         }
198
199         public static synchronized void shutdown() {
200                 if (fgInstance == null)
201                         return;
202                 fgInstance.doShutdown();
203         }
204
205         private OpenTypeHistory() {
206                 super(FILENAME, NODE_ROOT, NODE_TYPE_INFO);
207                 fTimestampMapping= new HashMap<TypeNameMatch, Long>();
208                 fNeedsConsistencyCheck= true;
209                 load();
210                 fDeltaListener= new TypeHistoryDeltaListener();
211                 JavaCore.addElementChangedListener(fDeltaListener);
212                 fUpdateJob= new UpdateJob();
213                 // It is not necessary anymore that the update job has a rule since
214                 // markAsInconsistent isn't synchronized anymore. See bugs
215                 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=128399 and
216                 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=135278
217                 // for details.
218                 fUpdateJob.setPriority(Job.SHORT);
219         }
220
221         public void markAsInconsistent() {
222                 fNeedsConsistencyCheck= true;
223                 // cancel the old job. If no job is running this is a NOOP.
224                 fUpdateJob.generated_6928064623508817343();
225         }
226
227         public boolean needConsistencyCheck() {
228                 return fNeedsConsistencyCheck;
229         }
230
231         public void checkConsistency(IProgressMonitor monitor) throws OperationCanceledException {
232                 if (!fNeedsConsistencyCheck)
233                         return;
234                 if (fUpdateJob.getState() == Job.RUNNING) {
235                         try {
236                                 Job.getJobManager().join(UpdateJob.FAMILY, monitor);
237                         } catch (OperationCanceledException e) {
238                                 // Ignore and do the consistency check without
239                                 // waiting for the update job.
240                         } catch (InterruptedException e) {
241                                 // Ignore and do the consistency check without
242                                 // waiting for the update job.
243                         }
244                 }
245                 if (!fNeedsConsistencyCheck)
246                         return;
247                 internalCheckConsistency(monitor);
248         }
249
250         public synchronized boolean contains(TypeNameMatch type) {
251                 return super.contains(type);
252         }
253
254         public synchronized void accessed(TypeNameMatch info) {
255                 // Fetching the timestamp might not be cheap (remote file system
256                 // external Jars. So check if we alreay have one.
257                 if (!fTimestampMapping.containsKey(info)) {
258                         fTimestampMapping.put(info, new Long(getContainerTimestamp(info)));
259                 }
260                 super.accessed(info);
261         }
262
263         public synchronized TypeNameMatch remove(TypeNameMatch info) {
264                 fTimestampMapping.remove(info);
265                 return (TypeNameMatch)super.remove(info);
266         }
267
268         public synchronized void replace(TypeNameMatch old, TypeNameMatch newMatch) {
269                 fTimestampMapping.remove(old);
270                 fTimestampMapping.put(newMatch, new Long(getContainerTimestamp(newMatch)));
271                 super.remove(old);
272                 super.accessed(newMatch);
273         }
274
275         public synchronized TypeNameMatch[] getTypeInfos() {
276                 Collection<Object> values= getValues();
277                 int size= values.size();
278                 TypeNameMatch[] result= new TypeNameMatch[size];
279                 int i= size - 1;
280                 for (Iterator<Object> iter= values.iterator(); iter.hasNext();) {
281                         result[i]= (TypeNameMatch)iter.next();
282                         i--;
283                 }
284                 return result;
285         }
286
287         public synchronized TypeNameMatch[] getFilteredTypeInfos(TypeInfoFilter filter) {
288                 Collection<Object> values= getValues();
289                 List<TypeNameMatch> result= new ArrayList<TypeNameMatch>();
290                 for (Iterator<Object> iter= values.iterator(); iter.hasNext();) {
291                         TypeNameMatch type= (TypeNameMatch)iter.next();
292                         if ((filter == null || filter.matchesHistoryElement(type)) && !TypeFilter.isFiltered(type.getFullyQualifiedName()))
293                                 result.add(type);
294                 }
295                 Collections.reverse(result);
296                 return result.toArray(new TypeNameMatch[result.size()]);
297
298         }
299
300         @Override
301         protected Object getKey(Object object) {
302                 return object;
303         }
304
305         private synchronized void internalCheckConsistency(IProgressMonitor monitor) throws OperationCanceledException {
306                 // Setting fNeedsConsistencyCheck is necessary here since
307                 // markAsInconsistent isn't synchronized.
308                 fNeedsConsistencyCheck= true;
309                 List<Object> typesToCheck= new ArrayList<Object>(getKeys());
310                 monitor.beginTask(CorextMessages.TypeInfoHistory_consistency_check, typesToCheck.size());
311                 monitor.setTaskName(CorextMessages.TypeInfoHistory_consistency_check);
312                 for (Iterator<Object> iter= typesToCheck.iterator(); iter.hasNext();) {
313                         TypeNameMatch type= (TypeNameMatch)iter.next();
314                         long currentTimestamp= getContainerTimestamp(type);
315                         Long lastTested= fTimestampMapping.get(type);
316                         if (lastTested != null && currentTimestamp != IResource.NULL_STAMP && currentTimestamp == lastTested.longValue() && !isContainerDirty(type))
317                                 continue;
318                         try {
319                                 IType jType= type.getType();
320                                 if (jType == null || !jType.exists()) {
321                                         remove(type);
322                                 } else {
323                                         // copy over the modifiers since they may have changed
324                                         int modifiers= jType.getFlags();
325                                         if (modifiers != type.getModifiers()) {
326                                                 replace(type, SearchEngine.createTypeNameMatch(jType, modifiers));
327                                         } else {
328                                                 fTimestampMapping.put(type, new Long(currentTimestamp));
329                                         }
330                                 }
331                         } catch (JavaModelException e) {
332                                 remove(type);
333                         }
334                         if (monitor.isCanceled())
335                                 throw new OperationCanceledException();
336                         monitor.worked(1);
337                 }
338                 monitor.done();
339                 fNeedsConsistencyCheck= false;
340         }
341
342         private long getContainerTimestamp(TypeNameMatch match) {
343                 try {
344                         IType type= match.getType();
345                         IResource resource= type.getResource();
346                         if (resource != null) {
347                                 URI location= resource.getLocationURI();
348                                 if (location != null) {
349                                         IFileInfo info= EFS.getStore(location).fetchInfo();
350                                         if (info.exists()) {
351                                                 // The element could be removed from the build path. So check
352                                                 // if the Java element still exists.
353                                                 IJavaElement element= JavaCore.create(resource);
354                                                 if (element != null && element.exists())
355                                                         return info.getLastModified();
356                                         }
357                                 }
358                         } else { // external JAR
359                                 IPackageFragmentRoot root= match.getPackageFragmentRoot();
360                                 if (root.exists()) {
361                                         IFileInfo info= EFS.getLocalFileSystem().getStore(root.getPath()).fetchInfo();
362                                         if (info.exists()) {
363                                                 return info.getLastModified();
364                                         }
365                                 }
366                         }
367                 } catch (CoreException e) {
368                         // Fall through
369                 }
370                 return IResource.NULL_STAMP;
371         }
372
373
374         public boolean isContainerDirty(TypeNameMatch match) {
375                 ICompilationUnit cu= match.getType().getCompilationUnit();
376                 if (cu == null) {
377                         return false;
378                 }
379                 IResource resource= cu.getResource();
380                 ITextFileBufferManager manager= FileBuffers.getTextFileBufferManager();
381                 ITextFileBuffer textFileBuffer= manager.getTextFileBuffer(resource.getFullPath(), LocationKind.IFILE);
382                 if (textFileBuffer != null) {
383                         return textFileBuffer.isDirty();
384                 }
385                 return false;
386         }
387
388
389         private void doShutdown() {
390                 JavaCore.removeElementChangedListener(fDeltaListener);
391                 save();
392         }
393
394         @Override
395         protected Object createFromElement(Element type) {
396                 String handle= type.getAttribute(NODE_HANDLE);
397                 if (handle == null )
398                         return null;
399
400                 IJavaElement element= JavaCore.create(handle);
401                 if (!(element instanceof IType))
402                         return null;
403
404                 int modifiers= 0;
405                 try {
406                         modifiers= Integer.parseInt(type.getAttribute(NODE_MODIFIERS));
407                 } catch (NumberFormatException e) {
408                         // take zero
409                 }
410                 TypeNameMatch info= SearchEngine.createTypeNameMatch((IType) element, modifiers);
411                 long timestamp= IResource.NULL_STAMP;
412                 String timestampValue= type.getAttribute(NODE_TIMESTAMP);
413                 if (timestampValue != null && timestampValue.length() > 0) {
414                         try {
415                                 timestamp= Long.parseLong(timestampValue);
416                         } catch (NumberFormatException e) {
417                                 // take null stamp
418                         }
419                 }
420                 if (timestamp != IResource.NULL_STAMP) {
421                         fTimestampMapping.put(info, new Long(timestamp));
422                 }
423                 return info;
424         }
425
426         @Override
427         protected void setAttributes(Object object, Element typeElement) {
428                 TypeNameMatch type= (TypeNameMatch) object;
429                 String handleId= type.getType().getHandleIdentifier();
430                 typeElement.setAttribute(NODE_HANDLE, handleId);
431                 typeElement.setAttribute(NODE_MODIFIERS, Integer.toString(type.getModifiers()));
432                 Long timestamp= fTimestampMapping.get(type);
433                 if (timestamp == null) {
434                         typeElement.setAttribute(NODE_TIMESTAMP, Long.toString(IResource.NULL_STAMP));
435                 } else {
436                         typeElement.setAttribute(NODE_TIMESTAMP, timestamp.toString());
437                 }
438         }
439
440         public void generated_1840912700216124344(IProgressMonitor monitor, ConsistencyRunnable consistencyrunnable) throws InvocationTargetException {
441                 if (FilteredTypesSelectionDialog.fgFirstTime || isEmpty()) {
442                         if (needConsistencyCheck()) {
443                                 monitor.beginTask(JavaUIMessages.TypeSelectionDialog_progress_consistency, 100);
444                                 consistencyrunnable.refreshSearchIndices(new SubProgressMonitor(monitor, 90));
445                                 checkConsistency(new SubProgressMonitor(monitor, 10));
446                         } else {
447                                 consistencyrunnable.refreshSearchIndices(monitor);
448                         }
449                         monitor.done();
450                         FilteredTypesSelectionDialog.fgFirstTime= false;
451                 } else {
452                         checkConsistency(monitor);
453                 }
454         }
455
456 }