org.openengsb.core.ekb.common.EDBConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.openengsb.core.ekb.common.EDBConverter.java

Source

/**
 * Licensed to the Austrian Association for Software Tool Integration (AASTI)
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. The AASTI licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.openengsb.core.ekb.common;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.openengsb.core.api.context.ContextHolder;
import org.openengsb.core.api.model.FileWrapper;
import org.openengsb.core.api.model.OpenEngSBModel;
import org.openengsb.core.api.model.OpenEngSBModelEntry;
import org.openengsb.core.edb.api.EDBConstants;
import org.openengsb.core.edb.api.EDBObject;
import org.openengsb.core.edb.api.EDBObjectEntry;
import org.openengsb.core.edb.api.EngineeringDatabaseService;
import org.openengsb.core.ekb.api.EKBCommit;
import org.openengsb.core.ekb.api.EKBException;
import org.openengsb.core.util.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The EDBConverter class responsibility is the converting between EDBObjects and models and the vice-versa.
 */
public class EDBConverter {
    private static final Logger LOGGER = LoggerFactory.getLogger(EDBConverter.class);
    private EngineeringDatabaseService edbService;

    public EDBConverter(EngineeringDatabaseService edbService) {
        this.edbService = edbService;
    }

    /**
     * Converts an EDBObject to a model of the given model type.
     */
    @SuppressWarnings("unchecked")
    public <T> T convertEDBObjectToModel(Class<T> model, EDBObject object) {
        return (T) convertEDBObjectToUncheckedModel(model, object);
    }

    /**
     * Converts a list of EDBObjects to a list of models of the given model type.
     */
    public <T> List<T> convertEDBObjectsToModelObjects(Class<T> model, List<EDBObject> objects) {
        List<T> models = new ArrayList<T>();
        for (EDBObject object : objects) {
            T instance = convertEDBObjectToModel(model, object);
            if (instance != null) {
                models.add(instance);
            }
        }
        return models;
    }

    /**
     * Tests if an EDBObject has the correct model class in which it should be converted. Returns false if the model
     * type is not fitting, returns true if the model type is fitting or model type is unknown.
     */
    private boolean checkEDBObjectModelType(EDBObject object, Class<?> model) {
        String modelClass = object.getString(EDBConstants.MODEL_TYPE);
        if (modelClass == null) {
            LOGGER.warn(String.format("The EDBObject with the oid %s has no model type information."
                    + "The resulting model may be a different model type than expected.", object.getOID()));
        }
        if (modelClass != null && !modelClass.equals(model.getName())) {
            return false;
        }
        return true;
    }

    /**
     * Converts an EDBObject to a model by analyzing the object and trying to call the corresponding setters of the
     * model.
     */
    private Object convertEDBObjectToUncheckedModel(Class<?> model, EDBObject object) {
        if (!checkEDBObjectModelType(object, model)) {
            return null;
        }
        EDBConverterUtils.filterEngineeringObjectInformation(object, model);
        List<OpenEngSBModelEntry> entries = new ArrayList<OpenEngSBModelEntry>();
        for (PropertyDescriptor propertyDescriptor : ModelUtils.getPropertyDescriptorsForClass(model)) {
            if (propertyDescriptor.getWriteMethod() == null
                    || propertyDescriptor.getName().equals(ModelUtils.MODEL_TAIL_FIELD_NAME)) {
                continue;
            }
            Object value = getValueForProperty(propertyDescriptor, object);
            Class<?> propertyClass = propertyDescriptor.getPropertyType();
            if (propertyClass.isPrimitive()) {
                entries.add(new OpenEngSBModelEntry(propertyDescriptor.getName(), value,
                        ClassUtils.primitiveToWrapper(propertyClass)));
            } else {
                entries.add(new OpenEngSBModelEntry(propertyDescriptor.getName(), value, propertyClass));
            }
        }

        for (Map.Entry<String, EDBObjectEntry> objectEntry : object.entrySet()) {
            EDBObjectEntry entry = objectEntry.getValue();
            Class<?> entryType;
            try {
                entryType = model.getClassLoader().loadClass(entry.getType());
                entries.add(new OpenEngSBModelEntry(entry.getKey(), entry.getValue(), entryType));
            } catch (ClassNotFoundException e) {
                LOGGER.error("Unable to load class {} of the model tail", entry.getType());
            }
        }
        return ModelUtils.createModel(model, entries);
    }

    /**
     * Generate the value for a specific property of a model out of an EDBObject.
     */
    private Object getValueForProperty(PropertyDescriptor propertyDescriptor, EDBObject object) {
        Method setterMethod = propertyDescriptor.getWriteMethod();
        String propertyName = propertyDescriptor.getName();
        Object value = object.getObject(propertyName);
        Class<?> parameterType = setterMethod.getParameterTypes()[0];

        // TODO: OPENENGSB-2719 do that in a better way than just an if-else series
        if (Map.class.isAssignableFrom(parameterType)) {
            List<Class<?>> classes = getGenericMapParameterClasses(setterMethod);
            value = getMapValue(classes.get(0), classes.get(1), propertyName, object);
        } else if (List.class.isAssignableFrom(parameterType)) {
            Class<?> clazz = getGenericListParameterClass(setterMethod);
            value = getListValue(clazz, propertyName, object);
        } else if (parameterType.isArray()) {
            Class<?> clazz = parameterType.getComponentType();
            value = getArrayValue(clazz, propertyName, object);
        } else if (value == null) {
            return null;
        } else if (OpenEngSBModel.class.isAssignableFrom(parameterType)) {
            EDBObject obj = edbService.getObject((String) value);
            value = convertEDBObjectToUncheckedModel(parameterType, obj);
            object.remove(propertyName);
        } else if (parameterType.equals(FileWrapper.class)) {
            FileWrapper wrapper = new FileWrapper();
            String filename = object.getString(propertyName + EDBConverterUtils.FILEWRAPPER_FILENAME_SUFFIX);
            String content = (String) value;
            wrapper.setFilename(filename);
            wrapper.setContent(Base64.decodeBase64(content));
            value = wrapper;
            object.remove(propertyName + EDBConverterUtils.FILEWRAPPER_FILENAME_SUFFIX);
        } else if (parameterType.equals(File.class)) {
            return null;
        } else if (object.containsKey(propertyName)) {
            if (parameterType.isEnum()) {
                value = getEnumValue(parameterType, value);
            } else if (Number.class.isAssignableFrom(parameterType)) {
                value = NumberUtils.createNumber((String) value);
            }
        }
        object.remove(propertyName);
        return value;
    }

    /**
     * Get the type of the list parameter of a setter.
     */
    private Class<?> getGenericListParameterClass(Method setterMethod) {
        return getGenericParameterClasses(setterMethod, 1).get(0);
    }

    /**
     * Get the type of the map parameter of a setter
     */
    private List<Class<?>> getGenericMapParameterClasses(Method setterMethod) {
        return getGenericParameterClasses(setterMethod, 2);
    }

    /**
     * Loads the generic parameter classes up to the given depth (1 for lists, 2 for maps)
     */
    private List<Class<?>> getGenericParameterClasses(Method setterMethod, int depth) {
        Type t = setterMethod.getGenericParameterTypes()[0];
        ParameterizedType pType = (ParameterizedType) t;
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (int i = 0; i < depth; i++) {
            classes.add((Class<?>) pType.getActualTypeArguments()[i]);
        }
        return classes;
    }

    /**
     * Gets a list object out of an EDBObject.
     */
    @SuppressWarnings("unchecked")
    private <T> List<T> getListValue(Class<T> type, String propertyName, EDBObject object) {
        List<T> temp = new ArrayList<T>();
        for (int i = 0;; i++) {
            String property = EDBConverterUtils.getEntryNameForList(propertyName, i);
            Object obj = object.getObject(property);
            if (obj == null) {
                break;
            }
            if (OpenEngSBModel.class.isAssignableFrom(type)) {
                obj = convertEDBObjectToUncheckedModel(type, edbService.getObject(object.getString(property)));
            }
            temp.add((T) obj);
            object.remove(property);
        }
        return temp;
    }

    /**
     * Gets an array object out of an EDBObject.
     */
    @SuppressWarnings("unchecked")
    private <T> T[] getArrayValue(Class<T> type, String propertyName, EDBObject object) {
        List<T> elements = getListValue(type, propertyName, object);
        T[] ar = (T[]) Array.newInstance(type, elements.size());
        return elements.toArray(ar);
    }

    /**
     * Gets a map object out of an EDBObject.
     */
    private Object getMapValue(Class<?> keyType, Class<?> valueType, String propertyName, EDBObject object) {
        Map<Object, Object> temp = new HashMap<Object, Object>();
        for (int i = 0;; i++) {
            String keyProperty = EDBConverterUtils.getEntryNameForMapKey(propertyName, i);
            String valueProperty = EDBConverterUtils.getEntryNameForMapValue(propertyName, i);
            if (!object.containsKey(keyProperty)) {
                break;
            }
            Object key = object.getObject(keyProperty);
            Object value = object.getObject(valueProperty);
            if (OpenEngSBModel.class.isAssignableFrom(keyType)) {
                key = convertEDBObjectToUncheckedModel(keyType, edbService.getObject(key.toString()));
            }
            if (OpenEngSBModel.class.isAssignableFrom(valueType)) {
                value = convertEDBObjectToUncheckedModel(valueType, edbService.getObject(value.toString()));
            }
            temp.put(key, value);
            object.remove(keyProperty);
            object.remove(valueProperty);
        }
        return temp;
    }

    /**
     * Gets an enum value out of an object.
     */
    private Object getEnumValue(Class<?> type, Object value) {
        Object[] enumValues = type.getEnumConstants();
        for (Object enumValue : enumValues) {
            if (enumValue.toString().equals(value.toString())) {
                value = enumValue;
                break;
            }
        }
        return value;
    }

    /**
     * Converts the models of an EKBCommit to EDBObjects and return an object which contains the three corresponding
     * lists
     */
    public ConvertedCommit convertEKBCommit(EKBCommit commit) {
        ConvertedCommit result = new ConvertedCommit();
        ConnectorInformation information = EDBConverterUtils.getConnectorInformationOfEKBCommit(commit);
        result.setInserts(convertModelsToEDBObjects(commit.getInserts(), information));
        result.setUpdates(convertModelsToEDBObjects(commit.getUpdates(), information));
        result.setDeletes(convertModelsToEDBObjects(commit.getDeletes(), information));
        return result;
    }

    /**
     * Convert a list of models to a list of EDBObjects (the version retrieving is not considered here. This is done in
     * the EDB directly).
     */
    public List<EDBObject> convertModelsToEDBObjects(List<OpenEngSBModel> models, ConnectorInformation info) {
        List<EDBObject> result = new ArrayList<EDBObject>();
        if (models != null) {
            for (Object model : models) {
                result.addAll(convertModelToEDBObject(model, info));
            }
        }
        return result;
    }

    /**
     * Converts an OpenEngSBModel object to an EDBObject (the version retrieving is not considered here. This is done in
     * the EDB directly).
     */
    public List<EDBObject> convertModelToEDBObject(Object model, ConnectorInformation info) {
        if (!OpenEngSBModel.class.isAssignableFrom(model.getClass())) {
            throw new IllegalArgumentException("This function need to get a model passed");
        }
        List<EDBObject> objects = new ArrayList<EDBObject>();
        if (model != null) {
            convertSubModel((OpenEngSBModel) model, objects, info);
        }
        return objects;
    }

    /**
     * Recursive function to generate a list of EDBObjects out of a model object.
     */
    private String convertSubModel(OpenEngSBModel model, List<EDBObject> objects, ConnectorInformation info) {
        String contextId = ContextHolder.get().getCurrentContextId();
        String oid = EDBConverterUtils.createOID(model, contextId);
        EDBObject object = new EDBObject(oid);
        try {
            EDBConverterUtils.fillEDBObjectWithEngineeringObjectInformation(object, model);
        } catch (IllegalAccessException e) {
            LOGGER.warn("Unable to fill completely the EngineeringObjectInformation into the EDBObject", e);
            throw new EKBException("Unable to fill completely the EngineeringObjectInformation into the EDBObject",
                    e);
        }
        for (OpenEngSBModelEntry entry : model.toOpenEngSBModelEntries()) {
            if (entry.getValue() == null) {
                continue;
            } else if (entry.getType().equals(FileWrapper.class)) {
                try {
                    FileWrapper wrapper = (FileWrapper) entry.getValue();
                    String content = Base64.encodeBase64String(wrapper.getContent());
                    object.putEDBObjectEntry(entry.getKey(), content, String.class);
                    object.putEDBObjectEntry(entry.getKey() + EDBConverterUtils.FILEWRAPPER_FILENAME_SUFFIX,
                            wrapper.getFilename(), String.class);
                } catch (IOException e) {
                    LOGGER.error(e.getMessage());
                }
            } else if (OpenEngSBModel.class.isAssignableFrom(entry.getType())) {
                OpenEngSBModel temp = (OpenEngSBModel) entry.getValue();
                String subOid = convertSubModel(temp, objects, info);
                object.putEDBObjectEntry(entry.getKey(), subOid, String.class);
            } else if (List.class.isAssignableFrom(entry.getType())) {
                List<?> list = (List<?>) entry.getValue();
                if (list == null || list.size() == 0) {
                    continue;
                }
                Boolean modelItems = null;
                for (int i = 0; i < list.size(); i++) {
                    Object item = list.get(i);
                    if (modelItems == null) {
                        modelItems = OpenEngSBModel.class.isAssignableFrom(item.getClass());
                    }
                    if (modelItems) {
                        item = convertSubModel((OpenEngSBModel) item, objects, info);
                    }
                    String entryName = EDBConverterUtils.getEntryNameForList(entry.getKey(), i);
                    object.putEDBObjectEntry(entryName, item, item.getClass());
                }
            } else if (entry.getType().isArray()) {
                Object[] array = (Object[]) entry.getValue();
                if (array == null || array.length == 0) {
                    continue;
                }
                Boolean modelItems = null;
                for (int i = 0; i < array.length; i++) {
                    Object item = array[i];
                    if (modelItems == null) {
                        modelItems = OpenEngSBModel.class.isAssignableFrom(item.getClass());
                    }
                    if (modelItems) {
                        item = convertSubModel((OpenEngSBModel) item, objects, info);
                    }
                    String entryName = EDBConverterUtils.getEntryNameForList(entry.getKey(), i);
                    object.putEDBObjectEntry(entryName, item, item.getClass());
                }
            } else if (Map.class.isAssignableFrom(entry.getType())) {
                Map<?, ?> map = (Map<?, ?>) entry.getValue();
                if (map == null || map.size() == 0) {
                    continue;
                }
                Boolean keyIsModel = null;
                Boolean valueIsModel = null;
                int i = 0;
                for (Map.Entry<?, ?> ent : map.entrySet()) {
                    if (keyIsModel == null) {
                        keyIsModel = OpenEngSBModel.class.isAssignableFrom(ent.getKey().getClass());
                    }
                    if (valueIsModel == null) {
                        valueIsModel = OpenEngSBModel.class.isAssignableFrom(ent.getValue().getClass());
                    }
                    Object key = ent.getKey();
                    Object value = ent.getValue();
                    if (keyIsModel) {
                        key = convertSubModel((OpenEngSBModel) key, objects, info);
                    }
                    if (valueIsModel) {
                        value = convertSubModel((OpenEngSBModel) value, objects, info);
                    }
                    object.putEDBObjectEntry(EDBConverterUtils.getEntryNameForMapKey(entry.getKey(), i), key);
                    object.putEDBObjectEntry(EDBConverterUtils.getEntryNameForMapValue(entry.getKey(), i), value);
                    i++;
                }
            } else {
                object.putEDBObjectEntry(entry.getKey(), entry.getValue(), entry.getClass());
            }
        }
        object.putEDBObjectEntry(EDBConstants.MODEL_TYPE, model.retrieveModelName());
        object.putEDBObjectEntry(EDBConstants.MODEL_TYPE_VERSION, model.retrieveModelVersion());
        object.putEDBObjectEntry("domainId", info.getDomainId());
        object.putEDBObjectEntry("connectorId", info.getConnectorId());
        object.putEDBObjectEntry("instanceId", info.getInstanceId());
        object.putEDBObjectEntry("contextId", contextId);
        objects.add(object);
        return oid;
    }
}