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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.util;
13 import java.lang.reflect.InvocationTargetException;
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;
23 import org.w3c.dom.Element;
25 import org.eclipse.core.filesystem.EFS;
26 import org.eclipse.core.filesystem.IFileInfo;
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;
36 import org.eclipse.core.resources.IResource;
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;
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;
55 import org.eclipse.jdt.internal.corext.CorextMessages;
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;
62 * History for the open type dialog. Object and keys are both {@link TypeNameMatch}s.
64 public class OpenTypeHistory extends History {
66 private static class TypeHistoryDeltaListener implements IElementChangedListener {
67 public void elementChanged(ElementChangedEvent event) {
68 if (processDelta(event.getDelta())) {
69 OpenTypeHistory.getInstance().markAsInconsistent();
74 * Computes whether the history needs a consistency check or not.
76 * @param delta the Java element delta
78 * @return <code>true</code> if consistency must be checked
79 * <code>false</code> otherwise.
81 private boolean processDelta(IJavaElementDelta delta) {
82 IJavaElement elem= delta.getElement();
84 boolean isChanged= delta.getKind() == IJavaElementDelta.CHANGED;
85 boolean isRemoved= delta.getKind() == IJavaElementDelta.REMOVED;
87 switch (elem.getElementType()) {
88 case IJavaElement.JAVA_PROJECT:
89 if (isRemoved || (isChanged &&
90 (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0)) {
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))) {
100 return processChildrenDelta(delta);
101 case IJavaElement.TYPE:
102 if (isChanged && (delta.getFlags() & IJavaElementDelta.F_MODIFIERS) != 0) {
108 return processChildrenDelta(delta);
109 case IJavaElement.JAVA_MODEL:
110 case IJavaElement.PACKAGE_FRAGMENT:
111 case IJavaElement.CLASS_FILE:
115 return processChildrenDelta(delta);
116 case IJavaElement.COMPILATION_UNIT:
117 // Not the primary compilation unit. Ignore it
118 if (!JavaModelUtil.isPrimary((ICompilationUnit) elem)) {
122 if (isRemoved || (isChanged && isUnknownStructuralChange(delta.getFlags()))) {
125 return processChildrenDelta(delta);
127 // fields, methods, imports ect
132 private boolean isUnknownStructuralChange(int flags) {
133 if ((flags & IJavaElementDelta.F_CONTENT) == 0)
135 return (flags & IJavaElementDelta.F_FINE_GRAINED) == 0;
139 private boolean isPossibleStructuralChange(int flags) {
140 return (flags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_FINE_GRAINED)) == IJavaElementDelta.F_CONTENT;
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])) {
155 private static class UpdateJob extends Job {
156 public static final String FAMILY= UpdateJob.class.getName();
158 super(CorextMessages.TypeInfoHistory_consistency_check);
161 protected IStatus run(IProgressMonitor monitor) {
162 OpenTypeHistory history= OpenTypeHistory.getInstance();
163 history.internalCheckConsistency(monitor);
164 return Status.OK_STATUS;
167 public boolean belongsTo(Object family) {
168 return FAMILY.equals(family);
170 public void generated_6928064623508817343() {
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;
181 private final IElementChangedListener fDeltaListener;
182 private final UpdateJob fUpdateJob;
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$
191 private static OpenTypeHistory fgInstance;
193 public static synchronized OpenTypeHistory getInstance() {
194 if (fgInstance == null)
195 fgInstance= new OpenTypeHistory();
199 public static synchronized void shutdown() {
200 if (fgInstance == null)
202 fgInstance.doShutdown();
205 private OpenTypeHistory() {
206 super(FILENAME, NODE_ROOT, NODE_TYPE_INFO);
207 fTimestampMapping= new HashMap<TypeNameMatch, Long>();
208 fNeedsConsistencyCheck= true;
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
218 fUpdateJob.setPriority(Job.SHORT);
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();
227 public boolean needConsistencyCheck() {
228 return fNeedsConsistencyCheck;
231 public void checkConsistency(IProgressMonitor monitor) throws OperationCanceledException {
232 if (!fNeedsConsistencyCheck)
234 if (fUpdateJob.getState() == Job.RUNNING) {
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.
245 if (!fNeedsConsistencyCheck)
247 internalCheckConsistency(monitor);
250 public synchronized boolean contains(TypeNameMatch type) {
251 return super.contains(type);
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)));
260 super.accessed(info);
263 public synchronized TypeNameMatch remove(TypeNameMatch info) {
264 fTimestampMapping.remove(info);
265 return (TypeNameMatch)super.remove(info);
268 public synchronized void replace(TypeNameMatch old, TypeNameMatch newMatch) {
269 fTimestampMapping.remove(old);
270 fTimestampMapping.put(newMatch, new Long(getContainerTimestamp(newMatch)));
272 super.accessed(newMatch);
275 public synchronized TypeNameMatch[] getTypeInfos() {
276 Collection<Object> values= getValues();
277 int size= values.size();
278 TypeNameMatch[] result= new TypeNameMatch[size];
280 for (Iterator<Object> iter= values.iterator(); iter.hasNext();) {
281 result[i]= (TypeNameMatch)iter.next();
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()))
295 Collections.reverse(result);
296 return result.toArray(new TypeNameMatch[result.size()]);
301 protected Object getKey(Object object) {
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))
319 IType jType= type.getType();
320 if (jType == null || !jType.exists()) {
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));
328 fTimestampMapping.put(type, new Long(currentTimestamp));
331 } catch (JavaModelException e) {
334 if (monitor.isCanceled())
335 throw new OperationCanceledException();
339 fNeedsConsistencyCheck= false;
342 private long getContainerTimestamp(TypeNameMatch match) {
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();
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();
358 } else { // external JAR
359 IPackageFragmentRoot root= match.getPackageFragmentRoot();
361 IFileInfo info= EFS.getLocalFileSystem().getStore(root.getPath()).fetchInfo();
363 return info.getLastModified();
367 } catch (CoreException e) {
370 return IResource.NULL_STAMP;
374 public boolean isContainerDirty(TypeNameMatch match) {
375 ICompilationUnit cu= match.getType().getCompilationUnit();
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();
389 private void doShutdown() {
390 JavaCore.removeElementChangedListener(fDeltaListener);
395 protected Object createFromElement(Element type) {
396 String handle= type.getAttribute(NODE_HANDLE);
400 IJavaElement element= JavaCore.create(handle);
401 if (!(element instanceof IType))
406 modifiers= Integer.parseInt(type.getAttribute(NODE_MODIFIERS));
407 } catch (NumberFormatException e) {
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) {
415 timestamp= Long.parseLong(timestampValue);
416 } catch (NumberFormatException e) {
420 if (timestamp != IResource.NULL_STAMP) {
421 fTimestampMapping.put(info, new Long(timestamp));
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));
436 typeElement.setAttribute(NODE_TIMESTAMP, timestamp.toString());
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));
447 consistencyrunnable.refreshSearchIndices(monitor);
450 FilteredTypesSelectionDialog.fgFirstTime= false;
452 checkConsistency(monitor);