/******************************************************************************* * Copyright (c) 2004, 2015 Spring IDE Developers and others * 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 * GoPivotal, Inc. - performance optimizations *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IType; import org.springframework.beans.BeanMetadataAttribute; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MethodOverrides; import org.springframework.beans.factory.support.ReplaceOverride; import org.springframework.context.annotation.ScannedGenericBeanDefinition; import org.springframework.core.io.ResourceLoader; import org.springframework.ide.eclipse.beans.core.internal.model.BeansConfig.InternalScannedGenericBeanDefinition; import org.springframework.ide.eclipse.beans.core.model.IBean; import org.springframework.ide.eclipse.beans.core.model.IBeanConstructorArgument; import org.springframework.ide.eclipse.beans.core.model.IBeanMethodOverride; import org.springframework.ide.eclipse.beans.core.model.IBeanProperty; 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.IBeansModelElementTypes; import org.springframework.ide.eclipse.core.java.JdtUtils; import org.springframework.ide.eclipse.core.model.IModelElement; import org.springframework.ide.eclipse.core.model.IModelElementVisitor; import org.springframework.util.ObjectUtils; /** * This class holds the data for a Spring bean. * @author Torsten Juergeleit * @author Christian Dupuis * @author Martin Lippert */ public class Bean extends AbstractBeansModelElement implements IBean { private BeanDefinition definition; private String[] aliases; private Set<IBeanConstructorArgument> constructorArguments; private Set<IBeanMethodOverride> methodOverrides; private Map<String, IBeanProperty> properties; private Integer hashCode = null; private Boolean isFactory = null; public Bean(IModelElement parent, BeanDefinitionHolder bdHolder) { this(parent, bdHolder.getBeanName(), bdHolder.getAliases(), bdHolder.getBeanDefinition()); } public Bean(IModelElement parent, String name, String[] aliases, BeanDefinition definition) { super(parent, name, definition); if (definition instanceof ScannedGenericBeanDefinition) { this.definition = new InternalScannedGenericBeanDefinition((ScannedGenericBeanDefinition) definition); } else { this.definition = definition; } this.aliases = aliases; // Clean out references to resource resolvers ConstructorArgumentValues cargValues = definition.getConstructorArgumentValues(); for (Object cargValue : cargValues.getGenericArgumentValues()) { cleanValueHolder((ValueHolder) cargValue); } Map<?, ?> indexedCargValues = cargValues.getIndexedArgumentValues(); for (Object key : indexedCargValues.keySet()) { cleanValueHolder((ValueHolder) indexedCargValues.get(key)); } } public int getElementType() { return IBeansModelElementTypes.BEAN_TYPE; } /** * For inner beans we have to omit the element name because it consists of volatile stuff, like object ids. */ @Override protected String getUniqueElementName() { if (isInnerBean()) { return "" + ID_SEPARATOR + getElementStartLine(); } return super.getUniqueElementName(); } @Override public IResource getElementResource() { // We need to make sure that the beans resource comes back if (getElementSourceLocation() != null && getElementSourceLocation().getResource() instanceof IAdaptable) { IResource resource = (IResource) ((IAdaptable) getElementSourceLocation().getResource()) .getAdapter(IResource.class); if (resource != null) { return resource; } } return super.getElementResource(); } @Override public IModelElement[] getElementChildren() { Set<IModelElement> children = new LinkedHashSet<IModelElement>(getConstructorArguments()); children.addAll(getMethodOverrides()); children.addAll(getProperties()); return children.toArray(new IModelElement[children.size()]); } @Override public void accept(IModelElementVisitor visitor, IProgressMonitor monitor) { // First visit this bean if (!monitor.isCanceled() && visitor.visit(this, monitor)) { // Now ask this beans's constructor arguments for (IBeanConstructorArgument carg : getConstructorArguments()) { carg.accept(visitor, monitor); if (monitor.isCanceled()) { return; } } // Now ask this bean's method overrides for (IBeanMethodOverride mo : getMethodOverrides()) { mo.accept(visitor, monitor); if (monitor.isCanceled()) { return; } } // Finally ask this beans's properties for (IBeanProperty property : getProperties()) { property.accept(visitor, monitor); if (monitor.isCanceled()) { return; } } } } public BeanDefinition getBeanDefinition() { return definition; } public String[] getAliases() { return aliases; } public Set<IBeanConstructorArgument> getConstructorArguments() { if (constructorArguments == null) { initBean(); } return constructorArguments; } public Set<IBeanMethodOverride> getMethodOverrides() { if (methodOverrides == null) { initBean(); } return methodOverrides; } public IBeanProperty getProperty(String name) { if (name != null) { if (properties == null) { initBean(); } return properties.get(name); } return null; } public Collection<IBeanProperty> getProperties() { if (properties == null) { initBean(); } return Collections.unmodifiableCollection(properties.values()); } public String getClassName() { if (definition instanceof AbstractBeanDefinition) { return ((AbstractBeanDefinition) definition).getBeanClassName(); } return null; } public String getParentName() { return definition.getParentName(); } public boolean isRootBean() { return !isChildBean(); // (definition instanceof RootBeanDefinition || definition instanceof // GenericBeanDefinition); } public boolean isChildBean() { return definition.getParentName() != null; } public boolean isInnerBean() { IModelElement parent = getElementParent(); return !(parent instanceof IBeansConfig || parent instanceof IBeansComponent); } public boolean isSingleton() { if (definition instanceof AbstractBeanDefinition) { AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definition; return beanDefinition.getScope() == null || AbstractBeanDefinition.SCOPE_SINGLETON.equals(beanDefinition.getScope()) || beanDefinition.isSingleton(); } return true; } public boolean isAbstract() { if (definition instanceof AbstractBeanDefinition) { return ((AbstractBeanDefinition) definition).isAbstract(); } return false; } public boolean isLazyInit() { if (definition instanceof AbstractBeanDefinition) { return ((AbstractBeanDefinition) definition).isLazyInit(); } return true; } public boolean isInfrastructure() { if (definition instanceof AbstractBeanDefinition) { return ((AbstractBeanDefinition) definition).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE; } return false; } public boolean isGeneratedElementName() { if (definition instanceof AbstractBeanDefinition) { BeanMetadataAttribute attribute = ((AbstractBeanDefinition) definition) .getMetadataAttribute(UniqueBeanNameGenerator.GENERATED_BEAN_NAME_PROPERTY); if (attribute != null && attribute.getValue() instanceof Boolean) { return ((Boolean) attribute.getValue()).booleanValue(); } } return false; } public synchronized boolean isFactory() { if (isFactory == null && definition instanceof AbstractBeanDefinition) { isFactory = Boolean.FALSE; AbstractBeanDefinition bd = (AbstractBeanDefinition) definition; if (bd.getFactoryBeanName() != null) { isFactory = Boolean.TRUE; } else if (isRootBean() && bd.getFactoryMethodName() != null) { isFactory = Boolean.TRUE; } else { IType type = BeansModelUtils.getBeanType(this, null); if (type != null) { isFactory = JdtUtils.doesImplement(getElementResource(), type, FactoryBean.class.getName()); } } } return isFactory; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof Bean)) { return false; } Bean that = (Bean) other; if (!ObjectUtils.nullSafeEquals(this.definition, that.definition)) return false; if (!ObjectUtils.nullSafeEquals(this.aliases, that.aliases)) return false; // if (!ObjectUtils.nullSafeEquals(getElementParent(), that.getElementParent())) // return false; return super.equals(other); } @Override public int hashCode() { // need to cache hashCode as the value could change over time due to the mutable nature of // hashCode of source location if (hashCode == null) { hashCode = ObjectUtils.nullSafeHashCode(definition); hashCode = getElementType() * hashCode + ObjectUtils.nullSafeHashCode(aliases); // hashCode = getElementType() * hashCode + ObjectUtils.nullSafeHashCode(getElementParent()); hashCode = getElementType() * super.hashCode(); } return hashCode; } @Override public String toString() { StringBuffer text = new StringBuffer(super.toString()); if (getClassName() != null) { text.append(" ["); text.append(getClassName()); text.append(']'); } else if (getParentName() != null) { text.append(" <"); text.append(getParentName()); text.append('>'); } return text.toString(); } /** * Lazily initialize this bean's data (constructor arguments, properties and inner beans). */ private void initBean() { synchronized(this) { // Retrieve this bean's constructor arguments constructorArguments = new LinkedHashSet<IBeanConstructorArgument>(); ConstructorArgumentValues cargValues = definition.getConstructorArgumentValues(); for (Object cargValue : cargValues.getGenericArgumentValues()) { IBeanConstructorArgument carg = new BeanConstructorArgument(this, (ValueHolder) cargValue); constructorArguments.add(carg); } Map<?, ?> indexedCargValues = cargValues.getIndexedArgumentValues(); for (Object key : indexedCargValues.keySet()) { ValueHolder vHolder = (ValueHolder) indexedCargValues.get(key); IBeanConstructorArgument carg = new BeanConstructorArgument(this, ((Integer) key).intValue(), vHolder); constructorArguments.add(carg); } // Retrieve this bean's properties properties = new LinkedHashMap<String, IBeanProperty>(); for (PropertyValue propValue : definition.getPropertyValues().getPropertyValues()) { IBeanProperty property = new BeanProperty(this, propValue); properties.put(property.getElementName(), property); } // Retrieve this bean's method overrides if (definition instanceof AbstractBeanDefinition) { methodOverrides = new LinkedHashSet<IBeanMethodOverride>(); MethodOverrides mos = ((AbstractBeanDefinition) definition).getMethodOverrides(); if (mos != null) { for (Object mo : mos.getOverrides()) { if (mo instanceof LookupOverride) { methodOverrides.add(new BeanLookupMethodOverride(this, (LookupOverride) mo)); } else if (mo instanceof ReplaceOverride) { methodOverrides.add(new BeanReplaceMethodOverride(this, (ReplaceOverride) mo)); } } } } } } /** * Cleans out references to ClassLoaders */ private ValueHolder cleanValueHolder(ValueHolder vHolder) { if (vHolder.getValue() instanceof ResourceLoader) { vHolder.setValue(vHolder.getValue().getClass().getName()); } return vHolder; } }