de.tsl2.nano.bean.def.Bean Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.descriptor Show documentation
Show all versions of tsl2.nano.descriptor Show documentation
TSL2 Framework Descriptor (currency-handling, generic formatter, descriptors for beans, collections, actions and values)
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider
* created on: Oct 24, 2010
*
* Copyright: (c) Thomas Schneider 2010, all rights reserved
*/
package de.tsl2.nano.bean.def;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.text.Format;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import de.tsl2.nano.action.IAction;
import de.tsl2.nano.action.IConstraint;
import de.tsl2.nano.action.IStatus;
import de.tsl2.nano.bean.BeanContainer;
import de.tsl2.nano.bean.BeanUtil;
import de.tsl2.nano.bean.IConnector;
import de.tsl2.nano.bean.IValueAccess;
import de.tsl2.nano.collection.Entry;
import de.tsl2.nano.collection.TimedReferenceMap;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.Messages;
import de.tsl2.nano.core.cls.BeanAttribute;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.cls.IAttribute;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.messaging.IListener;
import de.tsl2.nano.core.util.CollectionUtil;
import de.tsl2.nano.core.util.MapUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
/**
* full bean access with extended attribute definitions, registering observers, attribute validations etc.! uses bean
* reflection base classes like {@link BeanClass} and {@link BeanAttribute}. through the extension interface
* {@link IAttributeDefinition}, the {@link BeanValue} works in the background for handling all defined bean values.
*
* it is possible to create virtual beans - means, you create a bean without base instance through {@link #Bean()} and
* are able to add attributes to that bean through
* {@link #addAttribute(Object, String, int, boolean, String, Object, String, IPresentable)}. this provides the
* possibility to avoid creating new java classes to have the bean definitions.
*
* you are able to define actions for the bean with {@link #addAction(IAction)}.
*
* use:
* - {@link #Bean(Object)} - {@link #setAttributeFilter(String...)}
* - {@link #setAttrDef(String, int, boolean, String, Object, String)}
* - {@link #getAttribute(String)}
* - {@link #getValue(String)}
* - {@link #setValue(String, Object)}
* - {@link #check()}
* - {@link #observe(String, IListener)}
*
* conveniences and utilities:
* - {@link #toValueMap()} - {@link #toValueMap(String, boolean, String...)}
*
* example test code:
*
*
* TypeBean inst1 = new TypeBean();
* TypeBean inst2 = new TypeBean();
* inst1.setObject(inst2);
*
* Bean b1 = new Bean(inst1);
* Bean b2 = new Bean(inst2);
*
* b1.setAttributeFilter("string", "bigDecimal");
* b2.setAttributeFilter("primitiveChar", "immutableInteger");
*
* b1.setAttrDef("string", 5, false, "[A-Z]+", null, null);
*
* b2.connect("primitiveChar", b1.getAttribute("string"), new CommonAction<Object>() {
* public Object action() throws Exception {
* LOG.info("starting connection callback...");
* return null;
* }
* });
*
* b1.observe("string", new de.tsl2.nano.bean.IValueChangeListener() {
* public void handleChange(de.tsl2.nano.bean.ValueChangeEvent changeEvent) {
* LOG.info(changeEvent);
* }
* });
*
* b1.setValue("string", "TEST");
* b1.check();
*
* b1.setValue("string", "test");
* try {
* b1.check();
* fail("check on " + b1.getValue("string") + " must fail!");
* } catch (IllegalArgumentException ex) {
* //ok
* }
* b1.setValue("string", "xxxxxxxxxxxxxxxxxxxxxxx");
* try {
* b1.check();
* fail("check on " + b1.getValue("string") + " must fail!");
* } catch (IllegalArgumentException ex) {
* //ok
* }
* b2.setValue("primitiveChar", 'X');
* b2.setValue("immutableInteger", 99);
* LOG.info(b1.getAttribute("object").getRelation("immutableInteger").getValue());
*
*
* @author Thomas Schneider
* @version $Revision$
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public class Bean extends BeanDefinition {
/** serialVersionUID */
private static final long serialVersionUID = 1192383647173369697L;
private static final Log LOG = LogFactory.getLog(Bean.class);
/** java object instance to evaluate the attributes for - may be null on virtual beans */
protected T instance;
/** string representation. see {@link #toString()} */
protected String asString;
/** inner function to detach this bean from a beancollector */
private IAction detacher;
transient protected boolean addSaveAction = true;
/** used to registere/find extensions of BeanDefinition/Bean through ENV property */
public static final String BEANWRAPPER = "BeanWrapper";
/**
* hold beans only for a short time. this will enhance performance on loading a bean. holding beans to long will
* result in memory problems as the application may be used by many clients.
*/
static final TimedReferenceMap timedCache = new TimedReferenceMap();
/**
* constructor to create a virtual bean - without an object instance. all attribute definitions must be done by
* yourself.
*/
public Bean() {
this((T) UNDEFINED);
//no reflection has to be done to evaluate the instance attributes!
allDefinitionsCached = true;
name = "virtualbean-" + System.currentTimeMillis();
}
/**
* constructor
*
* @param instance java instance to reflect
*/
public Bean(T instance) {
super((Class) instance.getClass());
this.instance = instance;
}
/**
* @return Returns the instance.
*/
public T getInstance() {
return instance;
}
/**
* unique id for this bean. on persistable beans, it is defined by evaluating {@link #getIdAttribute()} on current
* instance.
*
* @return unique bean id
*/
@Override
public Object getId() {
IAttribute idAttribute = getIdAttribute();
return idAttribute != null ? idAttribute.getValue(instance) : super.getId();
}
/**
* only to be used by framework for performance aspects.
*
* @param instance
*/
public Bean setInstance(T instance) {
//TODO: why clazz can be simple Object.class instead of UNDEFINED?
if (clazz.equals(UNDEFINED.getClass()) || clazz.equals(Object.class)) {
this.clazz = (Class) instance.getClass();
}
this.asString = null;
this.instance = instance;
replaceInstanceInAttributes(instance);
if (valueExpression != null && !valueExpression.hasArguments)
valueExpression = null;
if (actions != null) {
for (IAction a : actions) {
if (a instanceof IConstructable)
((IConstructable) a).setInstance(instance);
}
}
return this;
}
@Override
public Collection getActions() {
if (actions == null) {
if (!isVirtual()) {
if (instance != null) {
actions = getActionsByClass(instance.getClass(), null, new Object[] { instance });
actions = new ArrayList(actions);
Collections.sort((List) actions);
}
}
}
if (addSaveAction && !hasOkAction(actions) && isSelectable()) {
addDefaultSaveAction();
}
return super.getActions();
}
private boolean hasOkAction(Collection actions) {
if (actions != null) {
for (IAction a : actions) {
if (a.getActionMode() == IAction.MODE_DLG_OK)
return true;
}
}
return false;
}
@Override
public IValueDefinition getAttribute(String name) {
return (IValueDefinition) super.getAttribute(name);
}
/**
* evaluate the value of the given bean attribute path
*
* @param bean starting instance
* @param path full relation path, separated by '.'
* @return attribute value or null
*/
public Object getValue(String... path) {
if (isVirtual()) {
return getAttribute(path[0]).getValue();
} else {
return BeanClass.getValue(instance, path);
}
}
/**
* getValues
*
* @param attributeNames attribute names. if no names are given, all attributes will be used
* @return list of values ordered by attributeNames
*/
public Collection getValues(String... attributeNames) {
if (attributeNames.length == 0) {
attributeNames = getAttributeNames();
}
final ArrayList values = new ArrayList(attributeNames.length);
for (int i = 0; i < attributeNames.length; i++) {
// values.add(BeanAttribute.getBeanAttribute(clazz, attributeNames[i]).getValue(instance));
values.add(getValue(attributeNames[i]));
}
return values;
}
/**
* setValue
*
* @param attributeName attribute name
* @param value new value
*/
public void setValue(String attributeName, Object value) {
getAttribute(attributeName).setValue(value);
}
/**
* delegates to {@link BeanValue#setParsedValue(String)}
*/
public void setParsedValue(String attributeName, String value) {
((BeanValue) getAttribute(attributeName)).setParsedValue(value);
}
/**
* searches for the given attribute name. if it exists and it is writable (having a public setter) and the current
* value differs from the new given value, the method {@link BeanValue#setParsedValue(String)} will be called.
*
* @param attributeName attribute to change, if existing with setter
* @param value new value to set
* @return true, if attribute was changed to new value
*/
public boolean changeToParsedValue(String attributeName, String value) {
BeanValue bv;
if (hasAttribute(attributeName) && (bv = (BeanValue) getAttribute(attributeName)).hasWriteAccess()) {
if (!bv.getValueText().equals(value)) {
bv.setParsedValue(value);
return true;
}
}
return false;
}
/**
* @see #getValueAsBean(String, boolean)
*/
public BeanDefinition> getValueAsBean(String name) {
return getValueAsBean(name, true);
}
/**
* wraps the attribute value into a bean. the attribute has to be an entity.
*
* @param name attribute name
* @return new bean holding the attributes value.
*/
public BeanDefinition> getValueAsBean(String name, boolean cacheInstance) {
IValueDefinition> attribute = getAttribute(name);
if (BeanUtil
.isStandardType(attribute.getType()) /*!BeanContainer.instance().isPersistable(attribute.getType())*/) {
throw new ManagedException("The attribute '" + name + "' is not a persistable bean");
}
Serializable value = (Serializable) attribute.getValue();
if (value == null) {
return null;
}
Bean> bean = value instanceof Bean ? (Bean) value : getBean(value, cacheInstance);
return bean;
}
/**
* convenience to observe an attribute
*
* @param name attribute name
* @param listener observer
*/
public void observe(String name, IListener listener) {
getAttribute(name).changeHandler().addListener(listener);
}
public IAttributeDefinition> addAttribute(String name,
int length,
boolean nullable,
Format format,
Object defaultValue,
String description,
IPresentable presentation) {
if (instance.equals(UNDEFINED)) {
throw new IllegalStateException(
"this bean has no real instance (UNDEFINED). if you add bean-attributes, they must have own instances!");
}
return addAttribute(instance, name, length, nullable, format, defaultValue, description, presentation);
}
/**
* isValid
*
* @param messages filled warning and error messages
* @return true, if current value is ok
*/
public boolean isValid(Map, String> messages) {
return isValid(messages, false);
}
public boolean isValid(Map, String> messages, boolean refresh) {
final List> attributes = getBeanValues();
boolean valid = true;
IStatus status = IStatus.STATUS_OK;
for (final BeanValue beanValue : attributes) {
if ((refresh && !(status = beanValue.isValid(beanValue.getValue())).ok())
|| (!refresh && !beanValue.getStatus().ok())) {
if (messages != null)
messages.put(beanValue, beanValue.getStatus().message());
if (refresh)
beanValue.status = status;
if (!beanValue.status.ok()) {
valid = false;
}
}
}
return valid;
}
/**
* checks all attribute values and throws an {@link IllegalArgumentException}, if any value is not valid
*/
public void check() {
check(false);
}
public void check(boolean refresh) {
final Map, String> msgMap = new LinkedHashMap, String>();
final boolean isValid = isValid(msgMap, refresh);
if (!isValid) {
throw new IllegalArgumentException(StringUtil.toString(msgMap, 0));
}
if (crossChecker != null) {
crossChecker.check();
//TODO: refactore incubation rule to be usable here...
// if (rule != null) {
// RulePool.get(rule).run(...);
// }
}
}
public void addCrossValueChecker(BeanValue checkBean,
ArrayList mustHave,
ArrayList mustNotHave) {
crossChecker.add(checkBean, mustHave, mustNotHave);
}
/**
* {@inheritDoc}
*/
@Override
protected IAttributeDefinition createAttributeDefinition(String name) {
BeanValue beanValue = BeanValue.getBeanValue(instance, name);
beanValue.setRange(getPresentationHelper().getDefaultAllowedValues(beanValue));
return beanValue;
}
/**
* getBeanValues
*
* @return available bean attribute values
*/
public List> getBeanValues() {
//workaround for collection generic
Object result = getAttributes();
return (List>) result;
}
/**
*
* Attention: this implementation uses the {@link BeanValue} cache - means, that the new instance may resist inside
* that cache. override this method to initialize your bean.
*
* @param type object type
* @param constructorArgs object type constructor arguments
* @return new bean
*/
public void newInstance(Object... constructorArgs) {
instance = createInstance(constructorArgs);
replaceInstanceInAttributes(instance);
}
/**
* replaceInstanceInAttributes
*
* @param instance2
*/
Bean replaceInstanceInAttributes(T instance2) {
if (attributeDefinitions != null) {
for (final IAttributeDefinition> a : attributeDefinitions.values()) {
//TODO: what to do with virtual values?
if (a.isVirtual() && a.getAccessMethod() != null) {
continue;
}
((BeanValue) a).setInstance(instance2);
}
}
return this;
}
/**
* creates a new bean with new object instance
*
* @param type object type
* @param constructorArgs object type constructor arguments
* @return new bean
*/
public static Bean newBean(Class type, Object... constructorArgs) {
return new Bean(BeanClass.createInstance(type, constructorArgs));
}
@Override
public int hashCode() {
return isVirtual() ? super.hashCode() : 31 * super.hashCode() + instance.hashCode();
}
/**
* addDefaultSaveAction
*
* @return new created default save action for {@link #instance}.
*/
public IAction> addDefaultSaveAction() {
SecureAction> saveAction = createSaveAction(instance);
addAction(saveAction);
return saveAction;
}
/**
* creates a save action for the given bean. the action-id will be 'bean.getClass().save' - that will be used as
* permission id, too.
*
* @param bean bean to save
*/
protected SecureAction> createSaveAction(final Object bean) {
final String actionId = BeanContainer.getActionId(bean.getClass(), false, "save");
return createSaveAction(bean, actionId);
}
/**
* creates a save action for the given bean. the action-id will be 'bean.getClass().save' - that will be used as
* permission id, too.
*
* @param bean bean to save
* @param actionId action id (important for user permissions!)
*/
protected SecureAction createSaveAction(final Object bean, String actionId) {
final String saveLabel =
BeanContainer.isInitialized() && BeanContainer.instance().isPersistable(getDefiningClass(clazz))
&& !CompositionFactory.contains(bean) ? Messages
.getString("tsl2nano.save") : Messages.getString("tsl2nano.assign");
return new SaveAction(bean, actionId, saveLabel, saveLabel, IAction.MODE_DLG_OK);
}
/**
* save
*
* @return
*/
public Object save() {
return /*setInstance(*/save(instance)/*)*/;
}
/**
* overwrite this method to define your own saving mechanism
*
* @param bean normally the presenters bean, but on an unpersistable presenter-bean you should give the right
* persistable entity.
* @return saved and refreshed bean (please assign the new bean to the presenters {@link BasePresenter#data}) to be
* used as presenters bean (see {@link BasePresenter#data}). on any catched error, you should return
* {@link IAction#CANCELED} to inform the framework to cancel following gui-actions. then f.e. a dialog
* won't be closed.
*/
protected Object save(Object bean) {
//on not-persistable beans, the presentation layer has to handle the save or assignment
if (!BeanContainer.isInitialized() || !BeanContainer.instance().isPersistable(getDefiningClass(clazz))) {
return bean;
}
// do the save - fill the old bean with the new id value
Object newBean;
try {
if (CompositionFactory.markToPersist(bean)) {
//refresh the bean!
// result = BeanContainer.instance().getBeansByExample(instance).iterator().next();
newBean = bean;
} else {
newBean = BeanContainer.instance().save(bean);
/*
* after the save operation, the presenter can be used only after
* a BeanContainer.resolveLayzRelation() and a reset()-call.
*
* the gui using this presenter should be closed or recreated!
*/
newBean = BeanContainer.instance().resolveLazyRelations(newBean);
}
} catch (final RuntimeException e) {
if (BeanContainer.isConstraintError(e)) {
throw new ManagedException("tsl2nano.impossible_create", new Object[] { /*Configuration.current()
.getDefaultFormatter()
.format(*/bean /*)*/ });
} else {
throw e;
}
} finally {
// detach();
}
//refresh the old bean with the new id
final BeanAttribute idAttribute = BeanContainer.getIdAttribute(bean);
if (idAttribute != null) {
idAttribute.setValue(bean, idAttribute.getValue(newBean));
}
//if the saved object is the presenters bean - use the new refreshed bean
if (newBean.getClass().equals(instance.getClass())) {
instance = (T) newBean;
}
return newBean;
}
/**
* fills a map with all bean-attribute-names and their values.
*
* @param properties will be ignored (is only inherited)
* @return map filled with all attribute values
*/
@Override
public Map toValueMap(Map properties) {
return toValueMap(instance, true, false, false);
}
protected static Bean createBean(I instance, BeanDefinition beandef) {
return createBean(instance, beandef, new Bean());
}
/**
* creates a bean through informations of a bean-definition
*
* @param
* @param instance instance of bean
* @param beandef bean description
* @return new created bean holding given instance
*/
protected static Bean createBean(I instance, BeanDefinition beandef, Bean beanWrapper) {
Bean bean = beanWrapper;
copy(beandef, bean, "attributeFilter", "attributeDefinitions", "asString", "presentationHelper", "presentable",
"actions");
bean.attributeFilter = beandef.attributeFilter != null ? CollectionUtil.copy(beandef.attributeFilter) : null;
bean.attributeDefinitions =
(LinkedHashMap>) Util.untyped(createValueDefinitions(beandef
.getAttributeDefinitions()));
if (beandef.presentable != null)
bean.presentable = BeanUtil.copy(beandef.presentable);
if (beandef.actions != null) {
bean.actions = new ArrayList(beandef.actions.size());
for (IAction a : beandef.actions) {
bean.actions.add(a instanceof Serializable && !a.getClass().isAnonymousClass() ? BeanUtil.copy(a) : a);
}
}
//give the new bean the chance to create actions...only if null
if (bean.actions != null && bean.actions.size() == 0) {
bean.actions = null;
}
bean.setInstance(instance);
injectIntoRuleCovers(bean, instance);
if (bean.getPlugins() != null) {
for (IConnector p : bean.getPlugins()) {
p.connect(bean);
}
}
return bean;
}
/**
* creates new enhanced value definitions from given attribute definitions. the values instance will be
* {@link BeanDefinition#UNDEFINED}.
*
* @param attributeDefinitions attributes to copy and enhance
* @return new map holding value definitions
*/
protected static LinkedHashMap> createValueDefinitions(
Map> attributeDefinitions) {
LinkedHashMap> valueDefs =
new LinkedHashMap>(attributeDefinitions.size());
try {
for (IAttributeDefinition> attr : attributeDefinitions.values()) {
// if (!(attr instanceof IValueAccess)) {//--> standard attribute-definition
//use any simple arguments - they will be overwritten on next line in copy(...)
IValueAccess valueDef = new BeanValue();/*UNDEFINED, AttributeDefinition.UNDEFINEDMETHOD) {
@Override
protected void defineDefaults() {
// don't set any defaults - overwrite members in the next step
}
};*/
valueDef = copy(attr, valueDef, "parent");
//Workaround for 'parent' field in BeanValue to avoid a ConcurrentModificationException in Android
if (attr instanceof BeanValue)
((BeanValue) valueDef).setParent(((BeanValue) attr).getParent());
BeanValue.beanValueCache.add((BeanValue) valueDef);
if (valueDef instanceof IPluggable) {
Collection plugins = ((IPluggable) valueDef).getPlugins();
if (plugins != null) {
for (IConnector p : plugins) {
p.connect(valueDef);
}
}
}
valueDefs.put(attr.getName(), valueDef);
// } else {//it is a specialized beanvalue like pathvalue or ruleattribute
// valueDefs.put(attr.getName(), (IValueAccess>) BeanUtil.clone(attr));
// }
}
return valueDefs;
} catch (Exception e) {
ManagedException.forward(e);
return null;
}
}
/**
* creates a bean with given instance or name. if you give a name, a virtual bean will be created and returned. if
* you give an instance, a bean definition will be searched and copied to a new created bean.
*
* @param bean instance type
* @param instanceOrName an object or string (--> {@link #isVirtual()})
* @param keysAndValues key-value-pairs to be filled to the given instance
* @return new created bean
*/
public static Bean getBean(I instanceOrName, Object... keysAndValues) {
Bean bean = getBean(instanceOrName);
Map map = MapUtil.asMap(keysAndValues);
Set keySet = map.keySet();
for (String key : keySet) {
bean.setValue(key, map.get(key));
}
return bean;
}
/**
* @see #getBean(Serializable, boolean)
*/
public static Bean getBean(I instanceOrName) {
return getBean(instanceOrName, true/*!BeanUtil.isStandardType(instanceOrName)*/);
}
/**
* creates a bean with given instance or name. if you give a name, a virtual bean will be created and returned. if
* you give an instance, a bean definition will be searched and copied to a new created bean.
*
* @param bean instance type
* @param instanceOrName an object or string (--> {@link #isVirtual()})
* @return new created bean
*/
public static Bean getBean(I instanceOrName, boolean cacheInstance) {
Bean bean = timedCache.get(instanceOrName);
if (bean != null) {
return bean;
}
if (instanceOrName instanceof String) {
BeanDefinition beandef = (BeanDefinition) getBeanDefinition((String) instanceOrName);
// check, if beandef is for virtual type
if (!beandef.getDeclaringClass().equals(UNDEFINED.getClass())) {
//beandef = new BeanDefinition(); // this would overwrite all configurations???
beandef.setName(instanceOrName.toString());
}
bean = createBean((I) UNDEFINED, beandef);
} else if (instanceOrName.getClass().isArray()) {
bean = createArrayBean(instanceOrName);
} else if (Map.class.isAssignableFrom(instanceOrName.getClass())) {
bean = createMapBean(instanceOrName);
} else if (Entry.class.isAssignableFrom(instanceOrName.getClass())) {
BeanDefinition beandef =
getBeanDefinition((Class) BeanClass.getDefiningClass(instanceOrName.getClass()));
bean = createBean(instanceOrName, beandef);
Entry entry = (Entry) instanceOrName;
if (entry.getValue() != null) {
IConstraint c = bean.getAttribute("value").getConstraint();
c.setFormat(null);
c.setType(BeanClass.getDefiningClass(entry.getValue().getClass()));
}
} else {
Class type = instanceOrName.getClass();
BeanDefinition beandef =
getBeanDefinition((Class) BeanClass.getDefiningClass(instanceOrName.getClass()));
Class instanceBeanWrapper = ENV.get(type.getName() + BEANWRAPPER, null);
if (instanceBeanWrapper != null) {
bean = createBean(instanceOrName, beandef, (Bean)BeanClass.createInstance(instanceBeanWrapper, instanceOrName));
} else {
bean = createBean(instanceOrName, beandef);
}
}
if (cacheInstance && ENV.get("bean.use.cache", true)) {
timedCache.put(instanceOrName, bean);
}
return bean;
}
@Override
//not yet used on creation
public void autoInit(String name) {
super.autoInit(name);
List> beanValues = getBeanAttributes();
for (IAttributeDefinition> bv : beanValues) {
bv.getPresentation();
bv.getColumnDefinition();
}
}
private static Bean createArrayBean(Object array) {
int length = Array.getLength(array);
Bean bean = new Bean(array);
for (int i = 0; i < length; i++) {
bean.addAttribute(new BeanValue(bean.instance, new ArrayValue(String.valueOf(i), i)));
}
return bean;
}
private static Bean createMapBean(Object mapInstance) {
Map map = (Map) mapInstance;
Bean bean = new Bean(map);
Set keySet = map.keySet();
Object v;
if (map.keySet() != null) {//on a proxy instance, keySet() may return null!
for (Object k : keySet) {
v = map.get(k);
bean.addAttribute(
new BeanValue(bean.instance,
new MapValue(v != null ? v : k, (v != null ? BeanClass.getDefiningClass(v
.getClass()) : null), map)));
}
}
return bean;
}
/**
* attaches the given detacher
*
* @param detacher
*/
public void attach(IAction detacher) {
this.detacher = detacher;
}
/**
* cleans all bean relevant caches (BeanClass, BeanValue, BeanDefinition, Bean).
*
* @return amount of cleared objects.
*/
public static int clearCache() {
CompositionFactory.clearCache();
int cleared = BeanValue.clearCache();
cleared += BeanClass.clearCache();
cleared += BeanDefinition.clearCache();
if (timedCache != null) {
cleared += timedCache.size();
LOG.info("clearing bean/value/class cache of " + cleared + " elements");
timedCache.clear();
}
return cleared;
}
@Override
public void onDeactivation(Map context) {
super.onDeactivation(context);
//if a new object was cancelled, it must be removed
if (!isMultiValue()) {
detach("remove");
}
//remove temp values of listeners
List> bvs = getBeanValues();
for (BeanValue> bv : bvs) {
bv.changeHandler().reset();
}
}
/**
* runs detacher and sets detacher to null. if arguments equals 'remove' this bean will be removed from a parent
* list.
*/
public boolean detach(Object... arguments) {
timedCache.remove(this);
if (detacher != null) {
detacher.setParameter(arguments);
detacher.run();
detacher = null;
return true;
} else {
return false;
}
}
@Override
public void setName(String name) {
super.setName(name);
asString = null;
}
public void setAddSaveAction(boolean addSaveAction) {
this.addSaveAction = addSaveAction;
}
@Override
public > B onActivation(Map context) {
//on new beans, we fill manyToOne relations if exactly one item is available
if (!BeanContainer.isInitialized()
|| BeanContainer.instance().isTransient(instance) && ENV.get("bean.new.fill.relations.on.one.item", true)) {
String[] names = getAttributeNames();
for (int i = 0; i < names.length; i++) {
IValueDefinition attr = getAttribute(names[i]);
if (attr.isRelation() && !attr.isMultiValue() && attr.getValue() == null) {
if (BeanContainer.getCount(attr.getType()) == 1) {
attr.setValue(BeanContainer.instance().getBeans(attr.getType(), 0, -1).iterator().next());
}
}
}
}
super.onActivation(context);
return (B) this;
}
@Override
public Bean refreshed() {
if (isStale())
return getBean(instance);
return this;
}
public String toStringDescription() {
final Collection extends IAttribute> attributes = getAttributes();
final StringBuilder buf = new StringBuilder(attributes.size() * 15);
buf.append(getName() + " {");
for (final IAttribute beanAttribute : attributes) {
if (beanAttribute instanceof BeanValue) {
buf.append(beanAttribute.toString());
} else {
buf.append(beanAttribute.getName() + "=" + beanAttribute.getValue(instance) + "\n");
}
}
buf.append("}");
return buf.toString();
}
@Override
protected void finalize() throws Throwable {
//don't call a getter to evaluate attributes - the attributes would be created then
if (attributeDefinitions != null) {
for (IAttributeDefinition> bv : attributeDefinitions.values()) {
if (bv instanceof BeanValue)
BeanValue.beanValueCache.remove(bv);
}
}
super.finalize();
}
@Override
public String toString() {
if (asString == null) {
asString = toString(instance);
}
return asString;
}
}
/**
* To be serializable, we had to extract a full class instead of using the shorter inline class. Only used at
* {@link Bean#createSaveAction(Object, String)}.
*
* @author Thomas Schneider
* @version $Revision$
*/
@SuppressWarnings({ "unchecked" })
class SaveAction extends SecureAction implements Serializable, IConstructable {
/** serialVersionUID */
private static final long serialVersionUID = -3009132079876910521L;
/** instance to save - may differ from bean.instance! */
private T instance;
protected SaveAction() {}
public SaveAction(T instance,
String id,
String shortDescription,
String longDescription,
int actionMode) {
super(id, shortDescription, longDescription, actionMode);
this.instance = instance;
}
@Override
public T action() throws Exception {
return (T) Bean.getBean(instance).save();
}
@Override
public String getImagePath() {
return "icons/save.png";
}
@Override
public T getInstance() {
return instance;
}
@Override
public void setInstance(T instance) {
this.instance = instance;
}
}