org.mule.modules.servicesource.metadata.MetadataExtractor.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.modules.servicesource.metadata.MetadataExtractor.java

Source

/**
 * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.md file.
 */

package org.mule.modules.servicesource.metadata;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.mule.common.metadata.DefaultDefinedMapMetaDataModel;
import org.mule.common.metadata.DefaultListMetaDataModel;
import org.mule.common.metadata.DefaultMetaData;
import org.mule.common.metadata.DefaultParameterizedMapMetaDataModel;
import org.mule.common.metadata.DefaultSimpleMetaDataModel;
import org.mule.common.metadata.MetaData;
import org.mule.common.metadata.MetaDataModel;
import org.mule.common.metadata.datatype.DataType;
import org.mule.modules.servicesource.ServiceSourceCollection;
import org.mule.modules.servicesource.model.Amount;
import org.mule.modules.servicesource.model.PropertyDescriptor;

import com.google.gson.annotations.SerializedName;

/**
 * 
 * @author mariano.gonzalez@mulesoft.com
 * 
 */
public class MetadataExtractor {

    private Map<Class<?>, Map<String, MetaDataModel>> pojoModelCache = new HashMap<Class<?>, Map<String, MetaDataModel>>();

    public MetaData extractStaticMetadata(ServiceSourceCollection collection) {
        return new DefaultMetaData(new DefaultDefinedMapMetaDataModel(describeStatic(collection)));
    }

    public MetaData extractMetadata(Map<String, Object> metadata, ServiceSourceCollection collection) {
        Map<String, MetaDataModel> model = describeStatic(collection);
        // System.out.println(metadata);

        this.describeExtensions(metadata, model);
        this.describeRelations(metadata, model);

        return new DefaultMetaData(new DefaultDefinedMapMetaDataModel(model));
    }

    @SuppressWarnings("unchecked")
    private void describeRelations(Map<String, Object> elements, Map<String, MetaDataModel> model) {

        List<Map<String, Object>> relationships = (List<Map<String, Object>>) elements.get("relations");
        List<Map<String, Object>> subTypes = (List<Map<String, Object>>) elements.get("subTypes");

        List<Map<String, Object>> subTypesRelationships = new ArrayList<Map<String, Object>>();
        if (subTypes != null) {
            for (Map<String, Object> subType : subTypes) {
                if (subType.get("relations") != null) {
                    subTypesRelationships.addAll((List<Map<String, Object>>) subType.get("relations"));
                }
            }
        }

        if (relationships == null && subTypesRelationships.isEmpty()) {
            return;
        }

        Map<String, MetaDataModel> relations = this.newModelMap();

        if (relationships != null) {
            createRelations(relationships, relations);
        }

        if (!subTypesRelationships.isEmpty()) {
            createRelations(subTypesRelationships, relations);
        }

        if (!relations.isEmpty()) {
            model.put("relations", new DefaultDefinedMapMetaDataModel(relations));
        }
    }

    private void createRelations(List<Map<String, Object>> relationships, Map<String, MetaDataModel> relations) {
        for (Map<String, Object> element : relationships) {

            String cardinality = (String) element.get("cardinality");

            if (StringUtils.isBlank(cardinality)) {
                throw new IllegalArgumentException("relation is missing cardinality element");
            }

            MetaDataModel keyModel = new DefaultSimpleMetaDataModel(DataType.STRING);

            Map<String, MetaDataModel> relationModel = this.newModelMap();
            relationModel.put("key", keyModel);

            String target = (String) element.get("target");
            if (target.contains("/")) {
                target = target.substring(0, target.indexOf("/"));
            }
            relations.put(String.format("%s (%s)", element.get("name"), target),
                    new DefaultDefinedMapMetaDataModel(relationModel));

            this.describeRelations(element, relationModel);
        }
    }

    @SuppressWarnings("unchecked")
    private void describeExtensions(Map<String, Object> metadata, Map<String, MetaDataModel> model) {

        Map<String, MetaDataModel> master = this.newModelMap();
        Map<String, MetaDataModel> tenant = this.newModelMap();

        for (Map<String, Object> ext : (List<Map<String, Object>>) metadata.get("properties")) {

            if ("true".equals(ext.get("extension"))) {
                MetaDataModel extensionModel = this.parseElementByType(ext);
                MetaDataModel meta = "true".equals(ext.get("isArray"))
                        ? new DefaultListMetaDataModel(extensionModel)
                        : extensionModel;
                ("true".equals(ext.get("master")) ? master : tenant).put((String) ext.get("name"), meta);
            }
        }

        Map<String, MetaDataModel> modelExtensions = this.newModelMap();
        modelExtensions.put("master", new DefaultDefinedMapMetaDataModel(master));
        modelExtensions.put("tenant", new DefaultDefinedMapMetaDataModel(tenant));

        model.put("extensions", new DefaultDefinedMapMetaDataModel(modelExtensions));
    }

    private Map<String, MetaDataModel> describeStatic(ServiceSourceCollection collection) {
        Map<String, MetaDataModel> model = this.newModelMap();
        describeModel(model, collection.getType(), null);

        return model;
    }

    private MetaDataModel parseElementByType(Map<String, Object> extension) {
        return this.parseElement((String) extension.get("type"));
    }

    private MetaDataModel parseElement(String type) {
        if ("string".equals(type)) {
            return new DefaultSimpleMetaDataModel(DataType.STRING);
        } else if ("number".equals(type)) {
            return new DefaultSimpleMetaDataModel(DataType.NUMBER);
        } else if ("lookup".equals(type)) {
            return describeModel(this.newModelMap(), PropertyDescriptor.class, null);
        } else if ("core.currency".equals(type)) {
            return describeModel(this.newModelMap(), Amount.class, null);
        } else if ("boolean".equals(type)) {
            return new DefaultSimpleMetaDataModel(DataType.BOOLEAN);
        } else if ("date".equals(type)) {
            return new DefaultSimpleMetaDataModel(DataType.DATE_TIME);
        } else {
            ServiceSourceCollection collection = ServiceSourceCollection.getByEntityName(type);
            if (collection != null) {
                return new DefaultDefinedMapMetaDataModel(describeStatic(collection));
            }
        }
        return null;
    }

    public MetaDataModel describeModel(Map<String, MetaDataModel> model, Class<?> type, Field field) {
        DataType dataType = getDataType(type);
        MetaDataModel result = null;

        if (DataType.POJO.equals(dataType)) {

            Map<String, MetaDataModel> cachedModel = this.pojoModelCache.get(type);

            if (cachedModel != null) {
                model = cachedModel;

            } else {

                this.pojoModelCache.put(type, model);

                for (Field pojoField : getFields(type)) {

                    if (pojoField.getType() == null || Modifier.isStatic(pojoField.getModifiers())) {
                        continue; // this is a native type
                    }

                    String name = pojoField.getName();

                    if ("extensions".equals(name) || "relationships".equals(name)) {
                        continue;
                    } else {
                        SerializedName annotation = pojoField.getAnnotation(SerializedName.class);
                        name = annotation != null ? annotation.value() : pojoField.getName();
                    }

                    model.put(name, describeModel(newModelMap(), pojoField.getType(), pojoField));
                }
            }

            result = new DefaultDefinedMapMetaDataModel(model);

        } else if (DataType.MAP.equals(dataType)) {
            result = new DefaultParameterizedMapMetaDataModel(new DefaultSimpleMetaDataModel(DataType.STRING),
                    new DefaultSimpleMetaDataModel(DataType.STRING));
            model.put(field.getName(), result);

        } else if (DataType.LIST.equals(dataType)) {

            Class<?> listType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
            result = new DefaultListMetaDataModel(describeModel(newModelMap(), listType, field));
            model.put(field.getName(), result);

        } else {

            result = new DefaultSimpleMetaDataModel(dataType);
            model.put(field.getName(), result);
        }

        return result;
    }

    private DataType getDataType(Class<?> type) {
        if (String.class.equals(type)) {
            return DataType.STRING;
        } else if (Integer.class.equals(type) || Long.class.equals(type) || Double.class.equals(type)) {
            return DataType.NUMBER;
        } else if (Boolean.class.equals(type)) {
            return DataType.BOOLEAN;
        } else if (Map.class.isAssignableFrom(type)) {
            return DataType.MAP;
        } else if (List.class.isAssignableFrom(type)) {
            return DataType.LIST;
        } else {
            return DataType.POJO;
        }

    }

    private Collection<Field> getFields(Class<?> type) {
        Collection<Field> fields = new ArrayList<Field>();

        while (!type.equals(Object.class)) {
            fields.addAll(Arrays.asList(type.getDeclaredFields()));
            type = type.getSuperclass();
        }

        return fields;
    }

    private Map<String, MetaDataModel> newModelMap() {
        return new HashMap<String, MetaDataModel>();
    }

}