de.openknowledge.jaxrs.versioning.conversion.CompatibilityMapper.java Source code

Java tutorial

Introduction

Here is the source code for de.openknowledge.jaxrs.versioning.conversion.CompatibilityMapper.java

Source

/*
 * Copyright (C) open knowledge GmbH
 * 
 * Licensed 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 de.openknowledge.jaxrs.versioning.conversion;

import java.util.Collection;

import org.apache.commons.lang3.StringUtils;

import de.openknowledge.jaxrs.versioning.Added;
import de.openknowledge.jaxrs.versioning.MovedFrom;
import de.openknowledge.jaxrs.versioning.Provider;
import de.openknowledge.jaxrs.versioning.Removed;

/**
 * @author Arne Limburg - open knowledge GmbH
 * @author Philipp Geers - open knowledge GmbH
 */
public class CompatibilityMapper {

    private VersionTypeFactory versionTypeFactory;

    CompatibilityMapper(VersionTypeFactory factory) {
        versionTypeFactory = factory;
    }

    public void map(Object object) {
        map(object, new DefaultVersionContext(object));
    }

    public void map(Object object, DefaultVersionContext context) {
        VersionType<?> versionType = versionTypeFactory.get(object.getClass());

        for (VersionProperty versionProperty : versionType.getProperties()) {
            MovedFrom movedFrom = versionProperty.getAnnotation(MovedFrom.class);
            Added added = versionProperty.getAnnotation(Added.class);

            if (movedFrom == null && added == null) {
                if (versionProperty.isCollection() && !versionProperty.isCollectionOfSimpleTypes()) {
                    if (!versionProperty.isDefault(object)) {
                        Collection<?> collection = (Collection<?>) versionProperty.get(object);
                        for (Object entry : collection) {
                            map(entry, context.getChildContext(entry));
                        }
                    }
                } else if (!versionProperty.isSimple() && !versionProperty.isCollectionOfSimpleTypes()) {
                    Object value;
                    if (!versionProperty.isDefault(object)) {
                        value = versionProperty.get(object);
                    } else {
                        value = versionTypeFactory.get(versionProperty.getType()).newInstance();
                        versionProperty.set(object, value);
                    }
                    map(value, context.getChildContext(value));
                }
                continue;
            }
            if (versionProperty.isDefault(object)) {
                updateDependentValues(new VersionPropertyValue(versionProperty, context));
            } else {
                Object value = versionProperty.get(object);
                setDependentValues(versionType, movedFrom, added, versionProperty, value, context);
                if (!versionProperty.isSimple()) {
                    map(value, context.getChildContext(value));
                }
            }
        }
    }

    private void updateDependentValues(VersionPropertyValue propertyValue) {
        DefaultVersionContext dependentContext = propertyValue.getContext();
        VersionType<?> dependentType = versionTypeFactory.get(dependentContext.getParent().getClass());

        MovedFrom movedFrom = propertyValue.getAnnotation(MovedFrom.class);
        if (movedFrom != null) {
            updateDependentValues(dependentType, propertyValue, movedFrom, dependentContext);
            if (!propertyValue.isDefault()) {
                return;
            }
        }
        String defaultValue = StringUtils.EMPTY;
        Class<? extends Provider> provider = Provider.class;

        Added added = propertyValue.getAnnotation(Added.class);
        if (added != null) {
            for (int i = 0; i < added.value().length; i++) {
                CollectionElementValue dependentValue = new CollectionElementValue(propertyValue.getProperty(), i,
                        propertyValue.getContext());
                updateDependentValues(dependentType, dependentValue, added.value()[i], dependentContext);
            }
            if (!propertyValue.isDefault()) {
                return;
            }
            for (String dependency : added.dependsOn()) {
                VersionPropertyValue dependentValue = getPropertyValue(dependentType, dependency, dependentContext);
                if (dependentValue.isDefault()) {
                    updateDependentValues(dependentValue);
                }
                if (dependentValue.isDefault()) {
                    // we can't call the provider for now. Will be done later
                    return;
                }
            }
            defaultValue = added.defaultValue();
            provider = added.provider();
        }
        setValue(propertyValue, provider, defaultValue, dependentContext);
    }

    private void updateDependentValues(VersionType<?> dependentType, VersionPropertyValue propertyValue,
            MovedFrom movedFrom, DefaultVersionContext dependentContext) {
        VersionPropertyValue dependentValue = getPropertyValue(dependentType, movedFrom.value(), dependentContext);
        if (dependentValue.isDefault()) {
            updateDependentValues(dependentValue);
        }
        if (!dependentValue.isDefault()) {
            propertyValue.set(dependentValue.get());
        }
    }

    private void setValue(VersionPropertyValue value, Class<? extends Provider> provider, String defaultValue,
            DefaultVersionContext context) {
        if (!provider.equals(Provider.class)) {
            Provider<?> providerInstance = (Provider<?>) versionTypeFactory.get(provider).newInstance();
            try {
                context.setPropertyName(value.getProperty().getName());
                value.set(providerInstance.get(context));
            } finally {
                context.setPropertyName(null);
            }
        } else if (!defaultValue.isEmpty()) {
            value.set(defaultValue);
        } else if (!value.getProperty().isSimple() && !value.getProperty().isCollection()) {
            Object instance = versionTypeFactory.get(value.getProperty().getType()).newInstance();
            value.set(instance);
            map(instance, context.getChildContext(instance));
        }
    }

    private void setDependentValues(VersionType<?> versionType, MovedFrom movedFrom, Added added,
            VersionProperty property, Object value, DefaultVersionContext context) {
        if (movedFrom != null) {
            setDependentValues(versionType, movedFrom.value(), value, context);
        }
        if (added != null) {
            for (int i = 0; i < added.value().length; i++) {
                setDependentValues(versionType, added.value()[i].value(),
                        new CollectionElementValue(property, i, context).get(), context);
            }
            for (String dependency : added.dependsOn()) {
                setRemovedValues(versionType, dependency, context);
            }
        }
    }

    private void setDependentValues(VersionType<?> versionType, String path, Object value,
            DefaultVersionContext context) {
        VersionPropertyValue propertyValue = getPropertyValue(versionType, path, context);
        propertyValue.set(value);
        MovedFrom movedFrom = propertyValue.getAnnotation(MovedFrom.class);
        Added added = propertyValue.getAnnotation(Added.class);
        VersionType<?> propertyParentType = versionTypeFactory
                .get(propertyValue.getContext().getParent().getClass());
        setDependentValues(propertyParentType, movedFrom, added, propertyValue.getProperty(), value,
                propertyValue.getContext());
    }

    private void setRemovedValues(VersionType<?> versionType, String dependsOn, DefaultVersionContext context) {
        VersionPropertyValue propertyValue = getPropertyValue(versionType, dependsOn, context);
        if (propertyValue.isDefault()) {
            Removed removed = propertyValue.getAnnotation(Removed.class);
            if (removed != null) {
                for (String dependency : removed.isDependencyOf()) {
                    if (getPropertyValue(versionType, dependency, context).isDefault()) {
                        // we can't call the provider for now. Will be done later
                        return;
                    }
                }
                setValue(propertyValue, removed.provider(), removed.defaultValue(), context);
                if (!propertyValue.isDefault()) {
                    Object value = propertyValue.get();
                    MovedFrom movedFrom = propertyValue.getAnnotation(MovedFrom.class);
                    Added added = propertyValue.getAnnotation(Added.class);
                    setDependentValues(versionType, movedFrom, added, propertyValue.getProperty(), value, context);
                }
            }
        }
    }

    private VersionPropertyValue getPropertyValue(VersionType<?> versionType, String path,
            DefaultVersionContext context) {
        return getPropertyValue(versionType, path.split("/"), 0, context);
    }

    private VersionPropertyValue getPropertyValue(VersionType<?> versionType, String[] pathElements, int index,
            DefaultVersionContext context) {
        String propertyName = pathElements[index];
        if (propertyName.equals("..")) {
            context = context.getParentContext();
            return getPropertyValue(versionTypeFactory.get(context.getParent().getClass()), pathElements, index + 1,
                    context);
        }
        int collectionIndex = -1;
        if (isCollectionProperty(propertyName)) {
            int bracketIndex = propertyName.lastIndexOf('[');
            propertyName = propertyName.substring(0, bracketIndex);
            collectionIndex = Integer
                    .parseInt(pathElements[index].substring(bracketIndex + 1, pathElements[index].length() - 1));
            if (collectionIndex < 0) {
                throw new IndexOutOfBoundsException("index in " + pathElements[index] + " may not be negative");
            }
        }
        VersionProperty property = versionType.getProperty(propertyName);
        if (property == null) {
            throw new IllegalArgumentException("@MoveFrom contains unknown property " + propertyName);
        }
        if (pathElements.length == index + 1) {
            return createPropertyValue(property, collectionIndex, context);
        }
        Object value = null;
        if (isCollection(collectionIndex)) {
            CollectionElementValue collectionElementValue = new CollectionElementValue(property, collectionIndex,
                    context);
            value = collectionElementValue.get();
        } else if (!property.isDefault(context.getParent())) {
            value = property.get(context.getParent());
        }
        if (value == null) {
            value = versionTypeFactory.get(property.getType()).newInstance();
        }
        return getPropertyValue(versionTypeFactory.get(property.getType()), pathElements, index + 1,
                context.getChildContext(value));
    }

    private boolean isCollectionProperty(String propertyName) {
        return propertyName.endsWith("]");
    }

    private VersionPropertyValue createPropertyValue(VersionProperty property, int collectionIndex,
            DefaultVersionContext context) {
        if (isCollection(collectionIndex)) {
            return new CollectionElementValue(property, collectionIndex, context);
        } else {
            return new VersionPropertyValue(property, context);
        }
    }

    private boolean isCollection(int collectionIndex) {
        return collectionIndex > -1;
    }
}