de.tsl2.nano.h5.collector.Compositor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.h5 Show documentation
Show all versions of tsl2.nano.h5 Show documentation
TSL2 Framework Html5 Extensions (WebServer, Html5Presentation, RuleCover, BeanConfigurator, LogicTable-Sheet, Expression-Descriptors for Actions, Rules, URLs, Queries)
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Tom, Thomas Schneider
* created on: 02.04.2016
*
* Copyright: (c) Thomas Schneider 2016, all rights reserved
*/
package de.tsl2.nano.h5.collector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.simpleframework.xml.Transient;
import org.simpleframework.xml.core.Commit;
import de.tsl2.nano.action.IAction;
import de.tsl2.nano.bean.BeanContainer;
import de.tsl2.nano.bean.BeanUtil;
import de.tsl2.nano.bean.def.Attachment;
import de.tsl2.nano.bean.def.Bean;
import de.tsl2.nano.bean.def.BeanCollector;
import de.tsl2.nano.bean.def.BeanDefinition;
import de.tsl2.nano.bean.def.BeanFinder;
import de.tsl2.nano.bean.def.BeanValue;
import de.tsl2.nano.bean.def.Composition;
import de.tsl2.nano.bean.def.IAttributeDefinition;
import de.tsl2.nano.bean.def.Presentable;
import de.tsl2.nano.bean.def.SecureAction;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.exception.Message;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.util.MapUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.execution.IPRunnable;
import de.tsl2.nano.specification.Pool;
/**
* The Compositor is a fast- or one-click collector using a base-type to create composition instances from. It's a kind
* of charging items of the base-type. see {@link Composition}
*
* It provides actions to refresh, save and storno the list of charges. Each charge (=add or create) will be done by
* activing the representable action for an item. Each item will represented by such an action.
*
* @author Tom, Thomas Schneider
* @version $Revision$
*/
@SuppressWarnings({ "serial", "rawtypes" })
public class Compositor, T> extends BeanCollector {
/** serialVersionUID */
private static final long serialVersionUID = 1L;
private static final Log LOG = LogFactory.getLog(Compositor.class);
/** base type of items to create composition instances from */
@Transient
Class> parentType;
/** composition instance attribute to assign the base instance to */
@Transient
protected String baseAttribute;
/** composition target type - normally the type of this BeanDefinition */
@Transient
protected Class> targetType;
/** composition instance attribute to assign the target instance to */
@Transient
protected String targetAttribute;
/** base instance attribute name to evaluate an image path for the compositor action */
@Transient
protected String iconAttribute;
@Transient
/** if true, the user will be asked to save the new item */
protected boolean forceUserInteraction = false;
@Transient
protected String actionEnablerRule;
private transient List compositorActions;
/**
* constructor
*/
public Compositor() {
}
/**
* constructor
*
* @param beanType
* @param iconAttribute2
* @param workingMode
*/
public Compositor(Class beanType,
Class> baseType,
final String baseAttribute,
final String targetAttribute,
String iconAttribute) {
BeanCollector, T> bc = BeanCollector.getBeanCollector(beanType, null, MODE_SEARCHABLE, null);
copy(bc, this, "asString", "presentationHelper", "presentable");
//use an own map instance to be independent of changes by other beans or beancollectors.
attributeDefinitions = new LinkedHashMap>(getAttributeDefinitions());
presentable = (Presentable) BeanUtil.copy(bc.getPresentable());
init(collection, new BeanFinder(beanType), workingMode, composition);
if (baseType != null)
init(beanType, baseType, baseAttribute, targetAttribute, iconAttribute);
else
LOG.warn("no baseType defined -> Compositor provides no function");
}
/**
* init
*
* @param beanType
* @param baseType
* @param baseAttribute
* @param targetAttribute
* @param iconAttribute
*/
void init(Class beanType,
Class> baseType,
final String baseAttribute,
final String targetAttribute,
String iconAttribute) {
this.name = createName(beanType, baseType);
this.parentType = baseType;
this.baseAttribute = baseAttribute;
this.targetAttribute = targetAttribute;
this.iconAttribute = iconAttribute;
}
public String createName(Class> beanType, Class> baseType) {
return createBeanDefName("Compositor", beanType, baseType);
}
public static String createBeanDefName(String classPrefix, Class> beanType, Class> baseType) {
return classPrefix + " (" + (baseType != null ? baseType.getSimpleName() + "-" : "") + beanType.getSimpleName() + ")";
}
@Override
public Collection getActions() {
if (compositorActions == null) {
Collection actions = super.getActions();
if (parentType == null)
return actions;
compositorActions = createCompositorActions(parentType, getSearchPanelBeans().iterator().next(),
baseAttribute, getTargetType(), targetAttribute,
iconAttribute);
compositorActions.addAll(actions);
}
return compositorActions;
}
@Override
public boolean isVirtual() {
return true;
}
/**
* @deprecated: see {@link #createCompositorActions(Class, Object, String, String, String)} creates an
* compositor-action for each item of type baseType, found in the database.
*
* @param baseType see {@link #parentType}
* @param preparedComposition pre-filled instance to be used to create new composition instances
* @param targetAttribute see {@link #targetAttribute}
* @param iconAttribute (optional) see {@link #iconAttribute}
* @return list of compositor actions
*/
public List createCompositorActions(Class> baseType,
final T preparedComposition,
final String targetAttribute,
final String iconAttribute) {
Collection> bases = BeanContainer.instance().getBeans(baseType, 0, -1);
ArrayList actions = new ArrayList<>();
for (final Object base : bases) {
actions.add(new SecureAction(Bean.getBean(base).getId().toString(),
Bean.getBean(base).toString()) {
T item;
@Override
public T action() throws Exception {
item = createItem(preparedComposition);
Bean.getBean(item).setValue(targetAttribute, base);
return item;
}
@Override
public String getImagePath() {
return (String) (iconAttribute != null ? Bean.getBean(base).getValue(iconAttribute)
: Bean.getBean(base).getPresentable().getIcon());
}
});
}
return actions;
}
/**
* creates an compositor-action for each item of type baseType, found in the database.
*
* @param baseType see {@link #parentType}
* @param preparedComposition pre-filled instance to be used to create new composition instances
* @param baseAttribute see {@link #targetAttribute}
* @param targetType see #
* @param targetAttribute see {@link #targetAttribute}
* @param iconAttribute (optional) see {@link #iconAttribute}
* @return list of compositor actions
*/
public List createCompositorActions(Class> baseType,
final T preparedComposition,
final String baseAttribute,
final Class> targetType,
final String targetAttribute,
final String iconAttribute) {
final Compositor _this = this;
Collection> bases = BeanContainer.instance().getBeans(baseType, 0, -1);
ArrayList actions = new ArrayList<>();
for (final Object base : bases) {
actions.add(new SecureAction(createCompositorActionId(base),
Bean.getBean(base).toString()) {
@SuppressWarnings("unchecked")
@Override
public T action() throws Exception {
getSelectionProvider().getValue().clear();
getSelectionProvider().getValue().add(preparedComposition);
BeanCollector c = targetAttribute == null ? Compositor.this : BeanCollector.getBeanCollector((Class)targetType);
T item = (T) c.createItem(preparedComposition);
BeanContainer.initDefaults(item);
if (targetAttribute == null) {
setDefaultValues(item, true);
//fill context parameters
Object[] pars = getParameter();
if (pars != null)
fillContext(item, pars);
}
BeanValue parent = (BeanValue) Bean.getBean(base).getAttribute(baseAttribute);
//on manyTomany targetAttribute must not be null
IAttributeDefinition> targetAttr = targetAttribute != null ? c.getAttribute(targetAttribute) : null;
Composition comp = new Composition(parent, targetAttr);
Object resolver = comp.createChildOnTarget(item);
if (resolver == item) //yes we need direct instance comparing with '=='
resolver = base;
if (!forceUserInteraction && Bean.getBean(item).isValid(null, true)) {
resolver = (T) persistResolverIfNotCascading(targetAttr, resolver);
item = (T) Bean.getBean(item).save();
getCurrentData().add(item);
return (T) _this;//the type T should be removed
} else if (targetAttr != null && !Collection.class.isAssignableFrom(targetAttr.getType())) {
persistResolverIfNotCascading(targetAttr, resolver);
}
getCurrentData().add(item);
return item;
}
private Object persistResolverIfNotCascading(IAttributeDefinition> targetAttr, Object resolver) {
if (targetAttr == null || !targetAttr.cascading()) {
HashMap, String> errors = new HashMap<>();
if (Bean.getBean(resolver).isValid(errors, true)) {
resolver = Bean.getBean(resolver).save();
} else {
LOG.error("couldn't persist resolver of type " + resolver.getClass() + " for composition target " + targetAttr);
String msg = StringUtil.toFormattedString(errors, -1, true);
if (ENV.get("app.mode.strict", false))
throw new ManagedException(msg);
else
Message.send(msg);
}
}
return resolver;
}
@Override
public boolean isEnabled() {
boolean actionEnabled = true;
if (actionEnablerRule != null) {
IPRunnable filterRule = ENV.get(Pool.class).get(actionEnablerRule);
if (filterRule != null)
actionEnabled = (boolean) filterRule.run(MapUtil.asMap(base));
}
return super.isEnabled() && actionEnabled;
}
@Override
public String getImagePath() {
if (iconAttribute != null) {
Bean bean = Bean.getBean(base);
Object pictInfo = bean.getValue(iconAttribute);
if (pictInfo instanceof String)
return (String) pictInfo;
else if (pictInfo != null) {
return Attachment.getValueFile(bean.getAttribute(iconAttribute).getId(), pictInfo).getPath();
} else {
return null;
}
} else {
return Bean.getBean(base).getPresentable().getIcon();
}
}
});
}
return actions;
}
protected String createCompositorActionId(final Object baseInstance) {
return getName().toLowerCase() + "." + Bean.getBean(baseInstance).getId().toString();
}
public void setForceUserInteraction(boolean forceUserInteraction) {
this.forceUserInteraction = forceUserInteraction;
}
public Class> getTargetType() {
if (targetType == null)
targetType = getDeclaringClass();
return targetType;
}
public void setTargetType(Class> targetType) {
this.targetType = targetType;
this.name = createName(targetType, parentType);
}
public T getParentInstance(Object targetInstance) {
Object v = getAttribute(targetAttribute).getValue(targetInstance);
return (T) Bean.getBean(v).getAttribute(parentType).getValue(v);
}
@Override
public > B onActivation(Map context) {
compositorActions = null;
return super.onActivation(context);
}
@Override
public Compositor refreshed() {
if (isStale())
return new Compositor<>(clazz, parentType, baseAttribute, targetAttribute, iconAttribute);
return this;
}
@SuppressWarnings("unchecked")
@Override
@Commit
protected void initDeserialization() {
if (hasMode(MODE_SEARCHABLE))
beanFinder = new BeanFinder(super.getClazz());
//without commit annotation in this class, the super wont be called
super.initDeserialization();
}
}