Package org.fusesource.ide.generator

Source Code of org.fusesource.ide.generator.NodeDefinition

/*******************************************************************************
* Copyright (c) 2014 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
*     Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.fusesource.ide.generator;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.apache.camel.model.DescriptionDefinition;
import org.apache.camel.model.ExpressionSubElementDefinition;
import org.apache.camel.model.OtherwiseDefinition;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.model.RedeliveryPolicyDefinition;
import org.apache.camel.model.ToDefinition;
import org.apache.camel.model.WhenDefinition;
import org.apache.camel.model.config.BatchResequencerConfig;
import org.apache.camel.model.config.StreamResequencerConfig;
import org.apache.camel.model.language.ExpressionDefinition;
import org.fusesource.ide.generator.model.Node;
import org.fusesource.scalate.introspector.BeanIntrospector;
import org.fusesource.scalate.introspector.BeanProperty;
import org.fusesource.scalate.introspector.Introspector;
import org.fusesource.scalate.introspector.Property;
import scala.collection.Iterator;

public class NodeDefinition<T> {

    private String name;
    private Class<T> clazz;
    private Generator generator;

    private String elementName;
    private String definitionName;
    private String id;

    private Introspector<T> introspector;

    private List<FieldProperty<T>> fields;
    private Map<String, Property<?>> propertyMap;
    private Map<String, Property<?>> allPropertyMap;
    private List<Property<?>> introspectionProperties;

    private Node modelNode;

    protected Set<String> primitivePropertyTypeNames = new HashSet<>(Arrays.asList(
            "java.lang.String", "java.lang.Boolean", "java.lang.Byte", "java.lang.Character",
            "java.lang.Short", "java.lang.Integer", "java.lang.Long", "java.lang.Float", "java.lang.Double", "java.util.Date"
    ));

    protected Set<String> ignoredProperties = new HashSet<>(Arrays.asList(
            "id", "description", "errorHandlerBuilder", "nodeFactory", "outputs", "parent", "whenClauses"
    ));

    protected Set<Class<?>> simplePropertyTypes = new HashSet<>(Arrays.asList(
            String.class, DescriptionDefinition.class, ExpressionDefinition.class,
            ExpressionSubElementDefinition.class, List.class
    ));

    protected Set<Class<?>> ignoreClasses = new HashSet<Class<?>>(Arrays.asList(
            ToDefinition.class
    ));

    public NodeDefinition(String name, Class<T> clazz, Generator generator) {
        this.name = name;
        this.clazz = clazz;
        this.generator = generator;

        XmlRootElement ann = clazz.getAnnotation(XmlRootElement.class);
        this.elementName = ann != null ? ann.name() : null;

        int idx = this.name.lastIndexOf("Definition");
        this.definitionName = idx > 0 ? name.substring(0, idx) : name;
        this.id = decapitalize(definitionName);

        this.introspector = new BeanIntrospector<>(clazz);

        this.modelNode = Generator.findElemById(generator.getXmlModel().nodes, getId());
    }

    public String decapitalize(String text) {
        return java.beans.Introspector.decapitalize(text);
    }

    public String capitalize(String text) {
        if (!(text == null || text.length() == 0)) {
            return text.substring(0, 1).toUpperCase() + text.substring(1);
        }
        return text;
    }

    public String splitCamelCase(String text) {
        StringBuilder buffer = new StringBuilder();
        char last = 'A';
        for (char c: text.toCharArray()) {
            if (Character.isLowerCase(last) && Character.isUpperCase(c)) {
                buffer.append(" ");
            }
            buffer.append(c);
            last = c;
        }
        return buffer.toString();
    }

    public boolean isProcessor() {
        return !Modifier.isAbstract(clazz.getModifiers()) &&
                ProcessorDefinition.class.isAssignableFrom(clazz) &&
                !ignoreClasses.contains(clazz);
    }

    protected boolean fieldBasedIntrospector() {
        XmlAccessorType annotation = clazz.getAnnotation(XmlAccessorType.class);
        return annotation != null && annotation.value() == XmlAccessType.FIELD;
    }

    public Map<String, Property<?>> getPropertyMap() {
        if (propertyMap == null) {
            propertyMap = new LinkedHashMap<>();
            // Scala collection -> Java collection
            Iterator<Property<T>> it = introspector.properties().toList().toIterator();
            while (it.hasNext()) {
                Property<T> p = it.next();
                if (!p.readOnly()) {
                    propertyMap.put(p.name(), p);
                }
            }
        }
        return propertyMap;
    }

    public Map<String, Property<?>> getAllPropertyMap() {
        if (allPropertyMap == null) {
            allPropertyMap = new LinkedHashMap<>();
            Iterator<Property<T>> it = introspector.properties().toList().toIterator();
            while (it.hasNext()) {
                Property<T> p = it.next();
                allPropertyMap.put(p.name(), p);
            }
        }
        return allPropertyMap;
    }

    public List<FieldProperty<T>> getFields() {
        if (fields == null) {
            boolean allFields = fieldBasedIntrospector();
            fields = new LinkedList<>();
            for (Field f : clazz.getDeclaredFields()) {
                if (Reflections.isStatic(f)) {
                    continue;
                }
                if (!allFields && getPropertyMap().containsKey(f.getName())) {
                    continue;
                }
                if (Reflections.hasAnnotation(f, XmlTransient.class)) {
                    continue;
                }
                fields.add(new FieldProperty<T>(f));
            }
        }
        return fields;
    }

    public List<Property<?>> getIntrospectionProperties() {
        if (introspectionProperties == null) {
            List<Property<?>> props;
            if (fieldBasedIntrospector()) {
                // TODO include parents settings?
                Class<? super T> s = clazz.getSuperclass();
                List<Property<?>> d = new LinkedList<>();
                if (s != null && s != Object.class) {
//                    d.addAll(generator.getNodeDefinitionClassMap().get(s).getIntrospectionProperties());
                    d.addAll(new NodeDefinition<>(s.getCanonicalName(), s, generator).getIntrospectionProperties());
                }
                d.addAll(getFields());
                props = d;
            } else {
                // only include fields where there is a property with a JAXB annotated setter
                List<Property<?>> badProperties = new LinkedList<>();
                for (Field f : clazz.getDeclaredFields()) {
                    if (Reflections.isStatic(f) || !hasAnnotatedProperty(f) || getPropertyMap().containsKey(f.getName())) {
                        continue;
                    }
                    badProperties.add(new FieldProperty(f));
                }
                props = new LinkedList<>();
                Iterator<Property<T>> it = introspector.properties().toList().toIterator();
                while (it.hasNext()) {
                    Property<T> p = it.next();
                    props.add(p);
                }
                props.addAll(badProperties);
            }
            introspectionProperties = new LinkedList<>();
            for (Property<?> p : props) {
                Class<?> t = p.propertyType();
                if (ExpressionSubElementDefinition.class.isAssignableFrom(t)) {
                    introspectionProperties.add(new ConvertedProperty<>(p, ExpressionDefinition.class));
                } else if (List.class.isAssignableFrom(t) && p.name().equals("expressions")) {
                    introspectionProperties.add(new ConvertedProperty<>(p, ExpressionDefinition.class));
                } else {
                    introspectionProperties.add(p);
                }
            }
        }
        return introspectionProperties;
    }

    private boolean hasAnnotatedProperty(Field f) {
        BeanProperty<?> property = (BeanProperty<?>) getAllPropertyMap().get(f.getName());
        if (property != null) {
            return hasMethodAnnotations(property, Generator.XML_ANNOTATIONS) ||
                    Reflections.hasAnnotations(f, Generator.XML_ANNOTATIONS);
        } else {
            try {
                String name = "set" + capitalize(f.getName());
                Method m = clazz.getMethod(name, f.getType());
                return Reflections.hasAnnotations(m, Generator.XML_ANNOTATIONS);
            } catch (Exception e) {
                return false;
            }
        }
    }

    protected boolean hasMethodAnnotations(BeanProperty<?> bp, Class[] cs) {
        PropertyDescriptor d = bp.descriptor();
        return Reflections.hasAnnotations(d.getWriteMethod(), cs) || Reflections.hasAnnotations(d.getReadMethod(), cs);
    }

    public List<Property<?>> properties() {
        List<Property<?>> props = new LinkedList<>();
        for (Property<?> n : getIntrospectionProperties()) {
            if (!ignoredProperties.contains(n.name()) && !isRefForUri(n) && !isTransient(n)) {
                props.add(n);
            }
        }
        return props;
    }

    protected boolean isRefForUri(Property<?> p) {
        String n = p.name();
        return (n.equals("ref") && getPropertyMap().containsKey("uri")) ||
                n.endsWith("Ref") && getPropertyMap().containsKey(n.substring(0, n.length() - 3) + "Uri");
    }

    protected boolean isTransient(Property<?> p) {
        return p instanceof BeanProperty && hasMethodAnnotation((BeanProperty<?>) p, XmlTransient.class);
    }

    protected <A extends Annotation> boolean hasMethodAnnotation(BeanProperty<?> bp, Class<A> c) {
        PropertyDescriptor d = bp.descriptor();
        return Reflections.hasAnnotation(d.getWriteMethod(), c) || Reflections.hasAnnotation(d.getReadMethod(), c);
    }

    public List<Property<?>> simpleProperties() {
        List<Property<?>> props = new LinkedList<>();
        for (Property<?> p : properties()) {
            if (!p.readOnly() && isSimplePropertyType(p) && !(definitionName.equals("Route") && p.name().equals("inputs"))) {
                props.add(p);
            }
        }
        return props;
    }

    public boolean isSimplePropertyType(Property<?> p) {
        Class<?> t = p.propertyType();
        return simplePropertyTypes.contains(t) || (Generator.eclipseMode &&
                (t.isPrimitive() || primitivePropertyTypeNames.contains(t.getCanonicalName()) || Enum.class.isAssignableFrom(t)));
    }

    public List<Property<?>> complexProperties() {
        List<Property<?>> props = new LinkedList<>();
        for (Property<?> p : properties()) {
            if (!p.readOnly() && !isSimplePropertyType(p)) {
                props.add(p);
            }
        }
        return props;
    }

    /**
     * The bean properties which are simple or expression properties excluding the common id & description properties
     *
     * @return
     */
    public List<Property<?>> beanProperties() {
        List<Property<?>> props = new LinkedList<>();
        props.addAll(simpleProperties());
        for (Property<?> p : complexProperties()) {
            if (isBeanProperty(p)) {
                props.add(p);
            }
        }
        return props;
    }

    public boolean isBeanProperty(Property<?> p) {
        boolean valid = !WhenDefinition.class.isAssignableFrom(p.propertyType()) &&
                !OtherwiseDefinition.class.isAssignableFrom(p.propertyType()) &&
                !Class.class.isAssignableFrom(p.propertyType());


        return isExpression(p) || RedeliveryPolicyDefinition.class.isAssignableFrom(p.propertyType()) ||
                BatchResequencerConfig.class.isAssignableFrom(p.propertyType()) ||
                StreamResequencerConfig.class.isAssignableFrom(p.propertyType()) ||
                (hasXmlAnnotatedField(p.name(), valid) && valid);
    }

    private boolean hasXmlAnnotatedField(String n, boolean valid) {
        FieldProperty<T> field = field(n);
        return field != null && Reflections.hasAnnotations(field.getField(), Generator.XML_ANNOTATIONS) && valid;
    }

    private FieldProperty<T> field(String n) {
        for (FieldProperty<T> field : getFields()) {
            if (field.name().equals(n)) {
                return field;
            }
        }
        return null;
    }

    public boolean isExpression(Property<?> p) {
        return ExpressionDefinition.class.isAssignableFrom(p.propertyType());
    }

    public XmlElement[] xmlElements(Property p) {
        FieldProperty<T> fp = field(p.name());
        if (fp != null) {
            XmlElements ann = fp.getField().getAnnotation(XmlElements.class);
            if (ann != null) {
                return ann.value();
            } else {
                return new XmlElement[0];
            }
        } else {
            return new XmlElement[0];
        }
    }

    /* getters & setters */

    public String getElementName() {
        return elementName;
    }

    public String getId() {
        return id;
    }

    public String getDescription() {
        return getTitle();
    }

    public String getTitle() {
        return splitCamelCase(definitionName);
    }

    public Class<T> getClazz() {
        return clazz;
    }

    public String getDefinitionName() {
        return definitionName;
    }

    public String getName() {
        return name;
    }

    public Introspector<T> getIntrospector() {
        return introspector;
    }

    public String tooltip() {
        return modelNode == null || modelNode.tooltip == null ? getDescription() : modelNode.tooltip;
    }

    public String propertyTooltip(String name) {
        return propertyElementTooltip(name, name);
    }

    public String propertyElementTooltip(String name, String defaultValue) {
        String text = defaultValue;
        if (modelNode != null) {
            for (org.fusesource.ide.generator.model.Property p : modelNode.properties) {
                if (p.id != null && (p.id.equals(name) || p.id.toLowerCase().equals(name.toLowerCase())) && p.tooltip != null) {
                    text = p.tooltip;
                }
            }
        }
        return text;
    }

    public String propertyElementLabel(String name) {
        String text = createDefaultLabel(name);
        if (modelNode != null) {
            for (org.fusesource.ide.generator.model.Property p : modelNode.properties) {
                if (p.id != null && (p.id.equals(name) || p.id.toLowerCase().equals(name.toLowerCase())) && p.label != null) {
                    text = p.label;
                }
            }
        }
        return text;
    }

    public String propertyLabel(String name) {
        return propertyElementLabel(name);
    }

    /**
     * Split the string using CamelCase, then lower case each word
     * @param text
     * @return
     */
    protected String createDefaultLabel(String text) {
        return capitalize(splitCamelCase(text));
    }

    public String propertyType(Property<?> p) {
        String name = p.propertyType().getSimpleName();
        if (name.startsWith("java.lang.")) {
            return name.substring("java.lang.".length());
        } else {
            return name;
        }
    }

    protected boolean isDefinitionName(String ... prefixes) {
        for (String p : prefixes) {
            if (getId().startsWith(p)) {
                return true;
            }
        }
        return false;
    }

    public String group() {
        if (isDefinitionName("bean", "log", "process", "to", "from", "endpoint")) {
            return "Endpoints";
        }
        else if (isDefinitionName("aggregate", "choice", "dynamicRouter", "filter", "idempotentConsumer", "loadBalance", "multicast", "otherwise", "pipeline", "recipient", "resequence", "routing", "split", "sort", "when", "wireTap")) {
            return "Routing";
        }
        else if (isDefinitionName("aOP", "catch", "delay", "finally", "intercept", "loop", "on", "rollback", "throttle", "throw", "transacted", "try")) {
            return "Control Flow";
        }
        else if (isDefinitionName("convert", "enrich", "inO", "marshal", "pollEnrich", "remove", "set", "transform", "unmarshal")) {
            return "Transformation";
        }
        else {
            return "Miscellaneous";
        }
    }

    public String defaultImageName() {
        switch (group()) {
            case "Transformation":
                return "transform";
            case "Endpoints":
                return "endpoint";
            default:
                return "generic";
        }
    }

    /**
     * Returns the contextId for the nodes
     * @return
     */
    public String documentationFile() {
        return modelNode == null || modelNode.contextId == null ? "allEIPs" : modelNode.contextId;
    }

    public boolean isPrimitiveBooleanPropertyType(Property<?> p) {
        return p.propertyType().getSimpleName().equals("boolean");
    }

    public String getOrIsMethodPrefix(Property<?> p) {
        return isPrimitiveBooleanPropertyType(p) ? "is" : "get";
    }

    public boolean isBooleanPropertyType(Property<?> p) {
        return isPrimitiveBooleanPropertyType(p) || p.propertyType().getCanonicalName().equals("java.lang.Boolean");
    }

    public boolean isEnumPropertyType(Property<?> p) {
        return Enum.class.isAssignableFrom(p.propertyType());
    }

    public boolean isListPropertyType(Property<?> p) {
        return List.class.isAssignableFrom(p.propertyType());
    }

    public String getterExpression(String source, Property<?> p) {
        String prefix = isPrimitiveBooleanPropertyType(p) ? "is" : "get";
        String method = prefix + capitalize(p.name());
        try {
            Method m = clazz.getMethod(method);
            return source + "." + method + "()";
        } catch (Exception e) {
            // lets use reflection if no method
            String pn;
            switch (propertyType(p)) {
                case "boolean":
                    pn = "Boolean";
                    break;
                case "byte":
                    pn = "Byte";
                    break;
                case "short":
                    pn = "Short";
                    break;
                case "long":
                    pn = "Long";
                    break;
                case "int":
                    pn = "Integer";
                    break;
                case "float":
                    pn = "Float";
                    break;
                case "double":
                    pn = "Double";
                    break;
                default:
                    pn = propertyType(p);
            }
            return "Objects.<" + pn + ">getField(" + source + ", \"" + p.name() + "\")";
        }
    }

    public String setterExpression(String source, Property<?> p, String value) {
        String method = "set" + capitalize(p.name());
        try {
            clazz.getMethod(method, p.propertyType());
            return source + "." + method + "(" + value + ")";
        } catch (Exception e) {
            // lets use reflection if no method
            return "Objects.setField(" + source + ", \"" + p.name() + "\", " + value + ")";
        }
    }

}

/*
    def isRequired(p: Property[_]): Boolean = p match {
        case bp: BeanProperty[_] =>
            hasMethodAnnotation(bp, classOf[Required])
        case _ => false
    }


    def isExpressionNode = !Modifier.isAbstract(clazz.getModifiers) && classOf[ExpressionNode].isAssignableFrom(clazz) &&
            !ignoreClasses.contains(clazz)

    def isBeanRef(prop: Property[_]) = classOf[BeanDefinition].isAssignableFrom(clazz) && prop.name == "ref"

    def isBeanMethod(prop: Property[_]) = classOf[BeanDefinition].isAssignableFrom(clazz) && prop.name == "method"



    def canAcceptInput(): Boolean = {
        return isProcessor && CamelModelUtils.canAcceptInput(clazz.getName())
    }

    def canAcceptOutput(): Boolean = {
        import generator.camelContext
        return isProcessor && CamelModelJavaHelper.canAcceptOutput(camelContext, clazz)
    }

    def isNextSiblingStepAddedAsNodeChild(): Boolean = {
        import generator.camelContext
        return isProcessor && CamelModelJavaHelper.isNextSiblingStepAddedAsNodeChild(camelContext, clazz)
    }




    // TODO extract from annotation on the definition?










    def role = id match {
        //case "choice" => "\"choice\", \"interaction\""
        case "choice" => "\"choice\", \"to\""
        case "when" => "\"toWhenOtherwise\", \"from\""
        case "otherwise" => "\"toWhenOtherwise\", \"from\""
        case _ => "\"from\", \"to\""
    }

    def containsRole = id match {
        case "choice" => "\"when\", \"otherwise\""
        case _ => "\"to\", \"choice\""
    }

    //def dimension = Dimensions.dimensions.get(svg).getOrElse(Dimension.doesNotExist)

    def svg = generator.findIconFileOrElse("view/", "node." + id, List("svg"),
    if (group == "Transformation") {
        "node.transform.svg"
    }
    else if (group == "Endpoints") {
        "node.endpoint.svg"
    }
    else {
        "node.generic.svg"
    })


    def icon = generator.findIconFileOrElse("icons/", "node." + id, "node.generic.gif")


    def createNode(properties: ju.Map[String, AnyRef]): Any = {
        import generator.camelContext
        val bean = camelContext.getInjector.newInstance(clazz)

        for ((k, v) <- properties) {
            debug("  " + k + " = " + v)
            def ignoreValue = debug("Ignored key: " + k + " value: " + v + " since it is blank")

            introspector.property(k) match {
                case Some(p) =>
                    v match {
                    case "" => ignoreValue
                    case None => ignoreValue
                    case null => ignoreValue

                    case _ =>
                        debug("    setting property: " + p + " to: " + v)
                        val convertedValue = if (classOf[ExpressionDefinition].isAssignableFrom(p.propertyType)) {
                        var lang = (new Objects()).getOrElse(properties.get("language").asInstanceOf[String], "XPath").toLowerCase
                        println("====== attempt to set an expression to: " + v + " for lang: " + lang)
                        new LanguageExpression(lang, v.toString)
                    } else {
                        camelContext.getTypeConverter.mandatoryConvertTo(p.propertyType, v)
                    }
                        p.set(bean, convertedValue)
                        debug("    property is now: " + p(bean) + " after evaluating to " + convertedValue)
                }

                case _ =>
                    warn("No such property: " + k + " in " + this)
            }
        }
        println("Created: " + bean)
        bean

    }
}

*/ 
TOP

Related Classes of org.fusesource.ide.generator.NodeDefinition

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.