package org.eclipse.debug.core.sourcelookup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationListener;
import org.eclipse.debug.core.ILaunchListener;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.IStatusHandler;
import org.eclipse.debug.core.model.IStackFrame;
import org.eclipse.debug.core.sourcelookup.containers.DefaultSourceContainer;
import org.eclipse.debug.internal.core.sourcelookup.SourceLookupMessages;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.ibm.icu.text.MessageFormat;
public abstract class AbstractSourceLookupDirector implements ISourceLookupDirector, ILaunchConfigurationListener, ILaunchListener {
protected String fId;
protected ArrayList<ISourceLookupParticipant> fParticipants = new ArrayList<>();
protected ISourceContainer[] fSourceContainers = null;
protected ILaunchConfiguration fConfig;
protected boolean fDuplicates = false;
protected ISourcePathComputer fComputer = null;
protected Map<Object, Object> fResolvedElements = null;
private ISourceLookupParticipant fCurrentParticipant;
protected static final IStatus fPromptStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 200, "", null);
protected static final IStatus fResolveDuplicatesStatus = new Status(IStatus.INFO, "org.eclipse.debug.ui", 205, "", null);
protected static final String DIRECTOR_ROOT_NODE = "sourceLookupDirector";
protected static final String CONTAINERS_NODE = "sourceContainers";
protected static final String DUPLICATES_ATTR = "duplicates";
protected static final String CONTAINER_NODE = "container";
protected static final String CONTAINER_TYPE_ATTR = "typeId";
protected static final String CONTAINER_MEMENTO_ATTR = "memento";
class SourceLookupQuery implements ISafeRunnable {
private List<Object> fSourceElements = new ArrayList<>();
private Object fElement = null;
private Throwable fException = null;
SourceLookupQuery(Object element) {
fElement = element;
}
@Override
public void handleException(Throwable exception) {
fException = exception;
}
public Throwable getException() {
return fException;
}
@Override
public void run() throws Exception {
MultiStatus multiStatus = null;
CoreException single = null;
ISourceLookupParticipant[] participants = getParticipants();
try {
for (ISourceLookupParticipant participant : participants) {
setCurrentParticipant(participant);
Object[] sourceArray;
try {
sourceArray = participant.findSourceElements(fElement);
if (sourceArray !=null && sourceArray.length > 0) {
if (isFindDuplicates()) {
for (Object s : sourceArray) {
if (!checkDuplicate(s, fSourceElements)) {
fSourceElements.add(s);
}
}
} else {
fSourceElements.add(sourceArray[0]);
return;
}
}
} catch (CoreException e) {
if (single == null) {
single = e;
} else if (multiStatus == null) {
multiStatus = new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, new IStatus[]{single.getStatus()}, SourceLookupMessages.Source_Lookup_Error, null);
multiStatus.add(e.getStatus());
} else {
multiStatus.add(e.getStatus());
}
}
}
} finally {
setCurrentParticipant(null);
}
if (fSourceElements.isEmpty()) {
if (multiStatus != null) {
fException = new CoreException(multiStatus);
} else if (single != null) {
fException = single;
}
}
}
public List<Object> getSourceElements() {
return fSourceElements;
}
public void dispose() {
fElement = null;
fSourceElements = null;
fException = null;
}
}
public AbstractSourceLookupDirector() {
}
public void setId(String id) {
fId = id;
}
@Override
public synchronized void dispose() {
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
launchManager.removeLaunchConfigurationListener(this);
launchManager.removeLaunchListener(this);
for (ISourceLookupParticipant participant : fParticipants) {
if(participant != this) {
participant.dispose();
}
}
fParticipants.clear();
if (fSourceContainers != null) {
for (ISourceContainer container : fSourceContainers) {
container.dispose();
}
}
fSourceContainers = null;
fResolvedElements = null;
}
protected void abort(String message, Throwable exception) throws CoreException {
IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, message, exception);
throw new CoreException(status);
}
private List<ISourceContainer> parseSourceContainers(NodeList list) throws CoreException {
List<ISourceContainer> containers = new ArrayList<>();
for (int i=0; i < list.getLength(); i++) {
if(!(list.item(i).getNodeType() == Node.ELEMENT_NODE)) {
continue;
}
Element element = (Element)list.item(i);
String typeId = element.getAttribute(CONTAINER_TYPE_ATTR);
if (typeId == null || typeId.equals("")) {
abort(SourceLookupMessages.AbstractSourceLookupDirector_11, null);
}
ISourceContainerType type = DebugPlugin.getDefault().getLaunchManager().getSourceContainerType(typeId);
if(type != null) {
String memento = element.getAttribute(CONTAINER_MEMENTO_ATTR);
if (memento == null || memento.equals("")) {
abort(SourceLookupMessages.AbstractSourceLookupDirector_13, null);
}
ISourceContainer container = type.createSourceContainer(memento);
containers.add(container);
}
else {
abort(MessageFormat.format(SourceLookupMessages.AbstractSourceLookupDirector_12, new Object[] { typeId }), null);
}
}
return containers;
}
private synchronized void addSourceLookupParticipant(ISourceLookupParticipant participant) {
if (!fParticipants.contains(participant)) {
fParticipants.add(participant);
participant.init(this);
}
}
@Override
public synchronized ISourceContainer[] getSourceContainers() {
if (fSourceContainers == null) {
return new ISourceContainer[0];
}
ISourceContainer[] copy = new ISourceContainer[fSourceContainers.length];
System.arraycopy(fSourceContainers, 0, copy, 0, fSourceContainers.length);
return copy;
}
@Override
public boolean isFindDuplicates() {
return fDuplicates;
}
@Override
public void setFindDuplicates(boolean duplicates) {
fDuplicates = duplicates;
}
private synchronized void removeSourceLookupParticipant(ISourceLookupParticipant participant) {
if (fParticipants.remove(participant)) {
participant.dispose();
}
}
@Override
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
ILaunchConfiguration from = DebugPlugin.getDefault().getLaunchManager().getMovedFrom(configuration);
if (from != null && from.equals(getLaunchConfiguration())) {
fConfig = configuration;
}
}
@Override
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
if (fConfig == null || configuration.isWorkingCopy()) {
return;
}
if(fConfig.equals(configuration)) {
try{
String locatorMemento = configuration.getAttribute(ILaunchConfiguration.ATTR_SOURCE_LOCATOR_MEMENTO,(String)null);
if (locatorMemento == null) {
initializeDefaults(configuration);
} else {
initializeFromMemento(locatorMemento, configuration);
}
} catch (CoreException e){
}
}
}
@Override
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
if (configuration.equals(getLaunchConfiguration())) {
if (DebugPlugin.getDefault().getLaunchManager().getMovedTo(configuration) == null) {
fConfig = null;
}
}
}
@Override
public synchronized String getMemento() throws CoreException {
Document doc = DebugPlugin.newDocument();
Element rootNode = doc.createElement(DIRECTOR_ROOT_NODE);
doc.appendChild(rootNode);
Element pathNode = doc.createElement(CONTAINERS_NODE);
if(fDuplicates) {
pathNode.setAttribute(DUPLICATES_ATTR, "true");
} else {
pathNode.setAttribute(DUPLICATES_ATTR, "false");
}
rootNode.appendChild(pathNode);
if(fSourceContainers !=null){
for (ISourceContainer container : fSourceContainers) {
Element node = doc.createElement(CONTAINER_NODE);
ISourceContainerType type = container.getType();
node.setAttribute(CONTAINER_TYPE_ATTR, type.getId());
node.setAttribute(CONTAINER_MEMENTO_ATTR, type.getMemento(container));
pathNode.appendChild(node);
}
}
return DebugPlugin.serializeDocument(doc);
}
@Override
public void initializeFromMemento(String memento) throws CoreException {
doInitializeFromMemento(memento, true);
}
protected void doInitializeFromMemento(String memento, boolean dispose) throws CoreException {
if (dispose) {
dispose();
}
Element rootElement = DebugPlugin.parseDocument(memento);
if (!rootElement.getNodeName().equalsIgnoreCase(DIRECTOR_ROOT_NODE)) {
abort(SourceLookupMessages.AbstractSourceLookupDirector_14, null);
}
NodeList list = rootElement.getChildNodes();
int length = list.getLength();
for (int i = 0; i < length; ++i) {
Node node = list.item(i);
short type = node.getNodeType();
if (type == Node.ELEMENT_NODE) {
Element entry = (Element) node;
if(entry.getNodeName().equalsIgnoreCase(CONTAINERS_NODE)){
setFindDuplicates("true".equals(entry.getAttribute(DUPLICATES_ATTR)));
NodeList children = entry.getChildNodes();
List<ISourceContainer> containers = parseSourceContainers(children);
setSourceContainers(containers.toArray(new ISourceContainer[containers.size()]));
}
}
}
initializeParticipants();
}
@Override
public void setSourceContainers(ISourceContainer[] containers) {
synchronized (this) {
List<ISourceContainer> list = Arrays.asList(containers);
ISourceContainer[] old = getSourceContainers();
for (ISourceContainer container : old) {
if (!list.contains(container)) {
container.dispose();
}
}
fSourceContainers = containers;
for (ISourceContainer container : containers) {
container.init(this);
}
}
fResolvedElements = null;
ISourceLookupParticipant[] participants = getParticipants();
for (ISourceLookupParticipant participant : participants) {
participant.sourceContainersChanged(this);
}
}
@Override
public Object getSourceElement(IStackFrame stackFrame) {
return getSourceElement((Object)stackFrame);
}
protected List<Object> doSourceLookup(Object element) {
SourceLookupQuery query = new SourceLookupQuery(element);
SafeRunner.run(query);
List<Object> sources = query.getSourceElements();
Throwable exception = query.getException();
if (exception != null) {
if (exception instanceof CoreException) {
CoreException ce = (CoreException) exception;
if (ce.getStatus().getSeverity() == IStatus.ERROR) {
DebugPlugin.log(ce);
}
} else {
DebugPlugin.log(exception);
}
}
query.dispose();
return sources;
}
public Object resolveSourceElement(Object element, List<Object> sources) {
for (Object dup : sources) {
Object resolved = getCachedElement(dup);
if (resolved != null) {
return resolved;
}
}
IStatusHandler prompter = DebugPlugin.getDefault().getStatusHandler(fPromptStatus);
if (prompter != null) {
try {
Object result = prompter.handleStatus(fResolveDuplicatesStatus, new Object[]{element, sources});
if (result != null) {
cacheResolvedElement(sources, result);
return result;
}
} catch (CoreException e) {
}
}
return sources.get(0);
}
private boolean checkDuplicate(Object sourceToAdd, List<Object> sources) {
if(sources.isEmpty()) {
return false;
}
for (Object obj : sources) {
if (obj.equals(sourceToAdd)) {
return true;
}
}
return false;
}
@Override
public void initializeFromMemento(String memento, ILaunchConfiguration configuration) throws CoreException {
dispose();
setLaunchConfiguration(configuration);
doInitializeFromMemento(memento, false);
}
@Override
public void initializeDefaults(ILaunchConfiguration configuration) throws CoreException {
dispose();
setLaunchConfiguration(configuration);
setSourceContainers(new ISourceContainer[]{new DefaultSourceContainer()});
initializeParticipants();
}
@Override
public ILaunchConfiguration getLaunchConfiguration() {
return fConfig;
}
protected void setLaunchConfiguration(ILaunchConfiguration configuration) {
fConfig = configuration;
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
launchManager.addLaunchConfigurationListener(this);
launchManager.addLaunchListener(this);
}
@Override
public void launchAdded(ILaunch launch) {
}
@Override
public void launchChanged(ILaunch launch) {
}
@Override
public void launchRemoved(ILaunch launch) {
if (this.equals(launch.getSourceLocator())) {
dispose();
}
}
@Override
public synchronized ISourceLookupParticipant[] getParticipants() {
return fParticipants.toArray(new ISourceLookupParticipant[fParticipants.size()]);
}
@Override
public boolean supportsSourceContainerType(ISourceContainerType type) {
return true;
}
protected void cacheResolvedElement(List<Object> duplicates, Object sourceElement) {
if (fResolvedElements == null) {
fResolvedElements = new HashMap<>(10);
}
for (Object dup : duplicates) {
fResolvedElements.put(dup, sourceElement);
}
}
protected Object getCachedElement(Object duplicate) {
if (fResolvedElements != null) {
return fResolvedElements.get(duplicate);
}
return null;
}
protected void clearCachedElement(Object duplicate) {
if (fResolvedElements != null) {
fResolvedElements.remove(duplicate);
}
}
@Override
public void clearSourceElements(Object element) {
List<Object> list = doSourceLookup(element);
if (list.size() > 0) {
for (Object obj : list) {
clearCachedElement(obj);
}
}
}
@Override
public void addParticipants(ISourceLookupParticipant[] participants) {
for (ISourceLookupParticipant participant : participants) {
addSourceLookupParticipant(participant);
participant.sourceContainersChanged(this);
}
}
@Override
public void removeParticipants(ISourceLookupParticipant[] participants) {
for (ISourceLookupParticipant participant : participants) {
removeSourceLookupParticipant(participant);
}
}
@Override
public String getId() {
return fId;
}
@Override
public ISourcePathComputer getSourcePathComputer() {
if (fComputer == null && getLaunchConfiguration() != null) {
try {
return DebugPlugin.getDefault().getLaunchManager().getSourcePathComputer(getLaunchConfiguration());
} catch (CoreException e) {
}
}
return fComputer;
}
@Override
public void setSourcePathComputer(ISourcePathComputer computer) {
fComputer = computer;
}
@Override
public Object[] findSourceElements(Object object) throws CoreException {
SourceLookupQuery query = new SourceLookupQuery(object);
SafeRunner.run(query);
List<Object> sources = query.getSourceElements();
Throwable exception = query.getException();
query.dispose();
if (exception != null && sources.isEmpty()) {
if (exception instanceof CoreException) {
throw (CoreException)exception;
}
abort(SourceLookupMessages.AbstractSourceLookupDirector_10, exception);
}
return sources.toArray();
}
@Override
public Object getSourceElement(Object element) {
List<Object> sources = doSourceLookup(element);
if(sources.size() == 1) {
return sources.get(0);
} else if(sources.size() > 1) {
return resolveSourceElement(element, sources);
} else {
return null;
}
}
private void setCurrentParticipant(ISourceLookupParticipant participant) {
fCurrentParticipant = participant;
}
public ISourceLookupParticipant getCurrentParticipant() {
return fCurrentParticipant;
}
}