All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.tsl2.nano.h5.configuration.BeanConfigurator Maven / Gradle / Ivy

Go to download

TSL2 Framework Html5 Extensions (WebServer, Html5Presentation, RuleCover, BeanConfigurator, LogicTable-Sheet, Expression-Descriptors for Actions, Rules, URLs, Queries)

There is a newer version: 2.5.2
Show newest version
/*
 * File: $HeadURL$
 * Id  : $Id$
 * 
 * created by: Tom, Thomas Schneider
 * created on: 07.01.2014
 * 
 * Copyright: (c) Thomas Schneider 2014, all rights reserved
 */
package de.tsl2.nano.h5.configuration;

import static de.tsl2.nano.h5.HtmlUtil.ATTR_BGCOLOR;
import static de.tsl2.nano.h5.HtmlUtil.COLOR_LIGHT_GRAY;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import de.tsl2.nano.action.CommonAction;
import de.tsl2.nano.action.IConstraint;
import de.tsl2.nano.bean.annotation.ConstraintValueSet;
import de.tsl2.nano.bean.def.AttributeDefinition;
import de.tsl2.nano.bean.def.Bean;
import de.tsl2.nano.bean.def.BeanDefinition;
import de.tsl2.nano.bean.def.Constraint;
import de.tsl2.nano.bean.def.IAttributeDefinition;
import de.tsl2.nano.bean.def.IPresentable;
import de.tsl2.nano.bean.def.IPresentableColumn;
import de.tsl2.nano.bean.def.Presentable;
import de.tsl2.nano.bean.def.ValueColumn;
import de.tsl2.nano.bean.def.ValueExpression;
import de.tsl2.nano.bean.def.ValueExpressionFormat;
import de.tsl2.nano.bean.def.ValueGroup;
import de.tsl2.nano.collection.Entry;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.cls.IAttribute;
import de.tsl2.nano.core.cls.PrivateAccessor;
import de.tsl2.nano.core.util.ConcurrentUtil;
import de.tsl2.nano.core.util.FileUtil;
import de.tsl2.nano.core.util.MapUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.h5.Html5Presentable;
import de.tsl2.nano.h5.SpecifiedAction;
import de.tsl2.nano.h5.collector.CSheet;
import de.tsl2.nano.h5.collector.Compositor;
import de.tsl2.nano.h5.collector.Controller;
import de.tsl2.nano.h5.collector.Increaser;
import de.tsl2.nano.h5.expression.Query;
import de.tsl2.nano.h5.expression.WebClient;
import de.tsl2.nano.specification.Pool;
import de.tsl2.nano.specification.actions.Action;
import de.tsl2.nano.specification.rules.Rule;
import de.tsl2.nano.specification.rules.RuleScript;

/**
 * wrapper class to handle presentation of a bean-definition. at a time, only one BeanConfigurator is active. this
 * instance will be registered to the environment to be usable as something like a singleton from outside.
 * 
 * @author Tom, Thomas Schneider
 * @version $Revision$
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class BeanConfigurator implements Serializable {
    /** serialVersionUID */
    private static final long serialVersionUID = 1L;
    
    BeanDefinition def;
    private PrivateAccessor> defAccessor;
    private transient List attrConfigurators;

    /**
     * factory method to create a bean configurator for the given instance type.
     * 
     * @param instance to evaluate the type and {@link BeanDefinition} for.
     * @return new bean configurator instance
     */
    public static  Bean> create(Class type) {
        boolean autopersistEnv = ENV.isAutopersist();
        BeanConfigurator configurer = ConcurrentUtil.getCurrent(BeanConfigurator.class);
        if (configurer == null || !configurer.def.getDeclaringClass().equals(type)) {
            try {
                ENV.setAutopersist(false);
                Serializable layout = (Serializable) MapUtil.asMap(ATTR_BGCOLOR, COLOR_LIGHT_GRAY);
                if (configurer == null)
                	prepareBeanDefinitionProperties(layout);
                
                //wrap the bean-def into a bean-configurator and pack it into an own bean
                configurer = new BeanConfigurator(BeanDefinition.getBeanDefinition(type));
                //register it to be used by creating new AttributeConfigurators 
                ConcurrentUtil.setCurrent(configurer); //avoid stackoverflow
    
                //define the presentation
                Bean configBean = Bean.getBean(configurer);
    
                if (configBean.isDefault()) {
                    configBean.setAttributeFilter("name", "valueExpression", "presentable", "valueGroups", "attributes");
                    configBean.getPresentable().setLayout(layout);
                    ((ValueExpressionFormat) configBean.getAttribute("attributes").getFormat())
                        .getValueExpression()
                        .setExpression("{name}");
    //          configBean.saveDefinition();
    
                }
                return (Bean>) configBean;
            } finally {
                ENV.setAutopersist(autopersistEnv);
            }
        }
        return (Bean>) (Object)Bean.getBean(configurer);
    }

    private static void prepareBeanDefinitionProperties(Serializable layout) {
        
        BeanDefinition configPres = BeanDefinition.getBeanDefinition(Html5Presentable.class, false);
        configPres.setAttributeFilter("label", "description", "icon", "type", "style", "visible", "searchable",
            "nesting", "width",
            "height", "layout",
            "layoutConstraints", "groups");
        configPres.getPresentable().setLayout(layout);
        configPres.getValueExpression();
//            configPres.saveDefinition();

        BeanDefinition configValueGroup = BeanDefinition.getBeanDefinition(ValueGroup.class, false);
        configValueGroup.setAttributeFilter("label", "description", "icon", "type", "style", "width", "height",
            "layout",
            "layoutConstraints", "attributes");
        configValueGroup.getPresentable().setLayout(layout);
        configValueGroup.getValueExpression();
//            configValueGroup.saveDefinition();

        BeanDefinition configColDef = BeanDefinition.getBeanDefinition(ValueColumn.class, false);
        configColDef.setAttributeFilter("name", "description", "index", "sortIndex", "sortUpDirection",
            "format",
            "width"
        /*                    "standardSummary",
        "presentable",
        "minSearchValue",
        "maxSearchValue"*/
        );
        configColDef.getPresentable().setLayout(layout);
        BeanDefinition.getBeanDefinition(IPresentableColumn.class, false).setAttributeFilter(configColDef.getAttributeNames());
//            configColDef.saveDefinition();

        BeanDefinition configConstraint = BeanDefinition.getBeanDefinition(Constraint.class, false);
        configConstraint.setAttributeFilter("type", "minimum", "maximum", "format", "length", "scale",
            "precision",
            "nullable");
        configConstraint.getPresentable().setLayout(layout);
        BeanDefinition.getBeanDefinition(IConstraint.class, false).setAttributeFilter(configConstraint.getAttributeNames());
//            configConstraint.saveDefinition();

        defineAction(layout);

        BeanDefinition configEntry = BeanDefinition.getBeanDefinition(Entry.class, false);
        configEntry.setAttributeFilter("key", "value");
        configEntry.getPresentable().setLayout(layout);
        configEntry.getValueExpression();
//            configEntry.saveDefinition();

        BeanDefinition configAttr =
            BeanDefinition.getBeanDefinition(AttributeConfigurator.class, false);
        configAttr.setAttributeFilter("name", "description", "type", "format",
            "constraint"/*, "length", "min", "max"*/,
            "presentable", "columnDefinition", "declaration", "valueExpression", "default", "listener");
        configAttr.getPresentable().setLayout(layout);
        configAttr.setValueExpression(new ValueExpression("{name}", AttributeConfigurator.class));
//                configAttr.saveDefinition();

	}

    /**
     * defineAction
     * 
     * @param layout
     */
    public static void defineAction(Serializable layout) {
        BeanDefinition configAction = BeanDefinition.getBeanDefinition(CommonAction.class);
        configAction.setAttributeFilter("id", "shortDescription", "longDescription", "keyStroke", "enabled", "default",
            "imagePath");
        configAction.setIdAttribute("id");
        configAction.setValueExpression(new ValueExpression("{shortDescription}", CommonAction.class));
        if (layout != null) {
            configAction.getPresentable().setLayout(layout);
        }
    }

    /**
     * constructor
     * 
     * @param def bean-def
     */
    protected BeanConfigurator(BeanDefinition def) {
        super();
        this.def = def;
        defAccessor = new PrivateAccessor>(def);
    }

    /**
     * @return Returns the attributeDefinitions.
     */
    public List getAttributes() {
        if (attrConfigurators == null) {
            //we know that there are attribute-defs inside!
            List attributes = def.getAttributes();
            attrConfigurators = new ArrayList(attributes.size());
            for (IAttribute a : attributes) {
                attrConfigurators.add(new AttributeConfigurator((AttributeDefinition) a));
            }
        }
        return attrConfigurators;
    }

    /**
     * @param attributes The attributeDefinitions to set.
     */
    public void setAttributes(List attributes) {
        Map definitions = defAccessor.call("getAttributeDefinitions", Map.class);
        Map invisibles = new LinkedHashMap<>(definitions);
        definitions.clear();
        int i = 0;
        for (AttributeConfigurator cattr : attributes) {
            IAttributeDefinition a = cattr.unwrap();
            if (a.getColumnDefinition() == null)
                a.setColumnDefinition(++i, IPresentable.UNDEFINED, true, IPresentable.UNDEFINED);
            else //IMPROVE: enhance interfaces to set index without reflection
                new PrivateAccessor(a.getColumnDefinition()).set("columnIndex", ++i);
            def.addAttribute(a);
            invisibles.remove(a.getName());
        }
        //add not-selected attributes as invisibles at the end
        for (IAttributeDefinition a : invisibles.values()) {
            //IMPROVE: enhance interfaces to set index without reflection
            a.getPresentation().setVisible(false);
            if (a.getColumnDefinition() != null) {
                new PrivateAccessor(a.getColumnDefinition()).set("columnIndex", ++i);
                a.getColumnDefinition().getPresentable().setVisible(false);
            }
            def.addAttribute(a);
        }
        attrConfigurators = null;
    }

    /**
     * @return Returns the presentable.
     */
    public Presentable getPresentable() {
        return (Presentable) def.getPresentable();
    }

    /**
     * @param presentable The presentable to set.
     */
    public void setPresentable(Presentable presentable) {
        def.setPresentable(presentable);
    }

    /**
     * @return Returns the presentable.
     */
    public Collection getValueGroups() {
        return defAccessor.member("valueGroups", Collection.class);
    }

    /**
     * @param valueGroups The presentable to set.
     */
    public void setValueGroups(Collection valueGroups) {
        defAccessor.set("valueGroups", valueGroups);
    }

    /**
     * @return Returns the name.
     */
    public String getName() {
        return def.getName();
    }

    /**
     * @return Returns the valueExpression.
     */
    public String getValueExpression() {
        return def.getValueExpression().getExpression();
    }

    /**
     * @param valueExpression The valueExpression to set.
     */
    public void setValueExpression(String valueExpression) {
        def.setValueExpression(new ValueExpression(valueExpression, def.getClazz()));
    }

    /**
     * saves the current bean configuration.
* this method will trigger the bean-framework to provide an action to be presented as button in a gui. * * @return null */ public Object actionSave() { defAccessor.set("isdefault", false); //WORKAROUND: doesn't call setAttributes(), so we do it here if (attrConfigurators != null) setAttributes(attrConfigurators); def.saveDefinition(); ConcurrentUtil.removeCurrent(BeanConfigurator.class); /* * refresh all beans */ Bean.clearCache(); //return null to let the session-navigation return to the last element. return null; } public Object actionReset() { defAccessor.set("isdefault", true); def.deleteDefinition(); ConcurrentUtil.removeCurrent(BeanConfigurator.class); /* * refresh all beans */ Bean.clearCache(); return null; } @Override public int hashCode() { return def.hashCode(); } @Override public boolean equals(Object obj) { return obj instanceof BeanConfigurator ? def.equals(((BeanConfigurator) obj).def) : false; } @Override public String toString() { return Util.toString(getClass(), def); } @de.tsl2.nano.bean.annotation.Action(name = "createRuleOrAction", argNames = { "newActionName", "actionType", "actionExpression" }) public void actionCreateRuleOrAction(String newActionName, @de.tsl2.nano.bean.annotation.Constraint(defaultValue = "%: RuleScript (--> JavaScript)", allowed = { "§: Rule (--> Operation)", "%: RuleScript (--> JavaScript)", "!: Action (--> Java)" , "?: Query (--> SQL statement)", "@: Web (--> URL/REST)" }) String actionType, /*@de.tsl2.nano.bean.annotation.Constraint(pattern = ".*") */String actionExpression) { if (actionType.startsWith("%")) ENV.get(Pool.class).add(new RuleScript<>(newActionName, actionExpression, null)); else if (actionType.startsWith("§")) ENV.get(Pool.class).add(new Rule(newActionName, actionExpression, null)); else if (actionType.startsWith("!")) ENV.get(Pool.class).add(new Action(newActionName, actionExpression)); else if (actionType.startsWith("@")) ENV.get(Pool.class).add(WebClient.create(actionExpression, def.getDeclaringClass())); else if (actionType.startsWith("?")) ENV.get(Pool.class).add(new Query(newActionName, actionExpression, true, null)); } @de.tsl2.nano.bean.annotation.Action(name = "addAction", argNames = { "specifiedAction" }) public void actionAddAction( @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_ENVFILES + ".*specification/action.*") String specifiedAction) { //check, if action available specifiedAction = StringUtil.substring(FileUtil.replaceToJavaSeparator(specifiedAction), "/", ".", true); ENV.get(Pool.class).get(specifiedAction, Action.class); SpecifiedAction action = new SpecifiedAction(specifiedAction, null); def.addAction(action); } @de.tsl2.nano.bean.annotation.Action(name = "createCompositor", argNames = { "baseType", "baseAttributeName", "targetAttributeName", "iconAttributeName" }) public void actionCreateCompositor( @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPCLASSES) String baseType, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String baseAttribute, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String targetAttribute, @de.tsl2.nano.bean.annotation.Constraint(nullable=true, allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String iconAttribute) { createCompositor(baseType, baseAttribute, targetAttribute, iconAttribute); Bean.clearCache(); } public Compositor createCompositor(String baseType, String baseAttribute, String targetAttribute, String iconAttribute) { Compositor compositor = createCompositorBean(Compositor.class, "icons/properties.png", baseType, baseAttribute, null, targetAttribute, iconAttribute); compositor.saveDefinition(); return compositor; } @de.tsl2.nano.bean.annotation.Action(name = "createController" , argNames = {"baseType", "baseAttributeName", "targetType", "targetAttributeName", "iconAttributeName", "increaseAttribute", "increaseCount", "increaseStep", }) public void actionCreateController ( @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPCLASSES) String baseType, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String baseAttribute, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPCLASSES) String targetType, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String targetAttribute, @de.tsl2.nano.bean.annotation.Constraint(nullable=true, allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String iconAttribute, @de.tsl2.nano.bean.annotation.Constraint(allowed=ConstraintValueSet.ALLOWED_APPBEANATTRS) String increaseAttribute, int increaseCount, int increaseStep ) { createControllerBean(notnull(baseType), notnull(baseAttribute), nullable(targetType), notnull(targetAttribute), nullable(iconAttribute), nullable(increaseAttribute), increaseCount, increaseStep, false, true, false); //TODO: let the user configure the presentationvalues Bean.clearCache(); } private String nullable(String arg) { return ConstraintValueSet.NULL_CLASS.equals(arg) ? null : arg; } private String notnull(String arg) { ManagedException.assertion(!ConstraintValueSet.NULL_CLASS.equals(arg), arg); return arg; } public Controller createControllerBean(String baseType, String baseAttribute, String targetType, String targetAttribute, String iconAttribute, String increaseAttribute, int increaseCount, int increaseStep, boolean showText, boolean transparent, boolean creationOnly) { Controller controller = createCompositorBean(Controller.class, "icons/cascade.png", baseType, baseAttribute, targetType, targetAttribute, iconAttribute); controller.setPresentationValues(showText, transparent, creationOnly); if (!Util.isEmpty(increaseAttribute)) { increaseAttribute = StringUtil.substring(increaseAttribute, ".", null, true); controller.setItemProvider(new Increaser(increaseAttribute, increaseCount, increaseStep)); } controller.saveDefinition(); return controller; } /** * createCompositorBean * @param baseType * @param baseAttribute * @param targetAttribute * @param iconAttribute */ public C createCompositorBean(Class compositorExtension, String compositorIcon, String baseType, String baseAttribute, String targetType, String targetAttribute, String iconAttribute) { BeanClass bcBaseType = BeanClass.createBeanClass(baseType); BeanClass bcTargetType = !Util.isEmpty(targetType) ? BeanClass.createBeanClass(targetType) : null; //check the attributes baseAttribute = StringUtil.substring(baseAttribute, ".", null, true); targetAttribute = Util.nonEmpty(StringUtil.substring(targetAttribute, ".", null, true)); iconAttribute = Util.nonEmpty(StringUtil.substring(iconAttribute, ".", null, true)); bcBaseType.getAttribute(baseAttribute); if (iconAttribute != null) bcBaseType.getAttribute(iconAttribute); if (targetAttribute != null) if (bcTargetType != null) bcTargetType.getAttribute(targetAttribute); else def.getAttribute(targetAttribute); //now create the compositor Compositor compositor = BeanClass.createInstance(compositorExtension, def.getClazz(), bcBaseType.getClazz(), baseAttribute, targetAttribute, iconAttribute); if (bcTargetType != null) compositor.setTargetType(bcTargetType.getClazz()); compositor.getPresentable().setIcon(compositorIcon); // compositor.getActions(); return (C) compositor; } @de.tsl2.nano.bean.annotation.Action(name = "createSheet", argNames = { "title", "cols", "rows" }) public void actionCreateSheet( String title, int cols, int rows) { new CSheet(title, cols, rows).save(); } @de.tsl2.nano.bean.annotation.Action(name = "addAttribute", argNames = { "attributeType", "attributeExpression"}) public void actionAddAttribute( @de.tsl2.nano.bean.annotation.Constraint(allowed = {" : (--> PathExpression)", "§: Rule (--> Operation)", "%: RuleScript (--> JavaScript)", "!: Action (--> Java)" , "?: Query (--> sql statement)", "@: Web (--> URL/REST)" }) String attributeType, String attributeExpression) { addAttribute(attributeType, attributeExpression); def.saveDefinition(); Bean.clearCache(); } public AttributeDefinition addAttribute(String attributeType, String attributeExpression) { if (Util.isEmpty(attributeType)) { ManagedException.assertion(!Util.isEmpty(attributeExpression), "At least attributeExpression or attributeType must be filled!", attributeExpression, attributeType); attributeType = String.valueOf(attributeExpression.charAt(0)); } else attributeType = String.valueOf(attributeType.charAt(0)).trim(); attributeExpression = attributeType.length() > 0 && !Util.isEmpty(attributeExpression) && attributeType.charAt(0) == attributeExpression.charAt(0) ? attributeExpression : attributeType + attributeExpression; ExpressionDescriptor exDescr = new ExpressionDescriptor<>(def.getDeclaringClass(), attributeExpression); AttributeDefinition attr = def.addAttribute(exDescr.getName(), exDescr.toInstance(), null, null); attr.getPresentation().setType(IPresentable.TYPE_DEPEND); attr.getPresentation().setStyle(IPresentable.UNDEFINED); return attr; } }