de.cismet.cids.jsonpatch.operation.cidsbean.AddOperation.java Source code

Java tutorial

Introduction

Here is the source code for de.cismet.cids.jsonpatch.operation.cidsbean.AddOperation.java

Source

/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
*              ... and it just works.
*
****************************************************/
/*
 * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
 *
 * This software is dual-licensed under:
 *
 * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
 *   later version;
 * - the Apache Software License (ASL) version 2.0.
 *
 * The text of this file and of both licenses is available at the root of this
 * project or, if you have the jar distribution, in directory META-INF/, under
 * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
 *
 * Direct link to the sources:
 *
 * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
 * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
 */
package de.cismet.cids.jsonpatch.operation.cidsbean;

import Sirius.server.localserver.attribute.ObjectAttribute;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ValueNode;

import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.jackson.jsonpointer.TokenResolver;
import com.github.fge.jsonpatch.JsonPatchException;

import com.google.common.collect.Iterables;

import com.vividsolutions.jts.geom.Geometry;

import org.apache.log4j.Logger;

import java.math.BigDecimal;

import java.sql.Timestamp;

import java.util.List;
import java.util.ListIterator;
import java.util.ResourceBundle;

import de.cismet.cids.dynamics.CidsBean;

import de.cismet.cids.jsonpatch.CidsBeanPatchUtils;
import de.cismet.cids.jsonpatch.operation.CidsBeanPatchOperation;

import de.cismet.commons.classloading.BlacklistClassloading;

/**
 * DOCUMENT ME!
 *
 * @version  $Revision$, $Date$
 */
public class AddOperation extends com.github.fge.jsonpatch.operation.AddOperation
        implements CidsBeanPatchOperation {

    //~ Static fields/initializers ---------------------------------------------

    protected static final Logger LOGGER = Logger.getLogger(AddOperation.class);
    protected static final CidsBeanPatchUtils UTILS = CidsBeanPatchUtils.getInstance();
    protected static final ResourceBundle RESOURCE_BUNDLE = UTILS.getResourceBundle();

    //~ Instance fields --------------------------------------------------------

    @JsonIgnore
    protected final com.github.fge.jsonpatch.operation.ReplaceOperation jsonPatchReplaceOperation;

    @JsonIgnore
    protected final boolean overwrite;

    //~ Constructors -----------------------------------------------------------

    /**
     * Creates a new AddOperation valueObject.
     *
     * @param  path   DOCUMENT ME!
     * @param  value  DOCUMENT ME!
     */
    @JsonCreator
    public AddOperation(@JsonProperty("path") final JsonPointer path, @JsonProperty("value") final JsonNode value) {
        this(path, value, false);
    }

    /**
     * Creates a new AddOperation valueObject.
     *
     * @param  path       DOCUMENT ME!
     * @param  value      DOCUMENT ME!
     * @param  overwrite  DOCUMENT ME!
     */
    protected AddOperation(final JsonPointer path, final JsonNode value, final boolean overwrite) {
        super(path, value);
        this.overwrite = overwrite;
        if (this.overwrite) {
            jsonPatchReplaceOperation = new com.github.fge.jsonpatch.operation.ReplaceOperation(path, value);
        } else {
            jsonPatchReplaceOperation = null;
        }
    }

    //~ Methods ----------------------------------------------------------------

    @Override
    public JsonNode apply(final JsonNode node) throws JsonPatchException {
        if (this.overwrite) {
            return this.jsonPatchReplaceOperation.apply(node);
        } else {
            return super.apply(node);
        }
    }

    @Override
    public CidsBean apply(final CidsBean cidsBean) throws JsonPatchException {
        if ((this.value == null) || this.value.isMissingNode()) {
            throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.nullValue"));
        }

        final String cidsBeanPointer = UTILS.jsonPointerToCidsBeanPointer(this.path);
        if ((cidsBeanPointer == null) || cidsBeanPointer.isEmpty()) {
            throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.rootNodeNotPermitted"));
        }

        final String cidsBeanParentPointer = UTILS.jsonPointerToCidsBeanPointer(this.path.parent());
        final Object parentObject;

        // parent is the root valueObject
        if ((cidsBeanParentPointer != null) && !cidsBeanParentPointer.isEmpty()) {
            parentObject = cidsBean.getProperty(cidsBeanParentPointer);
        } else {
            parentObject = cidsBean;
        }

        if (parentObject == null) {
            throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.noSuchParent"));
        }

        if (List.class.isAssignableFrom(parentObject.getClass())) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("performing add to array " + this.path.toString());
            }
            this.addToArray((List) parentObject);
            return cidsBean;
        } else if (CidsBean.class.isAssignableFrom(parentObject.getClass())) {
            this.addToObject((CidsBean) parentObject);
            return cidsBean;
        } else {
            throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.parentNotContainer"));
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @param   parentList  DOCUMENT ME!
     *
     * @throws  JsonPatchException  DOCUMENT ME!
     */
    protected void addToArray(final List parentList) throws JsonPatchException {
        final TokenResolver<JsonNode> token = Iterables.getLast(path);
        if (token.getToken().equals(LAST_ARRAY_ELEMENT)) {
            if (UTILS.isCidsBeanArray(value)) {
                final List<CidsBean> beanList = (List<CidsBean>) UTILS.deserializeAndVerifyCidsBean(this.value);
                if (this.overwrite) {
                    final ListIterator<CidsBean> listIterator = parentList.listIterator();
                    while (listIterator.hasNext()) {
                        final CidsBean listCidsBean = listIterator.next();
                        for (final CidsBean replacmentBean : beanList) {
                            if (replacmentBean.getCidsBeanInfo().getJsonObjectKey()
                                    .equals(listCidsBean.getCidsBeanInfo().getJsonObjectKey())) {
                                listIterator.set(replacmentBean);
                            }
                        }
                    }
                } else {
                    parentList.addAll(beanList);
                }
            } else if (UTILS.isCidsBean(value)) {
                final CidsBean cidsBean = (CidsBean) UTILS.deserializeAndVerifyCidsBean(this.value);
                if (this.overwrite) {
                    final ListIterator<CidsBean> listIterator = parentList.listIterator();
                    while (listIterator.hasNext()) {
                        final CidsBean listCidsBean = listIterator.next();
                        if (cidsBean.getCidsBeanInfo().getJsonObjectKey()
                                .equals(listCidsBean.getCidsBeanInfo().getJsonObjectKey())) {
                            listIterator.set(cidsBean);
                        }
                    }
                } else {
                    parentList.add(cidsBean);
                }
            } else {
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.invalidValueForArray"));
            }
        } else {
            final int size = parentList.size();
            final int index;
            try {
                index = Integer.parseInt(token.toString());
            } catch (NumberFormatException ex) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.notAnIndex") + ": " + ex.getMessage(), ex);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.notAnIndex"), ex);
            }

            if ((index < 0) || (index > size)) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.notAnIndex") + ": " + index + " (array size: "
                        + size + ")");
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.noSuchIndex"));
            }

            if (UTILS.isCidsBean(value)) {
                final CidsBean cidsBean = (CidsBean) UTILS.deserializeAndVerifyCidsBean(this.value);
                if (this.overwrite) {
                    parentList.set(index, cidsBean);
                } else {
                    parentList.add(index, cidsBean);
                }
            } else if (UTILS.isCidsBeanArray(value)) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.invalidValueForArrayIndex")
                        + ": arrays cannot be used as value in conjunction with an array index");
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.invalidValueForArrayIndex"));
            } else {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.invalidValueForArrayIndex") + ": " + value);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.invalidValueForArrayIndex"));
            }
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @param   parentBean  DOCUMENT ME!
     *
     * @throws  JsonPatchException  DOCUMENT ME!
     */
    protected void addToObject(final CidsBean parentBean) throws JsonPatchException {
        final String property = Iterables.getLast(path).getToken().getRaw();

        final ObjectAttribute objectAttribute = parentBean.getMetaObject().getAttributeByFieldName(property);
        if (objectAttribute == null) {
            LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.noSuchProperty") + ": " + this.path.toString());
            throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.noSuchProperty"));
        }

        final Object valueObject = UTILS.deserializeAndVerifyCidsBean(this.value);
        if (CidsBean.class.isAssignableFrom(valueObject.getClass())) {
            if (!objectAttribute.getMai().isForeignKey()) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.propertyValueMissmatch") + ": "
                        + "cids bean provided but not expected at " + this.path.toString());
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.propertyValueMissmatch"));
            }

            if (!this.overwrite && (parentBean.getProperty(property) != null)) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty") + ": " + property);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty"));
            }

            try {
                parentBean.setProperty(property, (CidsBean) valueObject);
            } catch (Exception ex) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.setPropertyFailed") + ": " + property, ex);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.setPropertyFailed"), ex);
            }
        } else if (List.class.isAssignableFrom(valueObject.getClass())) {
            final List<CidsBean> beanCollectionProperty = parentBean.getBeanCollectionProperty(property);
            if (!objectAttribute.getMai().isArray() || (beanCollectionProperty == null)) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.propertyValueMissmatch") + ": "
                        + "cids bean array provided but not expected at " + this.path.toString());
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.propertyValueMissmatch"));
            }

            if (!this.overwrite && !beanCollectionProperty.isEmpty()) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty") + ": array " + property
                        + "size: " + beanCollectionProperty.size());
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty"));
            } else if (this.overwrite) {
                beanCollectionProperty.clear();
                beanCollectionProperty.addAll((List<CidsBean>) valueObject);
            } else {
                beanCollectionProperty.addAll((List<CidsBean>) valueObject);
            }
        } else if (ValueNode.class.isAssignableFrom(valueObject.getClass())) {
            if (!this.overwrite && (parentBean.getProperty(property) != null)) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty") + ": " + property);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.propertyNotEmpty"));
            }

            final Class attrClass = BlacklistClassloading.forName(objectAttribute.getMai().getJavaclassname());
            if (attrClass == null) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.noSuchProperty") + ": " + this.path.toString());
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.noSuchProperty"));
            }

            final ValueNode valueNode = (ValueNode) valueObject;

            try {
                if (valueNode.isNumber()) {
                    if (attrClass.equals(Integer.class)) {
                        final int i = valueNode.asInt();
                        parentBean.setProperty(property, i);
                    } else if (attrClass.equals(Long.class)) {
                        final long l = valueNode.asLong();
                        parentBean.setProperty(property, l);
                    } else if (attrClass.equals(Float.class)) {
                        final float f = (float) valueNode.asDouble();
                        parentBean.setProperty(property, f);
                    } else if (attrClass.equals(Double.class)) {
                        final double d = valueNode.asDouble();
                        parentBean.setProperty(property, d);
                    } else if (attrClass.equals(java.sql.Timestamp.class)) {
                        final Timestamp ts = new Timestamp(valueNode.asLong());
                        parentBean.setProperty(property, ts);
                    } else if (attrClass.equals(BigDecimal.class)) {
                        final BigDecimal bd = new BigDecimal(valueNode.asText());
                        parentBean.setProperty(property, bd);
                    } else {
                        throw new Exception("no numeric handler available for class " + attrClass.getSimpleName());
                    }
                } else if (valueNode.isBoolean()) {
                    final boolean bl = valueNode.asBoolean();
                    parentBean.setProperty(property, bl);
                } else if (valueNode.isMissingNode() || NullNode.class.isAssignableFrom(valueNode.getClass())) {
                    parentBean.setProperty(property, null);
                } else if (valueNode.isTextual()) {
                    final String str = valueNode.textValue();
                    if (attrClass.equals(Geometry.class)) {
                        final Geometry geom = UTILS.fromEwkt(str);
                        parentBean.setProperty(property, geom);
                    } else if (attrClass.equals(String.class)) {
                        parentBean.setProperty(property, str);
                    } else {
                        LOGGER.warn("expected geometry or string property but got " + attrClass.getSimpleName());
                        parentBean.setProperty(property, str);
                    }
                } else {
                    throw new Exception("no handler available for value " + valueNode.toString());
                }
            } catch (Exception ex) {
                LOGGER.error(RESOURCE_BUNDLE.getString("jsonPatch.setPropertyFailed") + ": " + property, ex);
                throw new JsonPatchException(RESOURCE_BUNDLE.getString("jsonPatch.setPropertyFailed"), ex);
            }
        }
    }
}