/*******************************************************************************
* Copyright (c) 2006, 2012 Spring IDE Developers
* 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
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.aop.ui.navigator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.SourceField;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.navigator.ICommonContentExtensionSite;
import org.eclipse.ui.navigator.ICommonContentProvider;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.xml.core.internal.document.ElementImpl;
import org.springframework.ide.eclipse.aop.core.Activator;
import org.springframework.ide.eclipse.aop.core.internal.model.AopReferenceModel;
import org.springframework.ide.eclipse.aop.core.internal.model.builder.AopReferenceModelBuilderJob;
import org.springframework.ide.eclipse.aop.core.model.IAopModelChangedListener;
import org.springframework.ide.eclipse.aop.core.model.IAopReference;
import org.springframework.ide.eclipse.aop.core.model.IAopReference.ADVICE_TYPE;
import org.springframework.ide.eclipse.aop.core.model.IAspectDefinition;
import org.springframework.ide.eclipse.aop.core.util.AopReferenceModelUtils;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AdviceDeclareParentAopSourceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.BeanReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.ClassMethodReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.IReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.JavaElementReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.MethodBeanReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.MethodReference;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.BeansCoreUtils;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils;
import org.springframework.ide.eclipse.beans.core.model.IBean;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.beans.ui.editor.util.BeansEditorUtils;
import org.springframework.ide.eclipse.core.io.ZipEntryStorage;
import org.springframework.ide.eclipse.core.java.Introspector;
import org.springframework.ide.eclipse.core.model.IModelChangeListener;
import org.springframework.ide.eclipse.core.model.IModelElement;
import org.springframework.ide.eclipse.core.model.ModelChangeEvent;
import org.springframework.ide.eclipse.core.model.ModelChangeEvent.Type;
import org.springframework.ide.eclipse.core.model.xml.XmlSourceLocation;
import org.springframework.ide.eclipse.ui.SpringUIUtils;
/**
* {@link ICommonContentProvider} that contributes elements from the {@link AopReferenceModel} created by
* {@link AopReferenceModelBuilderJob}.
* @author Christian Dupuis
* @author Torsten Juergeleit
* @since 2.0
*/
@SuppressWarnings("restriction")
public class AopReferenceModelNavigatorContentProvider implements ICommonContentProvider, IAopModelChangedListener,
IModelChangeListener {
private StructuredViewer viewer;
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (viewer instanceof StructuredViewer) {
this.viewer = (StructuredViewer) viewer;
}
else {
this.viewer = null;
}
}
public void dispose() {
if (viewer != null && viewer.getInput() != null) {
this.viewer = null;
}
Activator.getModel().unregisterAopModelChangedListener(this);
BeansCorePlugin.getModel().removeChangeListener(this);
}
public Object[] getElements(Object inputElement) {
if (inputElement instanceof IType) {
return getChildren(inputElement);
}
else if (inputElement instanceof IMethod) {
return getChildren(inputElement);
}
else if (inputElement instanceof IField) {
return getChildren(inputElement);
}
else if (inputElement instanceof JavaElementReferenceNode) {
return getChildren(((JavaElementReferenceNode) inputElement).getJavaElement());
}
else if (inputElement instanceof ElementImpl) {
return getChildren(inputElement);
}
return IModelElement.NO_CHILDREN;
}
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof IReferenceNode) {
return ((IReferenceNode) parentElement).getChildren();
}
else if (parentElement instanceof JavaElementReferenceNode
&& ((JavaElementReferenceNode) parentElement).isRoot()) {
return getChildren(((JavaElementReferenceNode) parentElement).getJavaElement());
}
else if (parentElement instanceof IType) {
IType type = (IType) parentElement;
List<Object> me = new ArrayList<Object>();
try {
Set<IMethod> methods = Introspector.getAllMethods(type);
for (IMethod method : methods) {
if (Activator.getModel().isAdvice(method) || Activator.getModel().isAdvised(method)) {
me.addAll(Arrays.asList(getChildren(method)));
}
}
}
catch (JavaModelException e) {
}
ClassMethodReferenceNode node = new ClassMethodReferenceNode(type, me);
List<IAopReference> references = Activator.getModel().getAllReferences();
// fields
try {
for (IField field : type.getFields()) {
Object[] obj = getChildren(field);
if (obj != null && obj.length > 0) {
for (Object o : obj) {
node.getDeclareParentReferences().add((IReferenceNode) o);
}
}
}
}
catch (JavaModelException e) {
}
for (IAopReference reference : references) {
if (reference.getAdviceType() == ADVICE_TYPE.DECLARE_PARENTS) {
if (reference.getTarget().equals(type)) {
node.getDeclaredOnReferences().add(reference);
}
}
}
// add bean references
Set<IBeansConfig> configs = BeansCorePlugin.getModel().getConfigs(type.getFullyQualifiedName());
Set<String> beans = new HashSet<String>();
for (IBeansConfig config : configs) {
List<IBean> pBeans = new ArrayList<IBean>();
pBeans.addAll(config.getBeans());
pBeans.addAll(BeansModelUtils.getInnerBeans(config));
for (IBean b : pBeans) {
if (type.getFullyQualifiedName().equals(BeansModelUtils.getBeanClass(b, config))) {
beans.add(b.getElementID());
}
}
}
node.setBeans(beans);
return new Object[] { node };
}
else if (parentElement instanceof IMethod) {
IMethod method = (IMethod) parentElement;
List<IAopReference> references = Activator.getModel().getAllReferences();
List<IAopReference> foundSourceReferences = new ArrayList<IAopReference>();
List<IAopReference> foundTargetReferences = new ArrayList<IAopReference>();
for (IAopReference reference : references) {
if (reference.getTarget().equals(method)) {
foundTargetReferences.add(reference);
}
if (reference.getSource() != null && reference.getSource().equals(method)) {
foundSourceReferences.add(reference);
}
}
List<IReferenceNode> nodes = new ArrayList<IReferenceNode>();
Map<IMember, MethodReference> refs = new HashMap<IMember, MethodReference>();
for (IAopReference reference : foundSourceReferences) {
if (refs.containsKey(reference.getSource())) {
refs.get(reference.getSource()).getAspects().add(reference);
}
else {
MethodReference r = new MethodReference();
r.setMember(reference.getSource());
r.getAspects().add(reference);
refs.put(reference.getSource(), r);
}
}
for (IAopReference reference : foundTargetReferences) {
if (refs.containsKey(reference.getTarget())) {
refs.get(reference.getTarget()).getAdvices().add(reference);
}
else {
MethodReference r = new MethodReference();
r.setMember(reference.getTarget());
r.getAdvices().add(reference);
refs.put(reference.getTarget(), r);
}
}
for (Map.Entry<IMember, MethodReference> entry : refs.entrySet()) {
nodes.add(new MethodBeanReferenceNode(entry.getKey(), entry.getValue().getAspects(), entry.getValue()
.getAdvices()));
}
return nodes.toArray();
}
else if (parentElement instanceof IField && parentElement instanceof SourceField) {
IField method = (IField) parentElement;
List<IAopReference> references = Activator.getModel().getAllReferences();
List<IAopReference> foundSourceReferences = new ArrayList<IAopReference>();
for (IAopReference reference : references) {
if (reference.getSource() != null && reference.getSource().equals(method)) {
foundSourceReferences.add(reference);
}
}
List<IReferenceNode> nodes = new ArrayList<IReferenceNode>();
Map<IMember, List<IAopReference>> refs = new HashMap<IMember, List<IAopReference>>();
for (IAopReference reference : foundSourceReferences) {
if (refs.containsKey(reference.getSource())) {
refs.get(reference.getSource()).add(reference);
}
else {
List<IAopReference> f = new ArrayList<IAopReference>();
f.add(reference);
refs.put(reference.getSource(), f);
}
}
for (Map.Entry<IMember, List<IAopReference>> entry : refs.entrySet()) {
nodes.add(new AdviceDeclareParentAopSourceNode(entry.getValue()));
}
return nodes.toArray();
}
else if (parentElement instanceof ElementImpl) {
ElementImpl element = (ElementImpl) parentElement;
IStructuredDocument document = element.getStructuredDocument();
List<IReferenceNode> nodes = new ArrayList<IReferenceNode>();
IResource resource = SpringUIUtils.getFile(document);
// check if resource is a Beans Config
if (!BeansCoreUtils.isBeansConfig(resource, true) || document == null) {
return nodes.toArray();
}
IBeansConfig beansConfig = BeansCorePlugin.getModel().getConfig((IFile) resource, true);
int startLine = document.getLineOfOffset(element.getStartOffset()) + 1;
int endLine = document.getLineOfOffset(element.getEndOffset()) + 1;
String id = BeansEditorUtils.getAttribute(element, "id");
nodes.addAll(getChildrenFromXmlLocation(resource, startLine, endLine, id, BeansModelUtils.getBeans(beansConfig)));
return nodes.toArray();
}
return IModelElement.NO_CHILDREN;
}
private List<IReferenceNode> getChildrenFromXmlLocation(IResource resource, int startLine, int endLine, String id,
Set<IBean> beans) {
List<IReferenceNode> nodes = new ArrayList<IReferenceNode>();
List<IAopReference> references = Activator.getModel().getAllReferencesForResource(resource);
Map<IAspectDefinition, List<IAopReference>> foundSourceReferences = new HashMap<IAspectDefinition, List<IAopReference>>();
Map<IAspectDefinition, List<IAopReference>> foundIntroductionSourceReferences = new HashMap<IAspectDefinition, List<IAopReference>>();
Map<IBean, List<IAopReference>> foundTargetReferences = new HashMap<IBean, List<IAopReference>>();
Map<IBean, List<IAopReference>> foundIntroductionTargetReferences = new HashMap<IBean, List<IAopReference>>();
for (IAopReference reference : references) {
if (reference.getDefinition().getAspectName().equals(id)
|| (reference.getDefinition().getAspectStartLineNumber() >= startLine
&& reference.getDefinition().getAspectStartLineNumber() <= endLine && resource
.equals(reference.getDefinition().getResource()))) {
if (reference.getAdviceType() == ADVICE_TYPE.DECLARE_PARENTS) {
if (foundIntroductionSourceReferences.containsKey(reference.getDefinition())) {
foundIntroductionSourceReferences.get(reference.getDefinition()).add(reference);
}
else {
List<IAopReference> tmp = new ArrayList<IAopReference>();
tmp.add(reference);
foundIntroductionSourceReferences.put(reference.getDefinition(), tmp);
}
}
else {
if (foundSourceReferences.containsKey(reference.getDefinition())) {
foundSourceReferences.get(reference.getDefinition()).add(reference);
}
else {
List<IAopReference> tmp = new ArrayList<IAopReference>();
tmp.add(reference);
foundSourceReferences.put(reference.getDefinition(), tmp);
}
}
}
IBean targetBean = AopReferenceModelUtils.getBeanFromElementId(reference.getTargetBeanId());
if (targetBean != null) {
if (reference.getDefinition().getAspectName().equals(id)
|| (targetBean.getElementStartLine() >= startLine && targetBean.getElementEndLine() <= endLine && resource
.equals(targetBean.getElementResource()))) {
if (reference.getAdviceType() == ADVICE_TYPE.DECLARE_PARENTS) {
if (foundIntroductionTargetReferences.containsKey(targetBean)) {
foundIntroductionTargetReferences.get(targetBean).add(reference);
}
else {
List<IAopReference> tmp = new ArrayList<IAopReference>();
tmp.add(reference);
foundIntroductionTargetReferences.put(targetBean, tmp);
}
}
else {
if (foundTargetReferences.containsKey(targetBean)) {
foundTargetReferences.get(targetBean).add(reference);
}
else {
List<IAopReference> tmp = new ArrayList<IAopReference>();
tmp.add(reference);
foundTargetReferences.put(targetBean, tmp);
}
}
}
}
}
// add normal beans
Map<IBean, BeanReferenceNode> beansRefs = new HashMap<IBean, BeanReferenceNode>();
for (IBean bean : beans) {
if (bean.getElementStartLine() >= startLine && bean.getElementEndLine() <= endLine
&& BeansCorePlugin.getModel().getElement(bean.getElementID()) != null) {
// Check model source location so that aop nodes are not added
if (bean.getElementSourceLocation() instanceof XmlSourceLocation) {
String namespace = ((XmlSourceLocation) bean.getElementSourceLocation()).getNamespaceURI();
if (!namespace.equals("http://www.springframework.org/schema/aop")) {
BeanReferenceNode rn = new BeanReferenceNode(bean.getElementID());
nodes.add(rn);
beansRefs.put(bean, rn);
}
}
else {
BeanReferenceNode rn = new BeanReferenceNode(bean.getElementID());
nodes.add(rn);
beansRefs.put(bean, rn);
}
}
}
// add found references
if (foundTargetReferences.size() > 0) {
for (Map.Entry<IBean, List<IAopReference>> entry : foundTargetReferences.entrySet()) {
if (beansRefs.containsKey(entry.getKey())) {
beansRefs.get(entry.getKey()).getAdviseReferences().addAll(entry.getValue());
}
}
}
if (foundIntroductionTargetReferences.size() > 0) {
for (Map.Entry<IBean, List<IAopReference>> entry : foundIntroductionTargetReferences.entrySet()) {
if (beansRefs.containsKey(entry.getKey())) {
beansRefs.get(entry.getKey()).getDeclaredOnReferences().addAll(entry.getValue());
}
}
}
if (foundSourceReferences.size() > 0) {
for (Map.Entry<IAspectDefinition, List<IAopReference>> entry : foundSourceReferences.entrySet()) {
for (Map.Entry<IBean, BeanReferenceNode> n : beansRefs.entrySet()) {
if (n.getKey().getElementStartLine() == entry.getKey().getAspectStartLineNumber()
|| entry.getKey().getAspectName().equals(n.getKey().getElementName())) {
beansRefs.get(n.getKey()).getAspectReferences().addAll(entry.getValue());
}
}
}
}
if (foundIntroductionSourceReferences.size() > 0) {
for (Map.Entry<IAspectDefinition, List<IAopReference>> entry : foundIntroductionSourceReferences.entrySet()) {
for (Map.Entry<IBean, BeanReferenceNode> n : beansRefs.entrySet()) {
if (n.getKey().getElementStartLine() == entry.getKey().getAspectStartLineNumber()
|| entry.getKey().getAspectName().equals(n.getKey().getElementName())) {
beansRefs.get(n.getKey()).getDeclareParentReferences().addAll(entry.getValue());
}
}
}
}
return nodes;
}
public Object getParent(Object element) {
if (element instanceof IModelElement) {
return ((IModelElement) element).getElementParent();
}
else if (element instanceof IFile) {
IBeansConfig config = BeansCorePlugin.getModel().getConfig((IFile) element);
if (config != null) {
return config.getElementParent();
}
}
else if (element instanceof ZipEntryStorage) {
IBeansConfig config = BeansCorePlugin.getModel().getConfig(((ZipEntryStorage) element).getFullName());
if (config != null) {
return config.getElementParent();
}
}
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof IReferenceNode) {
return ((IReferenceNode) element).hasChildren();
}
else if (element instanceof IType) {
IType type = (IType) element;
try {
IMethod[] methods = type.getMethods();
for (IMethod method : methods) {
if (Activator.getModel().isAdvised(method) || Activator.getModel().isAdvice(method)) {
return true;
}
}
}
catch (JavaModelException e) {
}
}
else if (element instanceof IMethod) {
IMethod method = (IMethod) element;
List<IAopReference> references = Activator.getModel().getAllReferences();
for (IAopReference reference : references) {
if (reference.getTarget().equals(method)) {
return true;
}
if (reference.getSource().equals(method)) {
return true;
}
}
}
return false;
}
protected void refreshViewer(final Object element) {
if (viewer instanceof TreeViewer) {
Control ctrl = viewer.getControl();
// Are we in the UI thread?
if (ctrl.getDisplay().getThread() == Thread.currentThread()) {
AopReferenceModelNavigator.refreshViewer((TreeViewer) viewer, AopReferenceModelNavigator
.calculateRootElement(element), element);
}
else {
ctrl.getDisplay().asyncExec(new Runnable() {
public void run() {
// Abort if this happens after disposes
Control ctrl = viewer.getControl();
if (ctrl == null || ctrl.isDisposed()) {
return;
}
AopReferenceModelNavigator.refreshViewer((TreeViewer) viewer, AopReferenceModelNavigator
.calculateRootElement(element), element);
}
});
}
}
}
public void init(ICommonContentExtensionSite config) {
Activator.getModel().registerAopModelChangedListener(this);
BeansCorePlugin.getModel().addChangeListener(this);
}
public void saveState(IMemento aMemento) {
}
public void restoreState(IMemento aMemento) {
}
public void changed() {
Object obj = viewer.getInput();
refreshViewer(obj);
}
public void elementChanged(ModelChangeEvent event) {
if (event.getType() == Type.CHANGED && event.getSource() instanceof IBeansProject) {
changed();
}
}
}