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