/*******************************************************************************
* Copyright (c) 2004, 2011 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.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.ui.IPersistableElement;
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.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.IBeansModelElementTypes;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.core.model.AbstractResourceModelElement;
import org.springframework.ide.eclipse.core.model.IModelElement;
/**
* This class defines a Spring beans config set (a list of beans config names).
* @author Torsten Juergeleit
* @author Dave Watkins
* @author Christian Dupuis
*/
public class BeansConfigSet extends AbstractResourceModelElement implements IBeansConfigSet {
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
protected Set<String> configNames;
private volatile boolean allowAliasOverriding;
private volatile boolean allowBeanDefinitionOverriding;
private volatile boolean isIncomplete;
private volatile Map<String, IBeanAlias> aliasesMap;
private volatile boolean isAliasesMapPopulated = false;
private volatile Set<IBeansComponent> components;
private volatile boolean isComponentsPopulated = false;
private volatile Map<String, IBean> beansMap;
private volatile boolean isBeansMapPopulated = false;
private volatile Map<String, Set<IBean>> beanClassesMap;
private volatile boolean isBeanClassesMapPopulated = false;
private volatile Type type;
private volatile Set<String> profiles;
public BeansConfigSet(IBeansProject project, String name, Type type) {
this(project, name, new LinkedHashSet<String>(), type);
}
public BeansConfigSet(IBeansProject project, String name, Set<String> configNames, Type type) {
super(project, name);
this.configNames = new LinkedHashSet<String>(configNames);
allowAliasOverriding = true;
allowBeanDefinitionOverriding = true;
this.type = type;
this.profiles = new LinkedHashSet<String>();
}
/**
* Sets internal maps with <code>IBean</code>s and bean classes to <code>null</code>.
*/
public void reset() {
try {
w.lock();
aliasesMap = null;
isAliasesMapPopulated = false;
components = null;
isComponentsPopulated = false;
beansMap = null;
isBeansMapPopulated = false;
beanClassesMap = null;
isBeanClassesMapPopulated = false;
}
finally {
w.unlock();
}
}
public int getElementType() {
return IBeansModelElementTypes.CONFIG_SET_TYPE;
}
@Override
public IModelElement[] getElementChildren() {
Set<IBeansConfig> children = getConfigs();
return children.toArray(new IModelElement[children.size()]);
}
public IResource getElementResource() {
return ((IBeansProject) getElementParent()).getProject();
}
public boolean isElementArchived() {
return false;
}
public void setAllowAliasOverriding(boolean allowAliasOverriding) {
this.allowAliasOverriding = allowAliasOverriding;
reset();
}
public boolean isAllowAliasOverriding() {
return allowAliasOverriding;
}
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
reset();
}
public boolean isAllowBeanDefinitionOverriding() {
return allowBeanDefinitionOverriding;
}
public void setIncomplete(boolean isIncomplete) {
this.isIncomplete = isIncomplete;
}
public boolean isIncomplete() {
return isIncomplete;
}
public void addConfig(String configName) {
if (configName.length() > 0 && !getConfigNames().contains(configName)) {
try {
w.lock();
configNames.add(configName);
}
finally {
w.unlock();
}
reset();
}
}
public boolean hasConfig(String configName) {
return getConfigNames().contains(configName);
}
public boolean hasConfig(IFile file) {
if (file == null) return false;
if (file.getProject().equals(((IBeansProject) getElementParent()).getProject())) {
return getConfigNames().contains(file.getProjectRelativePath().toString());
}
return getConfigNames().contains(file.getFullPath().toString());
}
public void removeConfig(String configName) {
try {
w.lock();
configNames.remove(configName);
}
finally {
w.unlock();
}
reset();
}
public void removeAllConfigs() {
try {
w.lock();
configNames.clear();
}
finally {
w.unlock();
}
reset();
}
public Set<IBeansConfig> getConfigs() {
Set<IBeansConfig> configs = new LinkedHashSet<IBeansConfig>();
for (String configName : getConfigNames()) {
IBeansConfig config = BeansModelUtils.getConfig(configName, this);
if (config != null) {
configs.add(config);
}
}
return configs;
}
public Set<String> getConfigNames() {
try {
r.lock();
return new LinkedHashSet<String>(configNames);
}
finally {
r.unlock();
}
}
public boolean hasAlias(String name) {
return getAliasesMap().containsKey(name);
}
public IBeanAlias getAlias(String name) {
return getAliasesMap().get(name);
}
public Set<IBeanAlias> getAliases() {
return new LinkedHashSet<IBeanAlias>(getAliasesMap().values());
}
public Set<IBeansComponent> getComponents() {
return new LinkedHashSet<IBeansComponent>(getComponentsList());
}
public boolean hasBean(String name) {
return getBeansMap().containsKey(name);
}
public IBean getBean(String name) {
return getBeansMap().get(name);
}
public Set<IBean> getBeans() {
return new LinkedHashSet<IBean>(getBeansMap().values());
}
public boolean isBeanClass(String className) {
return getBeanClassesMap().containsKey(className);
}
public Set<String> getBeanClasses() {
return new LinkedHashSet<String>(getBeanClassesMap().keySet());
}
public Set<IBean> getBeans(String className) {
if (isBeanClass(className)) {
return new LinkedHashSet<IBean>(getBeanClassesMap().get(className));
}
return new HashSet<IBean>();
}
// TODO CD IDE-1079 commented out to prevent deadlocks
/*
* @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof
* BeansConfigSet)) { return false; } try { r.lock(); BeansConfigSet that = (BeansConfigSet) other; if
* (!ObjectUtils.nullSafeEquals(this.configNames, that.configNames)) return false; if
* (!ObjectUtils.nullSafeEquals(this.allowAliasOverriding, that.allowAliasOverriding)) return false; if
* (!ObjectUtils.nullSafeEquals(this.allowBeanDefinitionOverriding, that.allowBeanDefinitionOverriding)) return
* false; if (!ObjectUtils.nullSafeEquals(this.isIncomplete, that.isIncomplete)) return false; return
* super.equals(other); } finally { r.unlock(); } }
*/
// TODO CD IDE-1079 commented out to prevent deadlocks
/*
* @Override public int hashCode() { try { r.lock(); int hashCode = ObjectUtils.nullSafeHashCode(configNames);
* hashCode = getElementType() * hashCode + ObjectUtils.nullSafeHashCode(allowAliasOverriding); hashCode =
* getElementType() * hashCode + ObjectUtils.nullSafeHashCode(allowBeanDefinitionOverriding); hashCode =
* getElementType() * hashCode + ObjectUtils.nullSafeHashCode(isIncomplete); return getElementType() * hashCode +
* super.hashCode(); } finally { r.unlock(); } }
*/
@Override
public String toString() {
try {
r.lock();
return getElementName() + ": " + configNames.toString();
}
finally {
r.unlock();
}
}
/**
* Returns lazily initialized map with all beans defined in this config set.
*/
private Map<String, IBeanAlias> getAliasesMap() {
if (!this.isAliasesMapPopulated) {
try {
w.lock();
if (this.isAliasesMapPopulated) {
return aliasesMap;
}
aliasesMap = new LinkedHashMap<String, IBeanAlias>();
for (String configName : configNames) {
IBeansConfig config = BeansModelUtils.getConfig(configName, this);
if (config != null) {
for (IBeanAlias alias : config.getAliases()) {
if (allowAliasOverriding || !aliasesMap.containsKey(alias.getElementName())) {
aliasesMap.put(alias.getElementName(), alias);
}
}
}
}
}
finally {
this.isAliasesMapPopulated = true;
w.unlock();
}
}
try {
r.lock();
return aliasesMap;
}
finally {
r.unlock();
}
}
/**
* Returns lazily initialized list with all components defined in this config set.
*/
private Set<IBeansComponent> getComponentsList() {
if (!this.isComponentsPopulated) {
try {
w.lock();
if (this.isComponentsPopulated) {
return components;
}
components = new LinkedHashSet<IBeansComponent>();
for (String configName : configNames) {
IBeansConfig config = BeansModelUtils.getConfig(configName, this);
if (config != null) {
for (IBeansComponent component : config.getComponents()) {
components.add(component);
}
}
}
}
finally {
this.isComponentsPopulated = true;
w.unlock();
}
}
try {
r.lock();
return components;
}
finally {
r.unlock();
}
}
/**
* Returns lazily initialized map with all beans defined in this config set.
*/
private Map<String, IBean> getBeansMap() {
if (!this.isBeansMapPopulated) {
try {
w.lock();
if (this.isBeansMapPopulated) {
return beansMap;
}
beansMap = new LinkedHashMap<String, IBean>();
for (String configName : configNames) {
IBeansConfig config = BeansModelUtils.getConfig(configName, this);
if (config != null) {
for (IBean bean : config.getBeans()) {
if (allowBeanDefinitionOverriding || !beansMap.containsKey(bean.getElementName())) {
beansMap.put(bean.getElementName(), bean);
}
}
}
}
}
finally {
this.isBeansMapPopulated = true;
w.unlock();
}
}
try {
r.lock();
return beansMap;
}
finally {
r.unlock();
}
}
/**
* Returns lazily initialized map with all bean classes used in this config set.
*/
private Map<String, Set<IBean>> getBeanClassesMap() {
if (!this.isBeanClassesMapPopulated) {
try {
w.lock();
if (this.isBeanClassesMapPopulated) {
return beanClassesMap;
}
beanClassesMap = new LinkedHashMap<String, Set<IBean>>();
for (IBean bean : getBeansMap().values()) {
addBeanClassToMap(bean);
for (IBean innerBean : BeansModelUtils.getInnerBeans(bean)) {
addBeanClassToMap(innerBean);
}
}
}
finally {
this.isBeanClassesMapPopulated = true;
w.unlock();
}
}
try {
r.lock();
return beanClassesMap;
}
finally {
r.unlock();
}
}
private void addBeanClassToMap(IBean bean) {
// Get name of bean class - strip name of any inner class
String className = bean.getClassName();
if (className != null) {
int pos = className.indexOf('$');
if (pos > 0) {
className = className.substring(0, pos);
}
// Maintain a list of bean names within every entry in the
// bean class map
Set<IBean> beanClassBeans = beanClassesMap.get(className);
if (beanClassBeans == null) {
beanClassBeans = new LinkedHashSet<IBean>();
beanClassesMap.put(className, beanClassBeans);
}
beanClassBeans.add(bean);
}
}
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(Class adapter) {
if (adapter == IPersistableElement.class) {
return new BeansModelElementToPersistableElementAdapter(this);
}
else if (adapter == IProject.class) {
if (getElementParent() instanceof IBeansProject) {
return ((IBeansProject) getElementParent()).getProject();
}
}
return super.getAdapter(adapter);
}
public Type getType() {
return type;
}
public boolean isExternal() {
return false;
}
public Set<String> getProfiles() {
return this.profiles;
}
public void setProfiles(Set<String> profiles) {
this.profiles = profiles;
}
public void addProfile(String profile) {
this.profiles.add(profile);
}
public boolean hasProfiles() {
return this.profiles != null && this.profiles.size() > 0;
}
}