blob: c026a421e3638271465f26857ea27e5e4c1050fb [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2003, 2015 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.ui.callinmarkers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameRequestor;
import org.eclipse.jdt.internal.ui.javaeditor.IClassFileEditorInput;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultLineTracker;
import org.eclipse.jface.text.ILineTracker;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.objectteams.otdt.core.ICallinMapping;
import org.eclipse.objectteams.otdt.core.ICalloutMapping;
import org.eclipse.objectteams.otdt.core.ICalloutToFieldMapping;
import org.eclipse.objectteams.otdt.core.IMethodMapping;
import org.eclipse.objectteams.otdt.core.IOTJavaElement;
import org.eclipse.objectteams.otdt.core.IOTType;
import org.eclipse.objectteams.otdt.core.IRoleType;
import org.eclipse.objectteams.otdt.core.OTModelManager;
import org.eclipse.objectteams.otdt.core.ext.IMarkableJavaElement;
import org.eclipse.objectteams.otdt.core.ext.MarkableFactory;
import org.eclipse.objectteams.otdt.core.search.OTSearchHelper;
import org.eclipse.objectteams.otdt.internal.ui.preferences.GeneralPreferences;
import org.eclipse.objectteams.otdt.ui.OTDTUIPlugin;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
/**
* This class creates Object Teams binding markers of two kinds:
* <ul>
* <li>playedBy markers marking base classes bound by playedBy
* <li>callin markers marking base methods bound by callin.
* </ul>
* It also monitors editor and resource changes and automatically creates/removes callin
* markers for all methods.
*
* TODO(SH): validate updating for methods and implement something similar for types, too.
*
* @author carp
*/
public class CallinMarkerCreator2 extends JavaEditorActivationListener
{
protected CallinMarkerJob _currentJob;
private Map<IJavaElement,IType> m_cachedBaseForRole = new HashMap<IJavaElement,IType>();
private Set<IResource> m_cachedMarkersForResources = new HashSet<IResource>();
private Set<IJavaElement> m_cachedMarkersForJavaElements = new HashSet<IJavaElement>();
private boolean m_enabled = false;
protected AnnotationHelper annotationHelper;
private boolean isInitialized = false;
/**
* Typical usage:
* <code>
* CallinMarkerCreator creator = OTDTUIPlugin.getCallinMarkerCreator();
* creator.updateAllMarkers(myMethod, someProgressMonitor);
* </code>
*/
public CallinMarkerCreator2()
{
IPreferenceStore store = OTDTUIPlugin.getDefault().getPreferenceStore();
store.addPropertyChangeListener(new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event)
{
if (GeneralPreferences.CALLIN_MARKER_ENABLED_BOOL.equals(event.getProperty()))
{
boolean newValue = ((Boolean) event.getNewValue()).booleanValue();
setEnabled(newValue);
// FIXME: upon enablement, run callin marker creator for current editor, if any
}
}
});
setEnabled(store.getBoolean(GeneralPreferences.CALLIN_MARKER_ENABLED_BOOL));
}
public void setEnabled(boolean enable)
{
if (m_enabled == enable)
return;
if (enable)
installListener();
else
uninstallListener();
m_enabled = enable;
}
public boolean isEnabled()
{
return m_enabled;
}
/** API for {@link RoleBindingChangedListener}. */
public void updateCallinMarker(final IMember member, IStatusLineManager statusLine)
{
if (!isEnabled())
return;
if (!isCached(member.getResource()) && !isCreatingMarkersFor(member.getResource()))
return;
final IMarkableJavaElement target = MarkableFactory.createMarkable(member.getResource());
CallinMarkerJob job = new CallinMarkerJob(target) {
protected void updateMarkers(IProgressMonitor monitor) throws Exception {
updateCallinMarker(target, member, monitor);
}
};
// updating just one single method will not set current _currentJob
schedule(job, statusLine);
}
/**
* API for {@link RoleBindingChangedListener}.
* When some role bindings have changed remove cached information regarding its base.
*/
public void invalidateRole(IType roleType, IType baseClass) {
if (baseClass != null)
invalidateBase(baseClass);
IType cachedBaseClass = this.m_cachedBaseForRole.get(roleType);
if ( cachedBaseClass != null
&& !cachedBaseClass.equals(baseClass))
{
// playedBy mapping itself has changed
this.m_cachedBaseForRole.remove(roleType);
invalidateBase(cachedBaseClass);
}
}
public void invalidateBase(IJavaElement baseClass) {
IMarkableJavaElement baseMarkable = MarkableFactory.createMarkable(baseClass);
IEditorPart editor = (IEditorPart) this.fActiveEditor;
if (baseMarkable.isBinary()) {
IJavaElement baseJavaElement = baseMarkable.getJavaElement();
if (baseJavaElement != null) {
this.m_cachedMarkersForJavaElements.remove(baseJavaElement);
if (this.fActiveEditor != null && this.fActiveEditor instanceof IEditorPart) {
if (editor.getEditorInput() instanceof IClassFileEditorInput) {
IClassFile editorClassFile = ((IClassFileEditorInput)editor.getEditorInput()).getClassFile();
if (editorClassFile != null && !isCreatingMarkersFor(editorClassFile) && editorClassFile.equals(baseJavaElement))
updateForBaseMarkable(baseMarkable, editor);
}
}
}
} else {
IResource baseResource = baseMarkable.getResource();
this.m_cachedMarkersForResources.remove(baseResource);
if (this.fActiveEditor != null && this.fActiveEditor instanceof IEditorPart) {
if (editor.getEditorInput() instanceof IFileEditorInput) {
IResource editorResource = ((IFileEditorInput)editor.getEditorInput()).getFile();
if (editorResource != null && !isCreatingMarkersFor(editorResource) && editorResource.equals(baseResource))
updateForBaseMarkable(baseMarkable, editor);
}
}
}
}
private void updateForBaseMarkable(IMarkableJavaElement baseMarkable,
IEditorPart editor) {
IStatusLineManager statusLine = editor.getEditorSite().getActionBars().getStatusLineManager();
updateCallinMarkers(baseMarkable, statusLine);
}
/**
* When the editor input changed find the markable target and update markers.
*/
protected void activeJavaEditorChanged(IWorkbenchPart editor)
{
if (!(editor instanceof IEditorPart))
{
if (editor == null) {
this.annotationHelper = null;
this.fActiveEditor = null;
}
return;
}
IEditorPart targetEditor = (IEditorPart) editor;
final IEditorInput editorInput = targetEditor.getEditorInput();
final IStatusLineManager statusLine = targetEditor.getEditorSite().getActionBars().getStatusLineManager();
this.fActiveEditor = editor;
this.annotationHelper = new AnnotationHelper(targetEditor, editorInput);
IMarkableJavaElement target= null;
if ((editorInput instanceof IFileEditorInput)) { // source file
IResource resource = ((IFileEditorInput)editorInput).getFile();
if (resource == null || isCached(resource) || isCreatingMarkersFor(resource))
return; // already has markers -- skip it
target = MarkableFactory.createMarkable(resource);
} else if (editorInput instanceof IClassFileEditorInput) { // binary java element
IClassFile element = ((IClassFileEditorInput) editorInput).getClassFile();
if (element == null || isCached(element) || isCreatingMarkersFor(element))
return; // already has markers -- skip it
target = MarkableFactory.createMarkable(element);
} else {
return; // unexpected editor input
}
if (target.exists())
updateCallinMarkers(target, statusLine);
}
// FIXME: listen to classpath changes as well
// hint from the news group:
// See:
// - org.eclipse.jdt.core.JavaCore.addElementChangedListener(IElementChangedListener, int)
// - org.eclipse.jdt.core.IJavaElementDelta.F_CLASSPATH_CHANGED
/**
* @param resource the resource in where to look for bound base methods
* @param statusLine a status line where errors can be displayed or null if you don't want error messages
*/
public void updateCallinMarkers(final IMarkableJavaElement target, IStatusLineManager statusLine)
{
IJavaElement javaElement = target.getJavaElement();
IJavaElement pack = javaElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
if (pack != null) {
if (pack.getElementName().startsWith("java.lang")) //$NON-NLS-1$
return;
if (pack.getElementName().equals("java.io") && javaElement.getElementName().startsWith("Serializable")) //$NON-NLS-1$ //$NON-NLS-2$
return;
}
CallinMarkerJob job = new CallinMarkerJob(target) {
protected void updateMarkers(IProgressMonitor monitor) throws Exception {
updateCallinMarkers(target, monitor);
}
};
setCurrentJob(job);
schedule(job, statusLine);
}
/**
* For a given markable target remove all old markers and create new ones.
*
* @param target the markable item to create markers for
* @param monitor
* @throws CoreException thrown when one of the many searches and or lookups failed.
*/
private void updateCallinMarkers(final IMarkableJavaElement target, IProgressMonitor monitor) throws CoreException
{
try
{
if (target == null || !target.exists())
return;
monitor.beginTask(
NLS.bind(OTDTUIPlugin.getResourceString("CallinMarkerCreator2.updating_for_resource_message"), //$NON-NLS-1$
new Object[] { target.getName() }), 62);
target.removeMarkers(CallinMarker.CALLIN_MARKER_IDS);
monitor.worked(2);
// ==== role bindings: ====
IJavaProject[] projects = target.getProjects();
Set<IType> allTypes = target.getAllTypes(projects, monitor); // 10 ticks
Map<IMember, Set<IType>> playedByMap = OTSearchHelper.searchPlayedByBindings(allTypes, projects, new MySubProgressMonitor(monitor, 20));
if (playedByMap == null || playedByMap.size() == 0)
return; // no base types or cancelled
createMarkersFor(target, playedByMap, CallinMarker.PLAYEDBY_ID, new MySubProgressMonitor(monitor, 5));
// collect all roles for use as search scope:
IMember[] allRoleTypes = collectRolesAndSubroles(playedByMap, new MySubProgressMonitor(monitor, 10));
if (monitor.isCanceled())
return;
// ==== callin/callout bindings: ====
Set<IMember> allMembers = getAllMethodsAndFields(target.getJavaElement());
monitor.worked(5);
updateMethodMarkers(target, allRoleTypes, allMembers, new SubProgressMonitor(monitor, 10));
}
finally {
monitor.done();
}
}
private IMember[] collectRolesAndSubroles(Map<IMember, Set<IType>> playedByMap, IProgressMonitor submon)
throws JavaModelException
{
IMember[] allRoleTypes;
Set<IMember> collectedRoleTypes = new HashSet<IMember>();
submon.beginTask("Finding sub roles", playedByMap.size()); //$NON-NLS-1$
for (Set<IType> value : playedByMap.values()) {
collectedRoleTypes.addAll(value);
for (IType member : value) {
if (submon.isCanceled()) return null;
if (member.exists() && OTModelManager.isRole(member)) {
IType[] subtypes = member.newTypeHierarchy(submon).getAllSubtypes(member);
for (IType subtype : subtypes)
collectedRoleTypes.add(subtype);
}
submon.worked(1);
}
}
allRoleTypes = collectedRoleTypes.toArray(new IMember[collectedRoleTypes.size()]);
submon.done();
return allRoleTypes;
}
private void updateCallinMarker(IMarkableJavaElement target, IMember member, IProgressMonitor monitor) throws CoreException
{
// TODO(SH): in this scenario a search for all callins mentioning method should be faster.
try
{
monitor.beginTask(
NLS.bind(OTDTUIPlugin.getResourceString("CallinMarkerCreator2.updating_for_method_message"), //$NON-NLS-1$
new Object[] { member.getElementName() }), 45);
IResource resource = member.getResource();
if (resource != null) // only called when base class changed -> source only.
{
CallinMarkerRemover.removeCallinMarker( member, resource );
monitor.worked(5);
// find direct roles:
ArrayList<IType> allTypes = new ArrayList<IType>(1);
allTypes.add((IType)member.getAncestor(IJavaElement.TYPE)); // TODO(SH): could be IOTType?
Map<IMember, Set<IType>> playedByMap = OTSearchHelper.searchPlayedByBindings(allTypes,
new IJavaProject[]{member.getJavaProject()},
new MySubProgressMonitor(monitor, 20));
if (playedByMap == null || playedByMap.isEmpty())
return; // no base types or cancelled
// collect all roles w/ subroles for use as search scope:
IMember[] allRoleTypes = collectRolesAndSubroles(playedByMap, new MySubProgressMonitor(monitor, 10));
if (monitor.isCanceled())
return;
ArrayList<IMember> memberSet = new ArrayList<IMember>(1);
memberSet.add(member);
updateMethodMarkers(target, allRoleTypes, memberSet, new SubProgressMonitor(monitor, 10));
}
}
finally {
monitor.done();
}
}
private void updateMethodMarkers(IMarkableJavaElement target, IMember[] allRoleTypes, Collection<IMember> memberSet, IProgressMonitor monitor)
{
try {
monitor.beginTask(null, 40);
Map<IMember, Set<IMember>> callinMap = new HashMap<IMember, Set<IMember>>();
Map<IMember, Set<IMember>> calloutMap = new HashMap<IMember, Set<IMember>>();
searchMemberBindings(memberSet, allRoleTypes, callinMap, calloutMap, new MySubProgressMonitor(monitor, 20));
if (monitor.isCanceled()) return;
createMarkersFor(target, callinMap, CallinMarker.CALLIN_ID, new MySubProgressMonitor(monitor, 10));
createMarkersFor(target, calloutMap, CallinMarker.CALLOUT_ID, new MySubProgressMonitor(monitor, 10));
} finally {
monitor.done();
}
}
private void setCurrentJob(CallinMarkerJob job)
{
synchronized (this) {
if (_currentJob != null) {
_currentJob.cancel();
}
_currentJob = job;
}
}
// ------ Some of these methods are duplicated to account for the source/binary duality: ------
public boolean isCreatingMarkersFor(IResource resource)
{
synchronized(this) {
if (_currentJob == null) return false;
IResource jobResource = _currentJob.getResource();
return (jobResource != null) && jobResource.equals(resource);
}
}
private boolean isCreatingMarkersFor(IJavaElement element)
{
synchronized(this) {
if (_currentJob == null) return false;
IJavaElement javaElement = _currentJob.getJavaElement();
return (javaElement != null) && javaElement.equals(element);
}
}
private boolean isCached(IResource resource)
{
return m_cachedMarkersForResources.contains(resource);
}
private boolean isCached(IJavaElement element)
{
return m_cachedMarkersForJavaElements.contains(element);
}
private void setCached(final IResource resource)
{
m_cachedMarkersForResources.add(resource);
}
private void setCached(final IJavaElement element)
{
m_cachedMarkersForJavaElements.add(element);
}
private void removeFromCache(final IResource resource)
{
m_cachedMarkersForResources.remove(resource);
}
private void removeFromCache(final IJavaElement element)
{
m_cachedMarkersForJavaElements.remove(element);
}
private void removeFromCache(IWorkbenchPartReference ref)
{
IWorkbenchPart part = ref.getPart(false);
if (part instanceof IEditorPart)
{
IEditorInput input = ((IEditorPart) part).getEditorInput();
if (input instanceof IFileEditorInput)
removeFromCache(((IFileEditorInput) input).getFile());
}
}
public void partClosed(IWorkbenchPartReference ref)
{
removeFromCache(ref);
super.partClosed(ref);
}
/**
* Fetch all methods and fields contained in the input.
* @param javaElement the corresponding ICompilationUnit, IClassFile or IType
*/
private Set<IMember> getAllMethodsAndFields(IJavaElement javaElement) {
if (javaElement == null)
return new HashSet<IMember>(0);
Set<IMember> members = new HashSet<IMember>(13);
switch (javaElement.getElementType())
{
case IJavaElement.COMPILATION_UNIT:
{
ICompilationUnit unit = (ICompilationUnit)javaElement;
IType[] types;
try {
types = unit.getTypes();
for (int idx = 0; idx < types.length; idx++)
members.addAll(getAllMethodsAndFields(types[idx]));
} catch (JavaModelException e) {
// ignore, without types we simply find no methods
}
break;
}
case IJavaElement.CLASS_FILE:
{
IClassFile classFile = (IClassFile)javaElement;
members.addAll(getAllMethodsAndFields(classFile.getType()));
break;
}
case IJavaElement.TYPE:
{
IType type = (IType) javaElement;
try {
members.addAll(Arrays.asList(type.getMethods()));
members.addAll(Arrays.asList(type.getFields()));
IOTType otType = OTModelManager.getOTElement(type);
if (otType != null && otType.isRole())
for (IMethodMapping mapping : ((IRoleType)otType).getMethodMappings(IRoleType.CALLOUTS))
members.add(mapping);
// handle all inner types
IType[] memberTypes = type.getTypes();
for (int idx = 0; idx < memberTypes.length; idx++)
members.addAll(getAllMethodsAndFields(memberTypes[idx]));
} catch (JavaModelException e) {
// ignore, finding methods bailed out but keep those we already found
}
break;
}
default:
break;
}
return members;
}
/**
* Search all callin bindings within allRoleTypes mentioning one of baseMethods as a base method.
*
* @param baseMembers base methods and fields of interest
* @param allRoleTypes roles where to search
* @param callinMap store found callin bindings here (one set per base method)
* @param calloutMap store found callout bindings here (one set per base method/field)
* @param monitor
*/
private void searchMemberBindings(Collection<IMember> baseMembers,
IMember[] allRoleTypes,
Map<IMember, Set<IMember>> callinMap,
Map<IMember, Set<IMember>> calloutMap,
MySubProgressMonitor monitor)
{
if (baseMembers == null || baseMembers.size() == 0) {
monitor.doneNothing();
return;
}
// given all potential role types, just directly traverse to the callin bindings:
for (IMember roleMember : allRoleTypes)
{
if (monitor.isCanceled()) return;
IOTType otType = OTModelManager.getOTElement((IType)roleMember);
if (otType == null || !otType.isRole()) continue;
IRoleType roleType = (IRoleType)otType;
for (IMethodMapping mapping : roleType.getMethodMappings())
{
try {
if (mapping.getElementType() == IOTJavaElement.CALLIN_MAPPING)
{
ICallinMapping callinMapping = (ICallinMapping) mapping;
for (IMethod baseMethod : callinMapping.getBoundBaseMethods())
if (baseMembers.contains(baseMethod)) // TODO(SH): would comparison of resources suffice??
OTSearchHelper.addToMapOfSets(callinMap, baseMethod, mapping);
}
else if (mapping.getElementType() == IOTJavaElement.CALLOUT_MAPPING) {
ICalloutMapping calloutMapping = (ICalloutMapping) mapping;
IMethod baseMethod = calloutMapping.getBoundBaseMethod();
if (baseMembers.contains(baseMethod) && !isVisibleFor(baseMethod, roleType))
OTSearchHelper.addToMapOfSets(calloutMap, baseMethod, mapping);
}
else if (mapping.getElementType() == IOTJavaElement.CALLOUT_TO_FIELD_MAPPING) {
ICalloutToFieldMapping calloutMapping = (ICalloutToFieldMapping) mapping;
IField baseField = calloutMapping.getBoundBaseField();
if (baseMembers.contains(baseField) && !isVisibleFor(baseField, roleType))
OTSearchHelper.addToMapOfSets(calloutMap, baseField, mapping);
}
} catch (JavaModelException ex) {
OTDTUIPlugin.getDefault().getLog().log(new Status(Status.ERROR, OTDTUIPlugin.UIPLUGIN_ID, "Error checking callin/callout binding", ex)); //$NON-NLS-1$
}
}
}
}
boolean isVisibleFor(IMember baseMember, IType roleType) throws JavaModelException {
int flags = baseMember.getFlags();
if (Flags.isPrivate(flags))
return false;
if (Flags.isPublic(flags))
return true;
String rolePackage = roleType.getPackageFragment().getElementName();
return baseMember.getDeclaringType().getPackageFragment().getElementName().equals(rolePackage);
}
/**
* Create actual markers wrapped by CallinMarkers from the data given as bindingMap.
*
* @param target where to attach the markers
* @param bindingMap data for creating markers: a map from base elements (classes|methods) to role elements (roles|callins) binding to the former.
* @param markerKind what kind of marker should be created?
* @param monitor
*/
private <M extends IMember> void createMarkersFor(final IMarkableJavaElement target, final Map<IMember, Set<M>> bindingMap, final String markerKind, MySubProgressMonitor monitor)
{
if (bindingMap == null) {
monitor.doneNothing();
return;
}
final Set<IMember> baseElements = bindingMap.keySet();
if (baseElements == null || baseElements.size() == 0) {
monitor.doneNothing();
return;
}
// freeze this value to avoid shared access to the field:
final AnnotationHelper myAnnotationHelper = annotationHelper;
if (myAnnotationHelper == null) { // has the active editor been reset to null?
if (monitor != null)
monitor.setCanceled(true);
return;
}
IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException
{
monitor.beginTask(OTDTUIPlugin.getResourceString("CallinMarkerCreator2.creating_markers_message"), baseElements.size()); //$NON-NLS-1$
try {
ILineTracker lineTracker = null;
final ICompilationUnit compilationUnit = baseElements.iterator().next().getCompilationUnit();
if (compilationUnit != null) {
lineTracker = new DefaultLineTracker();
lineTracker.set(compilationUnit.getSource());
}
CallinMarker marker = new CallinMarker(markerKind);
for (IMember baseElement : baseElements)
{
// ensure type is actually contained in the target, not super/inherited
if (baseElement instanceof IType && !target.containsElement(baseElement))
continue;
Set<M> bindings = bindingMap.get(baseElement);
if (bindings != null && bindings.size() != 0)
{
if (markerKind == CallinMarker.PLAYEDBY_ID)
for (M binding : bindings)
CallinMarkerCreator2.this.m_cachedBaseForRole.put(binding, (IType)baseElement);
try {
ISourceRange nameRange = baseElement.getNameRange();
if (nameRange.getOffset() >= 0 && nameRange.getLength() >= 0) {
if (lineTracker != null) {
int line = lineTracker.getLineNumberOfOffset(nameRange.getOffset()) + 1; // one-based
marker.setLineNumber(line);
} else {
marker.setNameRange(nameRange);
}
if (markerKind.equals(CallinMarker.CALLOUT_ID)) {
myAnnotationHelper.removeSomeWarnings(target.getResource(), nameRange);
}
} else if (!baseElement.isBinary()) {
if (nameRange.getOffset() < 0)
throw new BadLocationException("Offset must be >= 0, is "+nameRange.getOffset()); //$NON-NLS-1$
if (nameRange.getLength() < 0)
throw new BadLocationException("Length must be >= 0, is "+nameRange.getLength()); //$NON-NLS-1$
}
}
catch (BadLocationException ex) {
OTDTUIPlugin.logException(OTDTUIPlugin.getResourceString("CallinMarkerCreator2.line_number_error_message"), ex); //$NON-NLS-1$
}
marker.setBaseElement(baseElement);
marker.setRoleElement(bindings);
marker.create(target);
}
monitor.worked(1);
}
}
finally {
monitor.done();
}
}
};
try {
ResourcesPlugin.getWorkspace().run(runnable, null, IWorkspace.AVOID_UPDATE, monitor);
} catch (CoreException ex) {
OTDTUIPlugin.logException("Error creating markers", ex); //$NON-NLS-1$
}
}
/** chained scheduling:<ul>
* <li> first wait until the indexes are ready (only on first invocation),
* <li> only then schedule our job.
*/
protected synchronized void schedule(final CallinMarkerJob job, final IStatusLineManager statusLine)
{
if (this.isInitialized) {
doSchedule(job, statusLine);
return;
} else {
this.isInitialized = true;
Job waitForIndex = new Job(OTDTUIPlugin.getResourceString("CallinMarkerCreator2.wait_for_index_message")) { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
// dummy query for waiting until the indexes are ready (see AbstractJavaModelTest)
new SearchEngine().searchAllTypeNames(
null,
SearchPattern.R_EXACT_MATCH,
"!@$#!@".toCharArray(), //$NON-NLS-1$
SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE,
IJavaSearchConstants.CLASS,
SearchEngine.createWorkspaceScope(),
new TypeNameRequestor() {
public void acceptType(int modifiers,char[] packageName,char[] simpleTypeName,char[][] enclosingTypeNames,String path) {}
},
IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
null);
} catch (CoreException e) {
} finally {
// chaining:
doSchedule(job, statusLine);
}
return Status.OK_STATUS;
}
};
waitForIndex.setPriority(Job.DECORATE);
waitForIndex.schedule(100);
}
}
protected void doSchedule(final CallinMarkerJob job, IStatusLineManager statusLine)
{
job.addJobChangeListener(new JobListener(statusLine) {
protected void jobFinished(int status) {
synchronized(CallinMarkerCreator2.this) {
if (_currentJob == job)
_currentJob = null;
IResource resource = job.getResource();
if (resource != null)
if (status == IStatus.OK)
setCached(resource);
else
removeFromCache(resource);
else {
IJavaElement element = job.getJavaElement();
if (status == IStatus.OK)
setCached(element);
else
removeFromCache(element);
}
}
}
});
job.setPriority(Job.DECORATE);
job.schedule(100); // delay 100ms
}
}