All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sdl.odata.renderer.AbstractPropertyWriter Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2014-2024 All Rights Reserved by the RWS Group for and on behalf of its affiliates and subsidiaries.
 *
 * 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 com.sdl.odata.renderer;

import com.sdl.odata.api.ODataClientException;
import com.sdl.odata.api.ODataException;
import com.sdl.odata.api.edm.ODataEdmException;
import com.sdl.odata.api.edm.model.EntityDataModel;
import com.sdl.odata.api.edm.model.StructuredType;
import com.sdl.odata.api.edm.model.Type;
import com.sdl.odata.api.parser.ODataUri;
import com.sdl.odata.api.parser.ODataUriUtil;
import com.sdl.odata.api.parser.TargetType;
import com.sdl.odata.api.renderer.ChunkedActionRenderResult;
import com.sdl.odata.api.renderer.ODataRenderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.Option;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import static com.sdl.odata.ODataRendererUtils.checkNotNull;
import static com.sdl.odata.renderer.PropertyStreamWriter.ChunkedStreamAction.BODY_DOCUMENT;
import static com.sdl.odata.renderer.PropertyStreamWriter.ChunkedStreamAction.END_DOCUMENT;
import static com.sdl.odata.renderer.PropertyStreamWriter.ChunkedStreamAction.START_DOCUMENT;
import static com.sdl.odata.util.edm.EntityDataModelUtil.getAndCheckType;

/**
 * Handles property writing.
 */
public abstract class AbstractPropertyWriter implements PropertyStreamWriter {
    private static final Logger LOG = LoggerFactory.getLogger(AbstractPropertyWriter.class);
    private final ODataUri oDataUri;
    private final EntityDataModel entityDataModel;
    private final TargetType targetType;

    public AbstractPropertyWriter(ODataUri oDataUri, EntityDataModel entityDataModel) throws ODataRenderException {
        this.oDataUri = checkNotNull(oDataUri);
        this.entityDataModel = checkNotNull(entityDataModel);
        this.targetType = getTargetType();
    }

    /**
     * This is main method to get property as string.
     *
     * @param data represents simple primitive or complex value or collections of collection of these.
     * @return String that represents simple primitive or complex value
     * or collections of collection of these in the form of xml or json.
     * @throws ODataException if an error occurs.
     */
    public String getPropertyAsString(Object data) throws ODataException {
        LOG.trace("GetPropertyAsString invoked with {}", data);
        if (data != null) {
            return makePropertyString(data);
        } else {
            return generateNullPropertyString();
        }
    }

    @Override
    public ChunkedActionRenderResult getPropertyStartDocument(Object data, OutputStream outputStream)
            throws ODataException {
        LOG.debug("GetPropertyStartDocument invoked with {}", data);
        if (data == null) {
            // If null - return info in one piece within getPropertyBodyDocument() call and here just empty string
            return new ChunkedActionRenderResult(outputStream);
        } else {
            return makePropertyStringChunked(data, START_DOCUMENT, new ChunkedActionRenderResult(outputStream));
        }
    }

    @Override
    public ChunkedActionRenderResult getPropertyBodyDocument(Object data, ChunkedActionRenderResult previousResult)
            throws ODataException {
        LOG.debug("GetPropertyBodyDocument invoked with {}", data);
        if (data == null) {
            try {
                previousResult.getOutputStream().write(generateNullPropertyString().getBytes());
            } catch (IOException e) {
                throw new ODataRenderException("Unable to render property body.", e);
            }
            return previousResult;
        } else {
            return makePropertyStringChunked(data, BODY_DOCUMENT, previousResult);
        }
    }

    @Override
    public void getPropertyEndDocument(Object data, ChunkedActionRenderResult previousResult) throws ODataException {
        LOG.debug("GetPropertyEndDocument invoked with {}", data);
        if (data != null) {
            makePropertyStringChunked(data, END_DOCUMENT, previousResult);
        }
    }

    protected abstract ChunkedActionRenderResult getPrimitivePropertyChunked(
            Object data, Type type, ChunkedStreamAction action, ChunkedActionRenderResult previousResult)
            throws ODataException;

    protected abstract ChunkedActionRenderResult getComplexPropertyChunked(
            Object data, StructuredType type, ChunkedStreamAction action, ChunkedActionRenderResult previousResult)
            throws ODataException;

    /**
     * This abstract method this needs to be implemented in subclass. Purpose of this method is to
     * generate string (either json or xml ) if the given property is null. For example, atom null string
     * 

* {@code *

     *  
     * 
* } * * @return String that represents null property * @throws com.sdl.odata.api.renderer.ODataRenderException in case of any problems */ protected abstract String generateNullPropertyString() throws ODataException; /** * This method handles simple primitive property and generates string based on property. For example following * atom xml generates in case of simple primitive in AtomPropertyWriter. *

* {@code *

     *     CEO
     * 
* } * * @param data that represents primitive data. This will never be null. * @param type type of the property. This will never be null. * @return String that represents simple property * @throws com.sdl.odata.api.renderer.ODataRenderException in case of any problems */ protected abstract String generatePrimitiveProperty(Object data, Type type) throws ODataException; /** * This method handles complex properties and generates string based on property. For example following * atom xml generates in case of complex property in AtomPropertyWriter. *

* {@code *

     * 
     * Obere Str. 57
     * Berlin
     * 
     * D-12209
     * 
     * 
* } * * @param data that represents complex property data. This will never be null. * @param type is StructuredType. This will never be null. * @return String that represents simple property * @throws com.sdl.odata.api.renderer.ODataRenderException in case of any problems */ protected abstract String generateComplexProperty(Object data, StructuredType type) throws ODataException; private String makePropertyString(Object data) throws ODataException { String propertyXML = null; Type type = getTypeFromODataUri(); validateRequest(type, data); switch (type.getMetaType()) { case PRIMITIVE: LOG.trace("Given property type is primitive"); propertyXML = generatePrimitiveProperty(data, type); break; case COMPLEX: LOG.trace("Given property type is complex"); propertyXML = generateComplexProperty(data, (StructuredType) type); break; default: defaultHandling(type); } return propertyXML; } private ChunkedActionRenderResult makePropertyStringChunked(Object data, ChunkedStreamAction action, ChunkedActionRenderResult previousResult) throws ODataException { if (previousResult.getType() == null) { previousResult.setType(getTypeFromODataUri()); } Type type = previousResult.getType(); if (!previousResult.isTypeValidated()) { validateRequestChunk(type, data); } switch (type.getMetaType()) { case PRIMITIVE: LOG.debug("Given property type is primitive"); return getPrimitivePropertyChunked(data, type, action, previousResult); case COMPLEX: LOG.debug("Given property type is complex"); return getComplexPropertyChunked(data, (StructuredType) type, action, previousResult); default: defaultHandling(type); } return null; } private void validateRequest(Type type, Object data) throws ODataRenderException, ODataClientException, ODataEdmException { if (!areValidTypesToProceed(type, data)) { throw new ODataRenderException("ODataUri type is not matched with given 'data' type: " + type); } } private boolean areValidTypesToProceed(Type type, Object data) throws ODataRenderException, ODataEdmException { return isEmptyCollection(data) || !(isCollection(data) ^ targetType.isCollection()) && getType(data).equals(type); } public void validateRequestChunk(Type type, Object data) throws ODataRenderException, ODataClientException, ODataEdmException { if (!areValidTypesToProceedChunk(type, data)) { throw new ODataRenderException("ODataUri type is not matched with given 'data' type: " + type); } } private boolean areValidTypesToProceedChunk(Type type, Object data) throws ODataRenderException, ODataEdmException { return isEmptyCollection(data) || getType(data).equals(type); } public Type getTypeFromODataUri() throws ODataRenderException { return entityDataModel.getType(targetType.typeName()); } private TargetType getTargetType() throws ODataRenderException { Option targetTypeOption = ODataUriUtil.resolveTargetType(oDataUri, entityDataModel); if (targetTypeOption.isEmpty()) { throw new ODataRenderException("Target type should not be empty"); } return targetTypeOption.get(); } protected Type getType(Object data) throws ODataEdmException { Type type; if (isCollection(data)) { LOG.trace("Given property is collection"); type = getAndCheckType(entityDataModel, ((List) data).get(0).getClass()); } else { type = getAndCheckType(entityDataModel, data.getClass()); } return type; } protected boolean isEmptyCollection(Object data) { return isCollection(data) && ((List) data).isEmpty(); } protected boolean isCollection(Object data) { return data instanceof List; } protected ODataUri getODataUri() { return oDataUri; } protected EntityDataModel getEntityDataModel() { return entityDataModel; } protected void defaultHandling(Type type) throws ODataRenderException { String msg = String.format("Unhandled object type %s", type); LOG.warn(msg); throw new ODataRenderException(msg); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy