/*******************************************************************************
* Copyright (c) 2005, 2014 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.beans.core.internal.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.ManagedProperties;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.ReplaceOverride;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.BeansCoreUtils;
import org.springframework.ide.eclipse.beans.core.BeansTags;
import org.springframework.ide.eclipse.beans.core.BeansTags.Tag;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansConnection.BeanType;
import org.springframework.ide.eclipse.beans.core.model.IBean;
import org.springframework.ide.eclipse.beans.core.model.IBeanAlias;
import org.springframework.ide.eclipse.beans.core.model.IBeanConstructorArgument;
import org.springframework.ide.eclipse.beans.core.model.IBeanProperty;
import org.springframework.ide.eclipse.beans.core.model.IBeanReference;
import org.springframework.ide.eclipse.beans.core.model.IBeansComponent;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet;
import org.springframework.ide.eclipse.beans.core.model.IBeansList;
import org.springframework.ide.eclipse.beans.core.model.IBeansMap;
import org.springframework.ide.eclipse.beans.core.model.IBeansMapEntry;
import org.springframework.ide.eclipse.beans.core.model.IBeansModel;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.beans.core.model.IBeansSet;
import org.springframework.ide.eclipse.beans.core.model.IBeansTypedString;
import org.springframework.ide.eclipse.beans.core.model.IImportedBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IProfileAwareBeansComponent;
import org.springframework.ide.eclipse.core.io.ZipEntryStorage;
import org.springframework.ide.eclipse.core.java.Introspector;
import org.springframework.ide.eclipse.core.java.JdtUtils;
import org.springframework.ide.eclipse.core.java.SuperTypeHierarchyCache;
import org.springframework.ide.eclipse.core.java.typehierarchy.TypeHierarchyEngine;
import org.springframework.ide.eclipse.core.model.IModelElement;
import org.springframework.ide.eclipse.core.model.IModelElementVisitor;
import org.springframework.ide.eclipse.core.model.IResourceModelElement;
import org.springframework.ide.eclipse.core.model.ISourceModelElement;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Helper methods for working with the BeansCoreModel.
*
* @author Torsten Juergeleit
* @author Christian Dupuis
* @author Martin Lippert
*/
public abstract class BeansModelUtils {
/**
* Returns the <code>IBean</code> for a given bean name from specified context ( <code>IBeansConfig</code> or
* <code>IBeansConfigSet</code>). If the corresponding bean is not found then the context's list of
* <code>IBeanAlias</code>es is checked too.
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the beans are looked-up
* @return <code>IBean</code> or <code>null</code> if bean not found
* @throws IllegalArgumentException if unsupported context specified
*/
public static IBean getBean(String name, IModelElement context) {
if (context instanceof IBeansConfig) {
IBeansConfig config = (IBeansConfig) context;
IBean bean = config.getBean(name);
if (bean == null) {
IBeanAlias alias = config.getAlias(name);
if (alias != null) {
bean = config.getBean(alias.getBeanName());
}
}
if (bean == null) {
for (IBeansComponent component : config.getComponents()) {
bean = getBean(name, component);
if (bean != null) {
return bean;
}
}
}
return bean;
}
else if (context instanceof IBeansConfigSet) {
IBeansConfigSet configSet = (IBeansConfigSet) context;
IBean bean = configSet.getBean(name);
if (bean == null) {
IBeanAlias alias = configSet.getAlias(name);
if (alias != null) {
bean = configSet.getBean(alias.getBeanName());
}
}
if (bean == null) {
for (IBeansComponent component : configSet.getComponents()) {
bean = getBean(name, component);
if (bean != null) {
return bean;
}
}
}
return bean;
}
else {
throw new IllegalArgumentException("Unsupported context " + context);
}
}
/**
* Return's the {@link IBean} for the given name by recursively looking into the {@link IBeansComponent}.
*/
private static IBean getBean(String name, IBeansComponent component) {
for (IBean componentBean : component.getBeans()) {
if (componentBean.getElementName().equals(name)) {
return componentBean;
}
}
for (IBeansComponent nestedComponent : component.getComponents()) {
IBean bean = getBean(name, nestedComponent);
if (bean != null) {
return bean;
}
}
return null;
}
/**
* Returns the given bean's class name.
* @param bean the bean to lookup the bean class name for
* @param context the context ({@link IBeanConfig} or {@link IBeanConfigSet}) the beans are looked-up; if
* <code>null</code> then the bean's config will be first used; if the bean class name cannot be resolved in the
* bean's configs, the algorithm will look in all bean config sets that contain the bean's config
*/
public static String getBeanClass(IBean bean, IModelElement context) {
Assert.notNull(bean);
if (context == null) {
// first use config
context = getConfig(bean);
String className = getBeanClassFromContext(bean, context);
if (className != null) {
return className;
}
// second use possibly config sets
Set<IBeansConfigSet> configSets = getConfigSets(bean);
for (IBeansConfigSet configSet : configSets) {
className = getBeanClassFromContext(bean, configSet);
if (className != null) {
return className;
}
}
}
else {
return getBeanClassFromContext(bean, context);
}
return null;
}
/**
* Returns the given bean's class name.
* @param bean the bean to lookup the bean class name for
* @param context the context ({@link IBeanConfig} or {@link IBeanConfigSet}) the beans are looked-up
* @throws IllegalArgumentException if context of bean is <code>null</code>
*/
public static String getBeanClassFromContext(IBean bean, IModelElement context) {
Assert.notNull(bean);
Assert.notNull(context);
// TODO add factory-bean and factory-method to this check
if (bean.getClassName() != null) {
return bean.getClassName();
}
Set<String> beanNames = new HashSet<String>();
do {
beanNames.add(bean.getElementName());
String parentName = bean.getParentName();
if (parentName != null) {
if (beanNames.contains(parentName)) {
// Break cyclic references
break;
}
bean = getBean(parentName, context);
// TODO add factory-bean and factory-method to this check
if (bean != null && bean.getClassName() != null) {
return bean.getClassName();
}
}
else {
bean = null;
}
} while (bean != null);
return null;
}
/**
* Returns a collection of {@link BeansConnection}s holding all {@link IBean<}s which are referenced from given
* model element. For a bean it's parent bean (for child beans only), constructor argument values and property
* values are checked. {@link IBean} look-up is done from the specified {@link IBeanConfig} or
* {@link IBeanConfigSet}.
* @param element the element ({@link IBean}, {@link IBeanConstructorArgument} or {@link IBeanProperty}) to get all
* referenced beans from
* @param context the context ({@link IBeanConfig} or {@link IBeanConfigSet}) the referenced beans are looked-up
* @param recursive set to <code>true</code> if the dependency graph is traversed recursively
* @throws IllegalArgumentException if unsupported model element specified
*/
public static Set<BeansConnection> getBeanReferences(IModelElement element, IModelElement context, boolean recursive) {
Set<BeansConnection> references = new LinkedHashSet<BeansConnection>();
Set<IBean> referencedBeans = new HashSet<IBean>(); // used to break
// from cycles
return getBeanReferences(element, context, recursive, references, referencedBeans);
}
/**
* Returns a list of all beans which belong to the given model element.
* @param element the model element which contains beans
* @throws IllegalArgumentException if unsupported model element specified
*/
public static Set<IBean> getBeans(IModelElement element) {
return getBeans(element, null);
}
/**
* Returns a list of all beans which belong to the given model element.
* @param element the model element which contains beans
* @param monitor the progress monitor to indicate progress; mark the monitor done after completing the work
* @throws IllegalArgumentException if unsupported model element specified
*/
public static Set<IBean> getBeans(IModelElement element, IProgressMonitor monitor) {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
final Set<IBean> beans = new LinkedHashSet<IBean>();
if (element instanceof IBeansModel) {
Set<IBeansProject> projects = ((IBeansModel) element).getProjects();
monitor.beginTask("Locating bean definitions", projects.size());
try {
for (IBeansProject project : projects) {
monitor.subTask("Locating bean definitions in project '" + project.getElementName() + "'");
for (IBeansConfig config : project.getConfigs()) {
monitor.subTask("Locating bean defintions from file '" + config.getElementName() + "'");
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
config.accept(new IModelElementVisitor() {
public boolean visit(IModelElement element, IProgressMonitor monitor) {
if (element instanceof IBean) {
beans.add((IBean) element);
}
return !monitor.isCanceled();
}
}, new NullProgressMonitor());
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
monitor.worked(1);
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
}
finally {
monitor.done();
}
}
else if (element instanceof IBeansProject) {
Set<IBeansConfig> configs = ((IBeansProject) element).getConfigs();
monitor.beginTask("Locating bean definitions", configs.size());
try {
for (IBeansConfig config : configs) {
monitor.subTask("Loading bean defintion from file '" + config.getElementName() + "'");
beans.addAll(config.getBeans());
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
for (IBeansComponent component : config.getComponents()) {
beans.addAll(component.getBeans());
}
monitor.worked(1);
if (monitor.isCanceled()) {
throw new OperationCanceledException();
}
}
}
finally {
monitor.done();
}
}
else if (element instanceof IBeansConfig) {
beans.addAll(((IBeansConfig) element).getBeans());
for (IBeansComponent component : ((IBeansConfig) element).getComponents()) {
beans.addAll(getBeans(component, monitor));
}
}
else if (element instanceof IBeansConfigSet) {
beans.addAll(((IBeansConfigSet) element).getBeans());
for (IBeansComponent component : ((IBeansConfigSet) element).getComponents()) {
beans.addAll(getBeans(component, monitor));
}
}
else if (element instanceof IBeansComponent) {
for (IBeansComponent component : ((IBeansComponent) element).getComponents()) {
beans.addAll(getBeans(component, monitor));
}
beans.addAll(((IBeansComponent) element).getBeans());
}
else if (element instanceof IBean) {
beans.add((IBean) element);
}
else {
throw new IllegalArgumentException("Unsupported model element " + element);
}
return beans;
}
/**
* Returns the corresponding Java type for given bean's class.
* @param bean the bean to lookup the bean class' Java type
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the beans are looked-up; if
* <code>null</code> then the bean's config is used
* @return the Java type of given bean's class or <code>null</code> if no bean class defined or type not found
*/
public static IType getBeanType(IBean bean, IModelElement context) {
Assert.notNull(bean);
String className = getBeanClass(bean, context);
if (className != null) {
return JdtUtils.getJavaType(getProject(bean).getProject(), className);
}
return null;
}
public static IBean getBeanWithConfigSets(String name, IBeansConfig config) {
IBean bean = getBean(name, config);
if (bean == null) {
IBeansProject project = (IBeansProject) config.getElementParent();
for (IBeansConfigSet configSet : project.getConfigSets()) {
if (configSet.hasConfig(config.getElementName())) {
bean = getBean(name, configSet);
if (bean != null) {
break;
}
}
}
}
return bean;
}
/**
* Returns the child of given parent element's subtree the specified element belongs to. If the given element does
* not belong to the subtree of the specified parent element <code>null</code> is returned.
*/
public static IModelElement getChildForElement(IModelElement parent, IModelElement element) {
while (element != null) {
IModelElement elementParent = element.getElementParent();
if (parent.equals(elementParent)) {
return element;
}
element = elementParent;
}
return null;
}
/**
* Returns the {@link IBeanConfig} the given {@link IModelElement} belongs to.
* @param element the model element to get the beans config for
* @throws IllegalArgumentException if unsupported model element specified
*/
public static IBeansConfig getConfig(IModelElement element) {
if (element instanceof IBeansConfig) {
return (IBeansConfig) element;
}
else if (element instanceof ISourceModelElement) {
do {
element = element.getElementParent();
} while (!(element instanceof IBeansConfig));
return (IBeansConfig) element;
}
throw new IllegalArgumentException("Unsupported model element " + element);
}
/**
* Returns config for given name from specified context (<code>IBeansProject</code> or <code>IBeansConfigSet</code>
* ). Externally referenced configs (config name starts with '/') are recognized too.
* @param configName the name of the config to look for
* @param context the context used for config look-up
* @throws IllegalArgumentException if unsupported context specified
*/
public static IBeansConfig getConfig(String configName, IModelElement context) {
// For external project get the corresponding project from beans model
if (configName.charAt(0) == IBeansConfigSet.EXTERNAL_CONFIG_NAME_PREFIX) {
// Extract project and config name from full qualified config name
int pos = configName.indexOf('/', 1);
String projectName = configName.substring(1, pos);
configName = configName.substring(pos + 1);
IBeansProject project = BeansCorePlugin.getModel().getProject(projectName);
if (project != null) {
return project.getConfig(configName);
}
}
else if (context instanceof IBeansProject) {
return ((IBeansProject) context).getConfig(configName);
}
else if (context instanceof IBeansConfigSet) {
return ((IBeansProject) context.getElementParent()).getConfig(configName);
}
return null;
}
/**
* Returns the beans config for a given ZIP file entry.
*/
public static IBeansConfig getConfig(ZipEntryStorage storage) {
IResourceModelElement parent = (IResourceModelElement) storage.getAdapter(IResourceModelElement.class);
if (parent instanceof IBeansConfig) {
return (IBeansConfig) parent;
}
IBeansProject project = BeansCorePlugin.getModel().getProject(storage.getFile().getProject());
if (project != null) {
return project.getConfig(storage.getFullName());
}
return null;
}
/**
* Returns all {@link IBeansConfigSet} the given {@link IModelElement} belongs to.
* @param element the model element to get the beans config for
* @throws IllegalArgumentException if unsupported model element specified
*/
public static Set<IBeansConfigSet> getConfigSets(IModelElement element) {
Set<IBeansConfigSet> configSets = new LinkedHashSet<IBeansConfigSet>();
if (element instanceof IBeansConfigSet) {
configSets.add((IBeansConfigSet) element);
}
else if (element instanceof IBeansConfig) {
for (IBeansProject beansProject : BeansCorePlugin.getModel().getProjects()) {
Set<IBeansConfigSet> css = beansProject.getConfigSets();
for (IBeansConfigSet cs : css) {
if (cs.getConfigs().contains(element)) {
configSets.add(cs);
}
}
}
}
else if (element instanceof ISourceModelElement) {
IBeansConfig bc = getConfig(element);
for (IBeansProject beansProject : BeansCorePlugin.getModel().getProjects()) {
Set<IBeansConfigSet> css = beansProject.getConfigSets();
for (IBeansConfigSet cs : css) {
if (cs.getConfigs().contains(bc)) {
configSets.add(cs);
}
}
}
}
else {
throw new IllegalArgumentException("Unsupported model element " + element);
}
return configSets;
}
/**
* Returns the {@link IFile} for a given {@link IModelElement}.
*/
public static IFile getFile(IModelElement element) {
if (element instanceof IResourceModelElement) {
IResource resource = ((IResourceModelElement) element).getElementResource();
if (resource instanceof IFile) {
return (IFile) resource;
}
}
return null;
}
/**
* Returns the first constructor argument defined for given bean.
* @param bean the bean to lookup the first constructor argument
* @return the first constructor argument or <code>null</code> if no constructor argument is defined
*/
public static IBeanConstructorArgument getFirstConstructorArgument(IBean bean) {
IBeanConstructorArgument firstCarg = null;
int firstCargStartLine = Integer.MAX_VALUE;
for (IBeanConstructorArgument carg : bean.getConstructorArguments()) {
if (carg.getElementStartLine() < firstCargStartLine) {
firstCarg = carg;
firstCargStartLine = carg.getElementStartLine();
}
}
return firstCarg;
}
/**
* Returns the importing {@link IBeansConfig} from a {@link IImportedBeansConfig}.
* <p>
* <code>null</code> can be returned if the given {@link IBeansConfig} is not imported.
* @since 2.0.4
*/
public static IBeansConfig getImportingBeansConfig(IBeansConfig beansConfig) {
if (beansConfig instanceof IImportedBeansConfig) {
// navigate up the model tree
return (IBeansConfig) beansConfig.getElementParent().getElementParent();
}
return null;
}
/**
* Returns the inner {@link IBean}s of a given {@link IModelElement}.
* @since 2.1.0
*/
public static Set<IBean> getInnerBeans(final IModelElement element, final boolean recursive) {
final Set<IBean> innerBeans = new HashSet<IBean>();
IModelElementVisitor visitor = new IModelElementVisitor() {
public boolean visit(IModelElement visitedElement, IProgressMonitor monitor) {
if (!element.equals(visitedElement) && visitedElement instanceof IBean
&& ((IBean) visitedElement).isInnerBean()) {
innerBeans.add((IBean) visitedElement);
return recursive;
}
return true;
}
};
element.accept(visitor, new NullProgressMonitor());
return innerBeans;
}
/**
* Returns the inner {@link IBean}s of a given {@link IModelElement}.
*/
public static Set<IBean> getInnerBeans(final IModelElement element) {
return getInnerBeans(element, true);
}
/**
* Returns the merged bean definition for a given bean from specified context ( {@link IBeansConfig} or
* {@link IBeansConfigSet}). Any cyclic-references are ignored.
* @param bean the bean the merged bean definition is requested for
* @param context the context ({@link IBeanConfig} or {@link IBeanConfigSet}) the beans are looked-up
* @return given bean's merged bean definition
* @throws IllegalArgumentException if unsupported context specified
*/
public static BeanDefinition getMergedBeanDefinition(IBean bean, IModelElement context) {
BeanDefinition bd = ((Bean) bean).getBeanDefinition();
if (bean.isChildBean()) {
// If no context specified the use the bean's config instead
if (context == null) {
context = BeansModelUtils.getConfig(bean);
}
// Fill a set with all bean definitions belonging to the
// hierarchy of the requested bean definition
List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
// used to detect a cycle
beanDefinitions.add(bd);
addBeanDefinition(bean, context, beanDefinitions);
// Merge the bean definition hierarchy to a single bean
// definition
AbstractBeanDefinition rbd = null;
int bdCount = beanDefinitions.size();
for (int i = bdCount - 1; i >= 0; i--) {
BeanDefinition abd = beanDefinitions.get(i);
if (rbd != null) {
rbd.overrideFrom(abd);
}
else {
if (abd instanceof RootBeanDefinition) {
rbd = new RootBeanDefinition((RootBeanDefinition) abd);
}
else if (abd instanceof GenericBeanDefinition) {
rbd = new GenericBeanDefinition(abd);
}
else {
// root of hierarchy is not a root bean definition
break;
}
}
}
if (rbd != null) {
return rbd;
}
}
return bd;
}
public static IModelElement getModelElement(Element element, IModelElement context) {
Node parent = element.getParentNode();
if (BeansTags.isTag(element, Tag.BEAN) && BeansTags.isTag(parent, Tag.BEANS)) {
String beanName = getBeanName(element);
if (beanName != null) {
return BeansModelUtils.getBean(beanName, context);
}
}
else if (BeansTags.isTag(element, Tag.PROPERTY) && BeansTags.isTag(parent, Tag.BEAN)
&& BeansTags.isTag(parent.getParentNode(), Tag.BEANS)) {
String beanName = getBeanName((Element) parent);
if (beanName != null) {
IBean bean = BeansModelUtils.getBean(beanName, context);
if (bean != null) {
Node nameAttribute = element.getAttributeNode("name");
if (nameAttribute != null && nameAttribute.getNodeValue() != null) {
return bean.getProperty(nameAttribute.getNodeValue());
}
return bean;
}
}
}
return null;
}
/**
* Returns the most specific {@link IModelElement} that corresponds to the given <code>startLine</code> and
* <code>endLine</code> line numbers.
* <p>
* Client should be aware of possible <code>null</code> returns in case on {@link IModelElement} can be found at the
* given location.
* @since 2.0.1
*/
public static IModelElement getMostSpecificModelElement(int startLine, int endLine, IFile resource,
IProgressMonitor monitor) {
if (BeansCoreUtils.isBeansConfig(resource, true)) {
if (monitor == null) {
monitor = new NullProgressMonitor();
}
IBeansConfig beansConfig = BeansCorePlugin.getModel().getConfig(resource, true);
ModelElementDetermingModelVisitor v = new ModelElementDetermingModelVisitor(startLine, endLine, resource);
beansConfig.accept(v, monitor);
return v.getElement();
}
return null;
}
/**
* Iterates up the model tree to find the first parent element that is of the given <code>parentType</code>.
* @since 2.0.4
*/
@SuppressWarnings("unchecked")
public static <T> T getParentOfClass(IModelElement child, Class<T> parentType) {
if (child != null) {
IModelElement parent = child.getElementParent();
while (parent != null) {
if (parentType.isAssignableFrom(parent.getClass())) {
return (T) parent;
}
parent = parent.getElementParent();
}
}
return null;
}
/**
* Returns the <code>IBeansProject</code> the given model element belongs to.
* @param element the model element to get the beans project for
* @throws IllegalArgumentException if unsupported model element specified
*/
public static IBeansProject getProject(IModelElement element) {
IBeansProject project = getParentOfClass(element, IBeansProject.class);
if (project != null) {
return project;
}
if (element instanceof IResourceModelElement) {
IResource resource = ((IResourceModelElement) element).getElementResource();
if (resource != null) {
project = BeansCorePlugin.getModel().getProject(resource.getProject());
if (project != null) {
return project;
}
}
}
throw new IllegalArgumentException("Unsupported model element " + element);
}
/**
* Returns the corresponding Java set method for given bean property.
* @param property the property to lookup the Java method for
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the beans are looked-up; if
* <code>null</code> then the bean's config is used
* @return the Java method of given bean property or <code>null</code> if no bean class defined or set method not
* found
*/
public static IMethod getPropertyMethod(IBeanProperty property, IModelElement context) {
Assert.notNull(property);
IType type = getBeanType((IBean) property.getElementParent(), context);
if (type != null) {
try {
return Introspector.getWritableProperty(type, property.getElementName());
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
}
return null;
}
/**
* Returns the {@link IResourceModelElement} for a given object.
*/
public static IResourceModelElement getResourceModelElement(Object obj) {
if (obj instanceof IFile) {
return BeansCorePlugin.getModel().getConfig((IFile) obj);
}
else if (obj instanceof IProject) {
return BeansCorePlugin.getModel().getProject((IProject) obj);
}
else if (obj instanceof IAdaptable) {
IResource resource = (IResource) ((IAdaptable) obj).getAdapter(IResource.class);
if (resource instanceof IFile) {
return BeansCorePlugin.getModel().getConfig((IFile) resource);
}
else if (resource instanceof IProject) {
return BeansCorePlugin.getModel().getConfig((IFile) obj);
}
}
return null;
}
/**
* Returns a string representation of the given value object.
*/
public static String getValueName(Object value) {
// Ignore bean name of inner beans
if (value instanceof BeanDefinitionHolder) {
value = ((BeanDefinitionHolder) value).getBeanDefinition();
}
StringBuffer name = new StringBuffer();
if (value instanceof String) {
name.append('"').append(value).append('"');
}
else if (value instanceof BeanDefinition) {
name.append("bean ");
if (value instanceof RootBeanDefinition) {
name.append('[');
name.append(((RootBeanDefinition) value).getBeanClassName());
name.append(']');
}
else {
name.append('<');
name.append(((BeanDefinition) value).getParentName());
name.append('>');
}
}
else if (value instanceof RuntimeBeanReference) {
name.append("reference ");
String beanName = ((RuntimeBeanReference) value).getBeanName();
name.append('<').append(beanName).append(">");
}
else if (value != null) {
String valueName = null;
if (value.getClass().isArray()) {
valueName = "[" + StringUtils.arrayToDelimitedString((Object[]) value, ", ") + "]";
}
else {
valueName = value.toString();
}
if (valueName.length() > 30) {
name.append(valueName.substring(0, 12)).append(" .. ")
.append(valueName.substring(valueName.length() - 13));
}
else {
name.append(valueName);
}
}
else {
name.append("<null>");
}
return name.toString();
}
/**
* Checks if a given <code>className</code> is used as a bean class. The check iterates the complete
* {@link IBeansModel} and not "only" the current {@link IBeansProject}.
* @param className
*/
public static boolean isBeanClass(String className) {
Set<IBeansConfig> beans = BeansCorePlugin.getModel().getConfigs(className);
return beans != null && beans.size() > 0;
}
/**
* Checks if a given <code>type</code> is used as a bean class. The check iterates the complete {@link IBeansModel}
* and not "only" the current {@link IBeansProject}.
* <p>
* The implementation checks if the given <code>type</code> is on the project's classpath.
* @param type
* @since 2.2.1
*/
public static boolean isBeanClass(IType type) {
for (IBeansProject project : BeansCorePlugin.getModel().getProjects()) {
IJavaProject javaProject = JdtUtils.getJavaProject(project.getProject());
if (javaProject != null && javaProject.isOnClasspath(type)) {
for (IBeansConfig config : project.getConfigs()) {
if (config.isBeanClass(type.getFullyQualifiedName())) {
return true;
}
}
}
}
return false;
}
public static boolean isInnerBean(IBean bean) {
return !(bean.getElementParent() instanceof IBeansConfig);
}
/**
* Registers all bean definitions and aliases from given {@link IBeansConfig} in specified
* {@link BeanDefinitionRegistry}. All {@link BeansException}s thrown by the {@link BeanDefinitionRegistry} are
* ignored.
*/
public static void register(IBeansConfigSet configSet, IBeansConfig config, BeanDefinitionRegistry registry) {
// Register bean aliases
for (IBeanAlias alias : config.getAliases()) {
try {
registry.registerAlias(alias.getBeanName(), alias.getElementName());
}
catch (BeansException e) {
// ignore - continue with next alias
}
}
// Register root bean definitions
for (IBean bean : config.getBeans()) {
String beanName = bean.getElementName();
// Register bean definition under primary name
try {
registry.registerBeanDefinition(beanName, ((Bean) bean).getBeanDefinition());
}
catch (BeansException e) {
// ignore - continue with next bean
}
// Register aliases for bean name, if any
String[] aliases = bean.getAliases();
if (aliases != null) {
for (String alias : aliases) {
try {
registry.registerAlias(beanName, alias);
}
catch (BeansException e) {
// ignore - continue with next bean
}
}
}
}
// Register bean definitions from components
registerComponents(configSet, config.getComponents(), registry);
}
public static Object resolveValueIfNecessary(ISourceModelElement parent, Object value) {
if (value instanceof IModelElement) {
return value;
}
else if (value instanceof BeanDefinitionHolder) {
return new Bean(parent, (BeanDefinitionHolder) value);
}
else if (value instanceof BeanDefinition) {
return new Bean(parent, "(inner bean)", null, (BeanDefinition) value);
}
else if (value instanceof RuntimeBeanNameReference) {
return new BeanReference(parent, (RuntimeBeanNameReference) value);
}
else if (value instanceof RuntimeBeanReference) {
return new BeanReference(parent, (RuntimeBeanReference) value);
}
else if (value instanceof ManagedList) {
return new BeansList(parent, (ManagedList<?>) value);
}
else if (value instanceof ManagedSet) {
return new BeansSet(parent, (ManagedSet<?>) value);
}
else if (value instanceof ManagedMap) {
return new BeansMap(parent, (ManagedMap<?, ?>) value);
}
else if (value instanceof ManagedProperties) {
return new BeansProperties(parent, (ManagedProperties) value);
}
else if (value instanceof TypedStringValue) {
return new BeansTypedString(parent, (TypedStringValue) value);
}
else if (value != null && value.getClass().isArray()) {
return new BeansTypedString(parent, "[" + StringUtils.arrayToDelimitedString((Object[]) value, ", ") + "]");
}
return new BeansTypedString(parent, (value != null ? value.toString() : "null"));
}
private static void addBeanDefinition(IBean bean, IModelElement context, List<BeanDefinition> beanDefinitions) {
String parentName = bean.getParentName();
Bean parentBean = (Bean) getBean(parentName, context);
if (parentBean != null) {
BeanDefinition parentBd = parentBean.getBeanDefinition();
// Break cyclic references
if (!parentName.equals(bean.getElementName()) && !beanDefinitions.contains(parentBd)) {
beanDefinitions.add(parentBd);
if (parentBean.isChildBean()) {
addBeanDefinition(parentBean, context, beanDefinitions);
}
}
}
}
/**
* If given target is not equal to source then a {@link BeansConnection} is created. This bean reference is added to
* the given list of bean references (if not already). If given target is not already checked for bean references
* then <code>true</code> is returned else <code>false</code>.
*/
private static boolean addBeanReference(BeanType type, IModelElement source, IBean target, IModelElement context,
Set<BeansConnection> references, Set<IBean> referencedBeans) {
if (target != null && target != source) {
BeansConnection ref = new BeansConnection(type, source, target, context);
if (!references.contains(ref)) {
references.add(ref);
// If given target not checked for references then check it too
if (!referencedBeans.contains(target)) {
return true;
}
}
}
return false;
}
/**
* Adds the all beans which are referenced by the specified bean to the given list as an instance of
* {@link BeansConnection}.
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the referenced beans are
* looked-up
* @param referencedBeans used to break from cycles
*/
private static void addBeanReferencesForBean(IBean element, IModelElement context, boolean recursive,
Set<BeansConnection> references, Set<IBean> referencedBeans) {
if (!referencedBeans.contains(element)) {
// must add this element first to break from cycles
referencedBeans.add(element);
for (BeansConnection ref : getBeanReferences(element, context, recursive, references, referencedBeans)) {
if (!references.contains(ref)) {
references.add(ref);
}
}
}
}
/**
* Adds the all beans which are referenced by the specified {@link IBeansComponent} to the given list as an instance
* of {@link BeansConnection}.
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the referenced beans are
* looked-up
* @param referencedBeans used to break from cycles
*/
private static void addBeanReferencesForBeansComponent(IBeansComponent component, IModelElement context,
boolean recursive, Set<BeansConnection> references, Set<IBean> referencedBeans) {
for (IBean bean : component.getBeans()) {
addBeanReferencesForBean(bean, context, recursive, references, referencedBeans);
}
for (IBeansComponent innerComponent : component.getComponents()) {
addBeanReferencesForBeansComponent(innerComponent, context, recursive, references, referencedBeans);
}
}
/**
* Given a bean property's or constructor argument's value, adds any beans referenced by this value. This value
* could be: <li>An {@link IBeanReference}, which bean will be added. <li>An inner {@link IBean}. This is an inner
* {@link IBean} that may contain {@link IBeanReference}s which will be added too. <li>An {@link IBeansList}. This
* is a collection that may contain {@link IBeanReference}s which will be added. <li>An {@link IBeansSet}. May also
* contain {@link IBeanReference}s that will be added. <li>An {@link IBeansMap}. In this case the value may be a
* {@link IBeanReference} that will be added. <li>An ordinary object or null, in which case it's ignored.
* @param context the context (<code>IBeanConfig</code> or <code>IBeanConfigSet</code>) the referenced beans are
* looked-up
*/
private static void addBeanReferencesForValue(IModelElement element, Object value, IModelElement context,
Set<BeansConnection> references, Set<IBean> referencedBeans, boolean recursive) {
if (value instanceof IBeanReference) {
String beanName = ((IBeanReference) value).getBeanName();
IBean bean = getBean(beanName, context);
if (addBeanReference(BeanType.STANDARD, element, bean, context, references, referencedBeans) && recursive) {
addBeanReferencesForBean(bean, context, recursive, references, referencedBeans);
}
}
else if (value instanceof IBeansList) {
// Add bean property's interceptors
if (element instanceof IBeanProperty && element.getElementName().equals("interceptorNames")) {
IType type = getBeanType((IBean) element.getElementParent(), context);
if (type != null) {
if (type.getFullyQualifiedName().equals("org.springframework.aop.framework.ProxyFactoryBean")) {
for (IModelElement child : ((IBeansList) value).getElementChildren()) {
if (child instanceof IBeansTypedString) {
IBean interceptor = getBean(((IBeansTypedString) child).getString(), context);
if (addBeanReference(BeanType.INTERCEPTOR, element, interceptor, context, references,
referencedBeans) && recursive) {
addBeanReferencesForBean(interceptor, context, recursive, references,
referencedBeans);
}
}
}
}
}
}
else {
for (IModelElement child : ((IBeansList) value).getElementChildren()) {
addBeanReferencesForValue(element, child, context, references, referencedBeans, recursive);
}
}
}
else if (value instanceof IBeansSet) {
for (IModelElement child : ((IBeansSet) value).getElementChildren()) {
addBeanReferencesForValue(element, child, context, references, referencedBeans, recursive);
}
}
else if (value instanceof IBeansMap) {
for (IModelElement child : ((IBeansMap) value).getElementChildren()) {
if (child instanceof IBeansMapEntry) {
addBeanReferencesForValue(element, ((IBeansMapEntry) child).getKey(), context, references,
referencedBeans, recursive);
addBeanReferencesForValue(element, ((IBeansMapEntry) child).getValue(), context, references,
referencedBeans, recursive);
}
}
}
}
private static String getBeanName(Element element) {
Node idAttribute = element.getAttributeNode("id");
if (idAttribute != null && idAttribute.getNodeValue() != null) {
return idAttribute.getNodeValue();
}
Node nameAttribute = element.getAttributeNode("name");
if (nameAttribute != null && nameAttribute.getNodeValue() != null) {
return nameAttribute.getNodeValue();
}
return null;
}
private static Set<BeansConnection> getBeanReferences(IModelElement element, IModelElement context,
boolean recursive, Set<BeansConnection> references, Set<IBean> referencedBeans) {
if (element instanceof IBeansComponent) {
addBeanReferencesForBeansComponent((IBeansComponent) element, context, recursive, references,
referencedBeans);
}
else if (element instanceof Bean) {
// Add referenced beans from bean element
Bean bean = (Bean) element;
// For a child bean add the parent bean
if (bean.isChildBean()) {
IBean parentBean = getBean(bean.getParentName(), context);
if (addBeanReference(BeanType.PARENT, bean, parentBean, context, references, referencedBeans)
&& recursive) {
// Now add all parent beans and all beans which are
// referenced by the parent beans
// The HashSet is used to detect a cycle
Set<String> beanNames = new HashSet<String>();
beanNames.add(bean.getElementName());
beanNames.add(parentBean.getElementName());
while (parentBean != null && parentBean.isChildBean()) {
String parentName = parentBean.getParentName();
if (beanNames.contains(parentName)) {
// break from cycle
break;
}
beanNames.add(parentName);
parentBean = getBean(parentName, context);
if (addBeanReference(BeanType.PARENT, bean, parentBean, context, references, referencedBeans)
&& recursive) {
addBeanReferencesForBean(parentBean, context, recursive, references, referencedBeans);
}
}
}
}
// Get bean's merged or standard bean definition
AbstractBeanDefinition bd;
if (recursive) {
bd = (AbstractBeanDefinition) getMergedBeanDefinition(bean, context);
}
else {
bd = (AbstractBeanDefinition) (bean).getBeanDefinition();
}
// Add bean's factoy bean
if (bd.getFactoryBeanName() != null) {
IBean factoryBean = getBean(bd.getFactoryBeanName(), context);
if (addBeanReference(BeanType.FACTORY, bean, factoryBean, context, references, referencedBeans)
&& recursive) {
addBeanReferencesForBean(factoryBean, context, recursive, references, referencedBeans);
}
}
// Add bean's depends-on beans
if (bd.getDependsOn() != null) {
for (String dependsOnBeanId : bd.getDependsOn()) {
IBean dependsOnBean = getBean(dependsOnBeanId, context);
if (addBeanReference(BeanType.DEPENDS_ON, bean, dependsOnBean, context, references, referencedBeans)
&& recursive) {
addBeanReferencesForBean(dependsOnBean, context, recursive, references, referencedBeans);
}
}
}
// Add beans from bean's MethodOverrides
if (!bd.getMethodOverrides().isEmpty()) {
for (Object methodOverride : bd.getMethodOverrides().getOverrides()) {
if (methodOverride instanceof LookupOverride) {
String beanName = ((LookupOverride) methodOverride).getBeanName();
IBean overrideBean = getBean(beanName, context);
if (addBeanReference(BeanType.METHOD_OVERRIDE, bean, overrideBean, context, references,
referencedBeans) && recursive) {
addBeanReferencesForBean(overrideBean, context, recursive, references, referencedBeans);
}
}
else if (methodOverride instanceof ReplaceOverride) {
String beanName = ((ReplaceOverride) methodOverride).getMethodReplacerBeanName();
IBean overrideBean = getBean(beanName, context);
if (addBeanReference(BeanType.METHOD_OVERRIDE, bean, overrideBean, context, references,
referencedBeans) && recursive) {
addBeanReferencesForBean(overrideBean, context, recursive, references, referencedBeans);
}
}
}
}
// Add beans referenced from bean's constructor arguments
for (IBeanConstructorArgument carg : bean.getConstructorArguments()) {
addBeanReferencesForValue(carg, carg.getValue(), context, references, referencedBeans, recursive);
}
// Add referenced beans from bean's properties
for (IBeanProperty property : bean.getProperties()) {
addBeanReferencesForValue(property, property.getValue(), context, references, referencedBeans,
recursive);
}
// Add referenced beans from bean annotations contributed into the bean meta data model
// for (IBeanProperty property : BeansCorePlugin.getMetadataModel().getBeanProperties(bean)) {
// addBeanReferencesForValue(property, property.getValue(), context, references, referencedBeans,
// recursive);
// }
// Add references from inner beans
for (IBean nestedBean : getInnerBeans(bean, false)) {
Set<BeansConnection> nestedConnections = getBeanReferences(nestedBean, context, false);
for (BeansConnection nestedConnection : nestedConnections) {
references.add(new BeansConnection(nestedConnection.getType(), bean, nestedConnection.getTarget(),
true));
}
}
}
else if (element instanceof IBeanConstructorArgument) {
// Add referenced beans from constructor arguments element
IBeanConstructorArgument carg = (IBeanConstructorArgument) element;
addBeanReferencesForValue(carg, carg.getValue(), context, references, referencedBeans, recursive);
}
else if (element instanceof IBeanProperty) {
// Add referenced beans from property element
IBeanProperty property = (IBeanProperty) element;
addBeanReferencesForValue(property, property.getValue(), context, references, referencedBeans, recursive);
}
else {
throw new IllegalArgumentException("Unsupported model element " + element);
}
return references;
}
/**
* Registers all {@link IBean}s and {@link IBeansComponent}s that are nested within the given
* <code>components</code>.
*/
private static void registerComponents(IBeansConfigSet configSet, Set<IBeansComponent> components,
BeanDefinitionRegistry registry) {
for (IBeansComponent component : components) {
if (configSet != null && component instanceof IProfileAwareBeansComponent) {
IProfileAwareBeansComponent profileAwareBeansComponent = (IProfileAwareBeansComponent) component;
if (profileAwareBeansComponent.getProfiles().size() != 0
&& !CollectionUtils.containsAny(profileAwareBeansComponent.getProfiles(),
configSet.getProfiles())) {
continue;
}
}
for (IBean bean : component.getBeans()) {
try {
String beanName = bean.getElementName();
// Register bean definition under primary name.
registry.registerBeanDefinition(beanName, ((Bean) bean).getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = bean.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
catch (BeansException e) {
// ignore - continue with next bean
}
}
// Register bean definitions from components
registerComponents(configSet, component.getComponents(), registry);
}
}
/**
* Returns a list of all configs which contain a bean that uses a bean class that is part of the java structure
* represented by the given <code>resource</code>.
* <p>
* This implementation considers <b>all</b> inner classes as potential bean classes as well.
* @since 2.0.5
*/
public static Set<IBeansConfig> getConfigsByContainingTypes(IResource resource, TypeHierarchyEngine typeEngine, IProgressMonitor monitor) {
if (System.getProperty(TypeHierarchyEngine.ENABLE_PROPERTY, "true").equals("true")) {
return getConfigsByContainingTypesUsingTypeHierarchyEngine(resource, typeEngine, monitor);
}
return getConfigsByContainingTypesJDT(resource, monitor);
}
protected static Set<IBeansConfig> getConfigsByContainingTypesUsingTypeHierarchyEngine(IResource resource, TypeHierarchyEngine typeEngine, IProgressMonitor monitor) {
Set<IBeansConfig> files = new LinkedHashSet<IBeansConfig>();
if (resource != null && resource.isAccessible() && resource.isSynchronized(IResource.DEPTH_ZERO)
&& resource.getName().endsWith(".java")) {
Set<IBeansProject> projects = BeansCorePlugin.getModel().getProjects();
if (projects != null) {
IJavaElement element = JavaCore.create(resource);
if (element instanceof ICompilationUnit && element.getJavaProject().isOnClasspath(element)) {
try {
IType[] types = ((ICompilationUnit) element).getAllTypes();
String[] changedTypeNames = new String[types.length];
boolean[] changedTypeIsInterface = new boolean[types.length];
for (int i = 0; i < types.length; i++) {
changedTypeNames[i] = types[i].getFullyQualifiedName();
changedTypeIsInterface[i] = types[i].isInterface();
}
for (IBeansProject project : projects) {
if (project != null) {
// don't look at projects that do not have the java element on their classpath
if (JdtUtils.isJavaProject(project.getProject()) && !JdtUtils.getJavaProject(project.getProject()).isOnClasspath(element)) {
continue;
}
Set<IBeansConfig> configs = project.getConfigs();
for (IBeansConfig config : configs) {
boolean configAdded = false;
Set<String> allBeanClasses = config.getBeanClasses();
for (int i = 0; i < changedTypeNames.length; i++) {
for (String className : allBeanClasses) {
if (changedTypeIsInterface[i] && typeEngine.doesImplement(className, changedTypeNames[i], project.getProject())) {
files.add(config);
configAdded = true;
break;
}
else if (!changedTypeIsInterface[i] && typeEngine.doesExtend(className, changedTypeNames[i], project.getProject())) {
files.add(config);
configAdded = true;
break;
}
}
if (configAdded) break;
}
}
// typeHierarchyEngine.cleanup(project.getProject());
}
}
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
}
}
}
return files;
}
protected static Set<IBeansConfig> getConfigsByContainingTypesJDT(IResource resource, IProgressMonitor monitor) {
Set<IBeansConfig> files = new LinkedHashSet<IBeansConfig>();
if (resource != null && resource.isAccessible() && resource.isSynchronized(IResource.DEPTH_ZERO)
&& resource.getName().endsWith(".java")) {
Set<IBeansProject> projects = BeansCorePlugin.getModel().getProjects();
if (projects != null) {
IJavaElement element = JavaCore.create(resource);
if (element instanceof ICompilationUnit && element.getJavaProject().isOnClasspath(element)) {
try {
IType[] types = ((ICompilationUnit) element).getAllTypes();
Set<List<IType>> hierachies = new HashSet<List<IType>>();
List<IType> relevantTypes = Arrays.asList(types);
for (IType type : types) {
IType[] subTypes = SuperTypeHierarchyCache.getTypeHierarchy(type, monitor).getAllSubtypes(
type);
if (subTypes != null && subTypes.length > 0) {
hierachies.add(Arrays.asList(subTypes));
}
}
for (IBeansProject project : projects) {
if (project != null) {
Set<IBeansConfig> configs = project.getConfigs();
for (IBeansConfig config : configs) {
Set<String> allBeanClasses = config.getBeanClasses();
for (String className : allBeanClasses) {
IType type = JdtUtils.getJavaType(project.getProject(), className);
if (type != null) {
// 1. check if the bean class is clear match
if (relevantTypes.contains(type)) {
files.add(config);
}
else {
for (List<IType> subTypes : hierachies) {
if (subTypes.contains(type)) {
files.add(config);
break;
}
}
}
// 3. break the for loop if file is already in
if (files.contains(config)) {
break;
}
}
}
}
}
}
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
}
}
}
return files;
}
/**
* Returns a list of all beans which use a bean class that is part of the java structure represented by the given
* <code>resource</code>.
* <p>
* This implementation considers <b>all</b> inner classes as potential bean classes as well.
* @since 2.0.5
*/
public static Set<IBean> getBeansByContainingTypes(IResource resource, TypeHierarchyEngine typeEngine, IProgressMonitor monitor) {
if (System.getProperty(TypeHierarchyEngine.ENABLE_PROPERTY, "true").equals("true")) {
return getBeansByContainingTypesUsingTypeHierarchyEngine(resource, typeEngine, monitor);
}
return getBeansByContainingTypesJDT(resource, monitor);
}
protected static Set<IBean> getBeansByContainingTypesUsingTypeHierarchyEngine(IResource resource, TypeHierarchyEngine typeEngine, IProgressMonitor monitor) {
Set<IBean> files = new LinkedHashSet<IBean>();
if (resource != null && resource.isAccessible() && resource.isSynchronized(IResource.DEPTH_ZERO)
&& resource.getName().endsWith(".java")) {
Set<IBeansProject> projects = BeansCorePlugin.getModel().getProjects();
if (projects != null) {
IJavaElement element = JavaCore.create(resource);
if (element instanceof ICompilationUnit && element.getJavaProject().isOnClasspath(element)) {
try {
IType[] types = ((ICompilationUnit) element).getAllTypes();
String[] changedTypeNames = new String[types.length];
boolean[] changedTypeIsInterface = new boolean[types.length];
for (int i = 0; i < types.length; i++) {
changedTypeNames[i] = types[i].getFullyQualifiedName();
changedTypeIsInterface[i] = types[i].isInterface();
}
for (IBeansProject project : projects) {
if (project != null) {
// don't look at projects that do not have the java element on their classpath
if (JdtUtils.isJavaProject(project.getProject()) && !JdtUtils.getJavaProject(project.getProject()).isOnClasspath(element)) {
continue;
}
Set<IBeansConfig> configs = project.getConfigs();
for (IBeansConfig config : configs) {
Set<IBean> allBeans = getBeans(config);
for (IBean bean : allBeans) {
String className = resolveBeanTypeAsString(bean);
if (className != null) {
for (int i = 0; i < changedTypeNames.length; i++) {
if (changedTypeIsInterface[i] && typeEngine.doesImplement(className, changedTypeNames[i], project.getProject())) {
files.add(bean);
break;
}
else if (!changedTypeIsInterface[i] && typeEngine.doesExtend(className, changedTypeNames[i], project.getProject())) {
files.add(bean);
break;
}
}
}
else {
// We can't determine the beans type so don't be cleverer as we can and let
// it be processed again
// One last check before adding too much that is not even on the resource's
// classpath
if (project != null
&& JdtUtils.isJavaProject(project.getProject())
&& JdtUtils.getJavaProject(project.getProject()).isOnClasspath(
resource)) {
files.add(bean);
}
}
}
}
// typeHierarchyEngine.cleanup(project.getProject());
}
}
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
}
}
}
return files;
}
protected static Set<IBean> getBeansByContainingTypesJDT(IResource resource, IProgressMonitor monitor) {
Set<IBean> files = new LinkedHashSet<IBean>();
if (resource != null && resource.isAccessible() && resource.isSynchronized(IResource.DEPTH_ZERO)
&& resource.getName().endsWith(".java")) {
Set<IBeansProject> projects = BeansCorePlugin.getModel().getProjects();
if (projects != null) {
IJavaElement element = JavaCore.create(resource);
if (element instanceof ICompilationUnit && element.getJavaProject().isOnClasspath(element)) {
try {
IType[] types = ((ICompilationUnit) element).getAllTypes();
Set<List<IType>> hierachies = new HashSet<List<IType>>();
List<IType> relevantTypes = Arrays.asList(types);
for (IType type : types) {
IType[] subTypes = SuperTypeHierarchyCache.getTypeHierarchy(type, monitor).getAllSubtypes(
type);
if (subTypes != null && subTypes.length > 0) {
hierachies.add(Arrays.asList(subTypes));
}
}
for (IBeansProject project : projects) {
if (project != null) {
Set<IBeansConfig> configs = project.getConfigs();
for (IBeansConfig config : configs) {
Set<IBean> allBeans = getBeans(config);
for (IBean bean : allBeans) {
IType type = resolveBeanType(bean);
if (type != null) {
if (relevantTypes.contains(type)) {
files.add(bean);
}
else {
for (List<IType> subTypes : hierachies) {
if (subTypes.contains(type)) {
files.add(bean);
break;
}
}
}
}
else {
// We can't determine the beans type so don't be cleverer as we can and let
// it be processed again
// One last check before adding too much that is not even on the resource's
// classpath
if (project != null
&& JdtUtils.isJavaProject(project.getProject())
&& JdtUtils.getJavaProject(project.getProject()).isOnClasspath(
resource)) {
files.add(bean);
}
}
}
}
}
}
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
}
}
}
return files;
}
/**
* Resolves the {@link IBean} bean class by looking at parent, factory-bean and factory-method.
*/
public static IType resolveBeanType(IBean bean) {
AbstractBeanDefinition mergedBd = (AbstractBeanDefinition) BeansModelUtils.getMergedBeanDefinition(bean, null);
String mergedClassName = mergedBd.getBeanClassName();
return extractBeanClass(mergedBd, bean, mergedClassName, getParentOfClass(bean, IBeansConfig.class));
}
/**
* Extracts the {@link IType} of a bean definition.
* <p>
* Honors <code>factory-method</code>s and <code>factory-bean</code>.
*/
private static IType extractBeanClass(BeanDefinition bd, IBean bean, String mergedClassName,
IBeansConfig beansConfig) {
IType type = JdtUtils.getJavaType(BeansModelUtils.getProject(bean).getProject(), mergedClassName);
// 1. factory-method on bean
if (bd.getFactoryMethodName() != null && bd.getFactoryBeanName() == null) {
type = extractTypeFromFactoryMethod(bd, type);
}
// 2. factory-method on factory-bean
else if (bd.getFactoryMethodName() != null && bd.getFactoryBeanName() != null) {
try {
IBean factoryB = getBeanWithConfigSets(bd.getFactoryBeanName(), beansConfig);
if (factoryB != null) {
BeanDefinition factoryBd = BeansModelUtils.getMergedBeanDefinition(factoryB, null);
IType factoryBeanType = extractBeanClass(factoryBd, bean, factoryBd.getBeanClassName(), beansConfig);
if (factoryBeanType != null) {
type = extractTypeFromFactoryMethod(bd, factoryBeanType);
}
}
}
catch (NoSuchBeanDefinitionException e) {
}
}
return type;
}
/**
* Resolves the {@link IBean} bean class by looking at parent, factory-bean and factory-method.
*/
public static String resolveBeanTypeAsString(IBean bean) {
AbstractBeanDefinition mergedBd = (AbstractBeanDefinition) BeansModelUtils.getMergedBeanDefinition(bean, null);
String mergedClassName = mergedBd.getBeanClassName();
return extractBeanClassAsString(mergedBd, bean, mergedClassName, getParentOfClass(bean, IBeansConfig.class));
}
/**
* Extracts the {@link IType} of a bean definition.
* <p>
* Honors <code>factory-method</code>s and <code>factory-bean</code>.
*/
private static String extractBeanClassAsString(BeanDefinition bd, IBean bean, String mergedClassName,
IBeansConfig beansConfig) {
String result = mergedClassName;
// 1. factory-method on bean
if (bd.getFactoryMethodName() != null && bd.getFactoryBeanName() == null) {
IType type = JdtUtils.getJavaType(BeansModelUtils.getProject(bean).getProject(), mergedClassName);
result = extractTypeFromFactoryMethodAsString(bd, type);
if (result == null) {
result = mergedClassName;
}
}
// 2. factory-method on factory-bean
else if (bd.getFactoryMethodName() != null && bd.getFactoryBeanName() != null) {
try {
IBean factoryB = getBeanWithConfigSets(bd.getFactoryBeanName(), beansConfig);
if (factoryB != null) {
BeanDefinition factoryBd = BeansModelUtils.getMergedBeanDefinition(factoryB, null);
String factoryBeanTypeName = extractBeanClassAsString(factoryBd, bean, factoryBd.getBeanClassName(), beansConfig);
if (factoryBeanTypeName != null) {
IType factoryBeanType = JdtUtils.getJavaType(BeansModelUtils.getProject(bean).getProject(), factoryBeanTypeName);
result = extractTypeFromFactoryMethodAsString(bd, factoryBeanType);
if (result == null) {
result = factoryBeanTypeName;
}
}
}
}
catch (NoSuchBeanDefinitionException e) {
}
}
return result;
}
/**
* Extracts the {@link IType} of a {@link BeanDefinition} by only looking at the <code>
* factory-method</code> . The passed in {@link IType} <b>must</b> be the bean class or the resolved type of the
* factory bean in use.
*/
private static String extractTypeFromFactoryMethodAsString(BeanDefinition bd, IType type) {
String factoryMethod = bd.getFactoryMethodName();
try {
int argCount = (!bd.isAbstract() ? bd.getConstructorArgumentValues().getArgumentCount() : -1);
Set<IMethod> methods = Introspector.getAllMethods(type);
for (IMethod method : methods) {
if (factoryMethod.equals(method.getElementName()) && method.getParameterNames().length == argCount) {
return JdtUtils.resolveClassNameBySignature(method.getReturnType(), type);
}
}
}
catch (JavaModelException e) {
}
return null;
}
/**
* Extracts the {@link IType} of a {@link BeanDefinition} by only looking at the <code>
* factory-method</code> . The passed in {@link IType} <b>must</b> be the bean class or the resolved type of the
* factory bean in use.
*/
private static IType extractTypeFromFactoryMethod(BeanDefinition bd, IType type) {
String factoryMethod = bd.getFactoryMethodName();
try {
int argCount = (!bd.isAbstract() ? bd.getConstructorArgumentValues().getArgumentCount() : -1);
Set<IMethod> methods = Introspector.getAllMethods(type);
for (IMethod method : methods) {
if (factoryMethod.equals(method.getElementName()) && method.getParameterNames().length == argCount) {
type = JdtUtils.getJavaTypeFromSignatureClassName(method.getReturnType(), type);
break;
}
}
}
catch (JavaModelException e) {
}
return type;
}
/**
* A {@link IModelElementVisitor} that tries to determine the closest {@link IModelElement} by looking at
* <code>startLine</code> and <code>endLine</code> information.
* @author Christian Dupuis
*/
private static class ModelElementDetermingModelVisitor implements IModelElementVisitor {
private int startLine;
private int endLine;
private final IFile file;
private IModelElement element;
public ModelElementDetermingModelVisitor(final int startLine, final int endLine, final IFile file) {
if (startLine + 1 == endLine) {
this.startLine = startLine + 1;
}
else {
this.startLine = startLine;
}
this.endLine = endLine;
this.file = file;
}
public IModelElement getElement() {
return element;
}
public boolean visit(IModelElement element, IProgressMonitor monitor) {
if (element instanceof ISourceModelElement) {
ISourceModelElement sourceElement = (ISourceModelElement) element;
if (sourceElement.getElementResource().equals(file)
&& (sourceElement.getElementStartLine() <= startLine || sourceElement.getElementStartLine() - 1 <= startLine)
&& endLine <= sourceElement.getElementEndLine()) {
this.element = element;
if (sourceElement.getElementStartLine() == startLine
&& endLine == sourceElement.getElementEndLine()) {
startLine = -1;
endLine = -1;
return false;
}
return true;
}
return false;
}
else if (element instanceof IBeansConfig) {
return true;
}
else {
return false;
}
}
}
}