org.betaconceptframework.astroboa.model.jaxb.visitor.ContentObjectMarshalVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.model.jaxb.visitor.ContentObjectMarshalVisitor.java

Source

/*
 * Copyright (C) 2005-2012 BetaCONCEPT Limited
 *
 * This file is part of Astroboa.
 *
 * Astroboa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Astroboa is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.betaconceptframework.astroboa.model.jaxb.visitor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.namespace.QName;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DurationFormatUtils;
import org.betaconceptframework.astroboa.api.model.BinaryChannel;
import org.betaconceptframework.astroboa.api.model.CmsProperty;
import org.betaconceptframework.astroboa.api.model.CmsRepositoryEntity;
import org.betaconceptframework.astroboa.api.model.ComplexCmsProperty;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.SimpleCmsProperty;
import org.betaconceptframework.astroboa.api.model.Space;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.definition.CalendarPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.CmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.ComplexCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.ContentObjectTypeDefinition;
import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.commons.visitor.AbstractCmsPropertyDefinitionVisitor;
import org.betaconceptframework.astroboa.model.impl.ComplexCmsPropertyImpl;
import org.betaconceptframework.astroboa.model.impl.definition.ComplexCmsPropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.impl.definition.SimpleCmsPropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.jaxb.AstroboaMarshaller;
import org.betaconceptframework.astroboa.model.jaxb.CmsEntitySerialization;
import org.betaconceptframework.astroboa.model.jaxb.MarshalUtils;
import org.betaconceptframework.astroboa.model.jaxb.adapter.BinaryChannelAdapter;
import org.betaconceptframework.astroboa.model.jaxb.adapter.ContentObjectAdapter;
import org.betaconceptframework.astroboa.model.jaxb.adapter.SpaceAdapter;
import org.betaconceptframework.astroboa.model.jaxb.adapter.TopicAdapter;
import org.betaconceptframework.astroboa.model.jaxb.type.BinaryChannelType;
import org.betaconceptframework.astroboa.model.jaxb.type.CmsPropertyType;
import org.betaconceptframework.astroboa.model.jaxb.type.CmsPropertyTypeJAXBElement;
import org.betaconceptframework.astroboa.model.jaxb.type.ComplexCmsPropertyType;
import org.betaconceptframework.astroboa.model.jaxb.type.ContentObjectType;
import org.betaconceptframework.astroboa.model.jaxb.type.SimpleCmsPropertyType;
import org.betaconceptframework.astroboa.model.jaxb.type.SpaceType;
import org.betaconceptframework.astroboa.model.jaxb.type.TopicType;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.betaconceptframework.astroboa.util.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Responsible to visit all content object properties
 * and provide corresponding JAXB Elements
 * 
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
public class ContentObjectMarshalVisitor extends AbstractCmsPropertyDefinitionVisitor {

    final Logger logger = LoggerFactory.getLogger(getClass());

    protected ContentObjectMarshalContext contentObjectMarshalContext;

    private DatatypeFactory df;

    protected Map<QName, String> xsiSchemaLocationMap;

    protected Marshaller marshaller;

    //When a recursion is detected then 
    //it counts how many times recursion is happening
    private int recursionLevelCount;

    private List<String> cmsPropertyPathsToMarshall = null;

    protected Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities = new HashMap<String, CmsRepositoryEntity>();

    protected Map<String, ContentObjectTypeDefinition> cachedContentObjectTypeDefinitions = new HashMap<String, ContentObjectTypeDefinition>();

    private boolean marshallBinaryContent;

    private boolean marshallAllProperties;

    public ContentObjectMarshalVisitor() {
        setVisitType(VisitType.Self);
    }

    public void setCachedCmsRepositoryEntities(Map<String, CmsRepositoryEntity> cachedCmsRepositoryEntities) {

        if (cachedCmsRepositoryEntities != null) {
            this.cachedCmsRepositoryEntities.putAll(cachedCmsRepositoryEntities);
        }
    }

    public void initialize(ContentObject contentObject, ContentObjectType contentObjectType, Marshaller marshaller,
            boolean marshallBinaryContent, boolean marshallAllProperties) {

        this.marshaller = marshaller;

        contentObjectType = populateContentObjectType(contentObject, contentObjectType);

        initialize(contentObjectType, marshallBinaryContent, marshallAllProperties);

        contentObjectMarshalContext.getFirstComplexCmsPropertyInfo()
                .setCmsProperty(contentObject.getComplexCmsRootProperty());
    }

    public void initialize(ContentObjectType contentObjectType, boolean marshallBinaryContent,
            boolean marshallAllProperties) {

        logger.debug("Initializing marshalling visitor.");

        this.marshallBinaryContent = marshallBinaryContent;
        this.marshallAllProperties = marshallAllProperties;

        if (this.xsiSchemaLocationMap == null) {
            this.xsiSchemaLocationMap = new HashMap<QName, String>();
        }

        logger.debug("Current schema locations {}", this.xsiSchemaLocationMap);

        try {
            this.cmsPropertyPathsToMarshall = (List<String>) marshaller
                    .getProperty(AstroboaMarshaller.CMS_PROPERTIES_TO_BE_MARSHALLED);
        } catch (PropertyException e1) {
            throw new CmsException(e1);
        }

        if (df == null) {
            try {
                df = DatatypeFactory.newInstance();
            } catch (DatatypeConfigurationException e) {
                throw new CmsException(e);
            }
        }

        contentObjectMarshalContext = new ContentObjectMarshalContext(contentObjectType);

        contentObjectMarshalContext.addComplexCmsPropertyInfoToQueue(createRootComplexCmsPropertyInfo());

    }

    private ContentObjectType populateContentObjectType(ContentObject contentObject,
            ContentObjectType contentObjectType) {
        contentObjectType.setId(contentObject.getId());
        contentObjectType.setContentObjectTypeName(contentObject.getContentObjectType());
        contentObjectType.setSystemName(contentObject.getSystemName());

        //TODO: Check whether user may have more control on whether a friendly url is generated or not
        contentObjectType.setUrl(contentObject.getResourceApiURL(
                marshalOutputTypeIsJSON() ? ResourceRepresentationType.JSON : ResourceRepresentationType.XML, false,
                contentObject.getSystemName() != null));

        if (cmsPropertyPathsToMarshall == null
                || cmsPropertyPathsToMarshall.contains(CmsConstants.OWNER_ELEMENT_NAME)) {
            contentObjectType.setOwner(contentObject.getOwner());
        }

        return contentObjectType;
    }

    private boolean marshalOutputTypeIsJSON() {
        return marshaller instanceof AstroboaMarshaller && ((AstroboaMarshaller) marshaller).outputTypeIsJSON();
    }

    private CmsPropertyInfo createRootComplexCmsPropertyInfo() {
        CmsPropertyInfo rootComplexPropertyInfo = new CmsPropertyInfo();
        rootComplexPropertyInfo.setId(contentObjectMarshalContext.getContentObjectType().getId());
        rootComplexPropertyInfo
                .setFullPath(contentObjectMarshalContext.getContentObjectType().getContentObjectTypeName());
        rootComplexPropertyInfo
                .setName(contentObjectMarshalContext.getContentObjectType().getContentObjectTypeName());

        return rootComplexPropertyInfo;

    }

    @Override
    public void visit(ContentObjectTypeDefinition contentObjectTypeDefinition) {

        if (!CollectionUtils.isEmpty(cmsPropertyPathsToMarshall)) {
            //Marshal owner only if full content object is marshaled
            contentObjectMarshalContext.getContentObjectType().setOwner(null);
        }

        if (contentObjectTypeDefinition != null) {

            xsiSchemaLocationMap.put(contentObjectTypeDefinition.getQualifiedName(),
                    contentObjectTypeDefinition.url(ResourceRepresentationType.XSD));

            if (contentObjectTypeDefinition.hasCmsPropertyDefinitions()) {
                for (CmsPropertyDefinition propertyDefinition : contentObjectTypeDefinition.getPropertyDefinitions()
                        .values()) {
                    //if (complexCmsPropertyHasChildProperty(contentObjectMarshalContext.getFirstComplexCmsPropertyInfo(), propertyDefinition.getName())){
                    if (shouldMarshallDefinition(propertyDefinition)) {
                        propertyDefinition.accept(this);
                    }
                }
            }
        }

    }

    @Override
    public void visitComplexPropertyDefinition(ComplexCmsPropertyDefinition complexPropertyDefinition) {

        if (complexPropertyDefinition != null) {

            long start = System.currentTimeMillis();

            if (!shouldMarshallDefinition(complexPropertyDefinition)) {
                return;
            }

            //Check recursion
            if (recursionLoopDetected(complexPropertyDefinition)) {
                return;
            }

            if (aspectsAreVisited()) {

                //Provide schemaLocation information only for aspect properties
                xsiSchemaLocationMap.put(complexPropertyDefinition.getQualifiedName(),
                        complexPropertyDefinition.url(ResourceRepresentationType.XSD));
            }

            if (propertyHasBeenMarkedForRemoval(complexPropertyDefinition)) {
                marshallNullValueForComplexProperty(complexPropertyDefinition);
                return;
            }

            List<CmsPropertyInfo> complexCmsPropertyInfos = loadComplexCmsPropertyInfos(complexPropertyDefinition);

            if (CollectionUtils.isNotEmpty(complexCmsPropertyInfos)) {
                for (CmsPropertyInfo complexCmsPropertyInfo : complexCmsPropertyInfos) {

                    if (logger.isDebugEnabled()) {
                        logger.debug("\t Marshalling complex cms property {}",
                                complexCmsPropertyInfo.getFullPath());
                        logger.debug("\t Current context {}", contentObjectMarshalContext.getContextInfo());
                    }

                    CmsPropertyTypeJAXBElement<ComplexCmsPropertyType> complexCmsPropertyTypeJAXBElement = new CmsPropertyTypeJAXBElement(
                            //If aspects are marshaled then we need namespace and local part info.
                            //Otherwise only local part info is adequate for the element
                            aspectsAreVisited() ? complexPropertyDefinition.getQualifiedName()
                                    : new QName(complexPropertyDefinition.getQualifiedName().getLocalPart()),
                            ComplexCmsPropertyType.class, null, new ComplexCmsPropertyType());

                    if (marshalOutputTypeIsJSON() && complexPropertyDefinition.isMultiple()) {
                        complexCmsPropertyTypeJAXBElement.getValue().setExportAsAnArray(true);
                    }

                    if (((ComplexCmsPropertyDefinitionImpl) complexPropertyDefinition)
                            .commonAttributesAreDefined()) {
                        complexCmsPropertyTypeJAXBElement.getValue()
                                .setCmsIdentifier(complexCmsPropertyInfo.getId());
                    }

                    //When aspects are added to a content object xml
                    //we need to define attribute xsi:type in order to give a hint to a potential 
                    //validator where to find the type definition for this element.
                    //Since JAXB does not automatically generates this attribute if both declared type and
                    //instance are of the same type (ComplexCmsPropertyType)
                    if (aspectsAreVisited()) {

                        if (logger.isDebugEnabled()) {
                            logger.debug("Created JAXBElement for complex property {} with qualified name {}",
                                    complexCmsPropertyInfo.getFullPath(),
                                    complexCmsPropertyTypeJAXBElement.getName().getPrefix() + " "
                                            + complexCmsPropertyTypeJAXBElement.getName().toString());
                        }

                        complexCmsPropertyTypeJAXBElement.getValue()
                                .setXsiType(complexPropertyDefinition.getQualifiedName().getPrefix() + ":"
                                        + complexPropertyDefinition.getQualifiedName().getLocalPart());
                    }

                    //Add complexCmsPropertyType to its parent
                    addJaxbElementToCurrentParentComplexCmsPropertyType(complexCmsPropertyTypeJAXBElement);

                    if (complexPropertyDefinition.hasChildCmsPropertyDefinitions()) {

                        Collection<CmsPropertyDefinition> childPropertyDefinitions = complexPropertyDefinition
                                .getChildCmsPropertyDefinitions().values();

                        //Change current parent

                        contentObjectMarshalContext.pushComplexCmsPropertyInfo(complexCmsPropertyInfo);
                        contentObjectMarshalContext
                                .pushComplexCmsPropertyType(complexCmsPropertyTypeJAXBElement.getValue());

                        //When aspects are visited we are interested only on root elements and not 
                        //on their children as it is there where xsi information will be printed
                        //Therefore, disable this flag when visiting aspect children
                        contentObjectMarshalContext.pushAspectsAreVisited(false);

                        //Visit its children
                        if (logger.isDebugEnabled()) {
                            logger.debug("Start marshalling for children of {} for Thread {}",
                                    complexPropertyDefinition.getFullPath(), Thread.currentThread().getName());
                        }

                        for (CmsPropertyDefinition cmsPropertyDefinition : childPropertyDefinitions) {

                            //Visit child definition only if there is a corresponding value
                            //if (complexCmsPropertyHasChildProperty(complexCmsPropertyInfo, cmsPropertyDefinition.getName()) || 
                            //   (CollectionUtils.isNotEmpty(cmsPropertyPathsToMarshall) && shouldMarshallDefinition(cmsPropertyDefinition))){
                            if (shouldMarshallDefinition(cmsPropertyDefinition)) {
                                cmsPropertyDefinition.accept(this);
                            }
                        }

                        if (logger.isDebugEnabled()) {
                            logger.debug("Finished marshalling children for {} for Thread {}",
                                    complexPropertyDefinition.getFullPath(), Thread.currentThread().getName());
                        }

                        contentObjectMarshalContext.pollComplexCmsPropertyInfo();
                        contentObjectMarshalContext.pollComplexCmsPropertyType();
                        contentObjectMarshalContext.pollAspectsVisited();

                    }

                    //In case complex property is empty and no ID is defined there is no point in showing in xml
                    if (StringUtils.isBlank(complexCmsPropertyTypeJAXBElement.getValue().getCmsIdentifier())
                            && CollectionUtils
                                    .isEmpty(complexCmsPropertyTypeJAXBElement.getValue().getCmsProperties())) {
                        if (contentObjectMarshalContext.getFirstComplexCmsPropertyType() == null) {
                            contentObjectMarshalContext.getContentObjectType().getCmsProperties()
                                    .remove(complexCmsPropertyTypeJAXBElement);
                        } else {
                            contentObjectMarshalContext.getFirstComplexCmsPropertyType().getCmsProperties()
                                    .remove(complexCmsPropertyTypeJAXBElement);
                        }
                    }
                }

                if (logger.isDebugEnabled()) {
                    logger.debug("Visited complex cms definition {} in {}", complexPropertyDefinition.getPath(),
                            DurationFormatUtils.formatDurationHMS(System.currentTimeMillis() - start));
                }

            }

        }
    }

    private void marshallNullValueForComplexProperty(ComplexCmsPropertyDefinition complexPropertyDefinition) {

        //Although this is a complex property we use the simple property type in order to generate an emtpy tag
        //which is what we really want
        final SimpleCmsPropertyType simpleCmsPropertyType = new SimpleCmsPropertyType();

        CmsPropertyTypeJAXBElement<SimpleCmsPropertyType> simpleCmsPropertyTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                aspectsAreVisited() ? complexPropertyDefinition.getQualifiedName()
                        : new QName(complexPropertyDefinition.getQualifiedName().getLocalPart()),
                SimpleCmsPropertyType.class, null, simpleCmsPropertyType);

        addJaxbElementToCurrentParentComplexCmsPropertyType(simpleCmsPropertyTypeJaxbElement);

    }

    protected List<CmsPropertyInfo> loadComplexCmsPropertyInfos(CmsPropertyDefinition complexPropertyDefinition) {

        List<CmsPropertyInfo> complexCmsPropertyInfos = new ArrayList<CmsPropertyInfo>();

        //To avoid the case of loading recursive properties,
        //which are new 
        //we check the existence of a property directly on the provided map
        //and not through method getChildPropertyList or getChildProperty
        //which will cause a property to be lazy loaded (if not found in the parent property) 
        //and thus to be created if not found in the repository
        ComplexCmsProperty<?, ?> parentComplexCmsProperty = (ComplexCmsProperty<?, ?>) contentObjectMarshalContext
                .getFirstComplexCmsPropertyInfo().getCmsProperty();

        if (parentComplexCmsProperty == null) {
            throw new CmsException(
                    "Found no parent complex cms property for property " + complexPropertyDefinition.getFullPath());
        }

        List<CmsProperty<?, ?>> complexCmsProperties = parentComplexCmsProperty.getChildProperties()
                .get(complexPropertyDefinition.getName());

        if (CollectionUtils.isEmpty(complexCmsProperties)) {
            //No properties exist. 
            //Check if any value exists in the repository
            if (parentComplexCmsProperty.hasValueForChildProperty(complexPropertyDefinition.getName())) {
                //Try usual method using getChildProperty or getChildPropertyList
                //methods. 
                if (complexPropertyDefinition.isMultiple()) {
                    complexCmsProperties = parentComplexCmsProperty
                            .getChildPropertyList(complexPropertyDefinition.getName());
                } else {
                    CmsProperty<?, ?> complexCmsProperty = parentComplexCmsProperty
                            .getChildProperty(complexPropertyDefinition.getName());

                    if (complexCmsProperty != null) {
                        complexCmsProperties = new ArrayList<CmsProperty<?, ?>>();
                        complexCmsProperties.add(complexCmsProperty);
                    }
                }
            }
        }

        if (CollectionUtils.isNotEmpty(complexCmsProperties)) {
            for (CmsProperty<?, ?> complexCmsProperty : complexCmsProperties) {

                CmsPropertyInfo complexCmsPropertyInfo = new CmsPropertyInfo();
                complexCmsPropertyInfo.setId(complexCmsProperty.getId());
                complexCmsPropertyInfo.setFullPath(complexCmsProperty.getFullPath());
                complexCmsPropertyInfo.setCmsProperty(complexCmsProperty);
                complexCmsPropertyInfo.setName(complexCmsProperty.getName());

                complexCmsPropertyInfos.add(complexCmsPropertyInfo);
            }
        }

        return complexCmsPropertyInfos;
    }

    private Boolean aspectsAreVisited() {
        return contentObjectMarshalContext.aspectsAreVisited();
    }

    protected boolean shouldMarshallDefinition(CmsPropertyDefinition cmsPropertyDefinition) {

        if (marshallAllProperties) {
            return true;
        }

        //Marshal Property if its path or its parent path exist in list
        //Get full path in case this definition is about an aspect.
        String propertyPath = cmsPropertyDefinition.getFullPath();

        if (propertyPath
                .startsWith(contentObjectMarshalContext.getContentObjectType().getContentObjectTypeName())) {
            propertyPath = StringUtils.substringAfter(propertyPath, CmsConstants.PERIOD_DELIM);
        }

        return MarshalUtils.propertyShouldBeMarshalled(cmsPropertyPathsToMarshall, cmsPropertyDefinition.getName(),
                propertyPath);

    }

    private boolean recursionLoopDetected(ComplexCmsPropertyDefinition complexPropertyDefinition) {

        if (contentObjectMarshalContext.getFirstComplexCmsPropertyInfo() != null) {
            if (StringUtils.equals(contentObjectMarshalContext.getFirstComplexCmsPropertyInfo().getName(),
                    complexPropertyDefinition.getName())) {
                recursionLevelCount++;
            } else {
                recursionLevelCount = 0;
                return false;
            }
        }

        if (recursionLevelCount > 500) {
            logger.warn("Detected recusrion loop. Reached level of 500 for property "
                    + complexPropertyDefinition.getName());
            return true;
        }

        return false;
    }

    @Override
    public <T> void visitSimplePropertyDefinition(SimpleCmsPropertyDefinition<T> simplePropertyDefinition) {

        if (simplePropertyDefinition != null) {

            if (!shouldMarshallDefinition(simplePropertyDefinition)) {
                return;
            }

            if (propertyHasBeenMarkedForRemoval(simplePropertyDefinition)) {
                marshallNullValueForSimpleProperty(simplePropertyDefinition);
                return;
            }

            //First retrieve CmsProperty
            CmsPropertyInfo simpleCmsProperty = getCmsPropertyInfoForChildProperty(simplePropertyDefinition);

            if (simpleCmsProperty == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug(
                            "Simple cms property {} does not have a value in repository and therefore will not be marshalled",
                            simplePropertyDefinition.getFullPath());
                }
                return;
            }

            if (logger.isDebugEnabled()) {
                logger.debug("\t Marshalling simple cms property {}", simpleCmsProperty.getFullPath());
            }

            List<?> values = simpleCmsProperty.getValues();

            //For each value generate a JAXB element of type SimpleCmsPropertyType
            if (CollectionUtils.isNotEmpty(values)) {
                for (Object value : values) {
                    marshallValueForSimpleProperty(simplePropertyDefinition, simpleCmsProperty, value);
                }
            }
        }
    }

    protected void marshallNullValueForSimpleProperty(SimpleCmsPropertyDefinition simpleCmsPropertyDefinition) {

        final SimpleCmsPropertyType simpleCmsPropertyType = new SimpleCmsPropertyType();

        simpleCmsPropertyType.setExportAsAnAttribute(
                ((SimpleCmsPropertyDefinitionImpl) simpleCmsPropertyDefinition).isRepresentsAnXmlAttribute());

        CmsPropertyTypeJAXBElement<SimpleCmsPropertyType> simpleCmsPropertyTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                new QName(simpleCmsPropertyDefinition.getQualifiedName().getLocalPart()),
                SimpleCmsPropertyType.class, null, simpleCmsPropertyType);

        //Special case. If property represents an attribute
        //and export is in XML format
        //attribute's value should be the empty string
        if (!marshalOutputTypeIsJSON() && simpleCmsPropertyType.exportAsAnAttribute()) {
            simpleCmsPropertyType.setContent("");
        }

        addJaxbElementToCurrentParentComplexCmsPropertyType(simpleCmsPropertyTypeJaxbElement);
    }

    protected <T> void marshallValueForSimpleProperty(SimpleCmsPropertyDefinition<T> simplePropertyDefinition,
            CmsPropertyInfo simpleCmsProperty, Object value) {

        if (value != null) {

            switch (simplePropertyDefinition.getValueType()) {
            case String:
            case Boolean:
            case Double:
            case Long:
            case Date:

                final SimpleCmsPropertyType simpleCmsPropertyType = new SimpleCmsPropertyType();

                simpleCmsPropertyType.setExportAsAnAttribute(
                        ((SimpleCmsPropertyDefinitionImpl) simplePropertyDefinition).isRepresentsAnXmlAttribute());

                if (marshalOutputTypeIsJSON() && simplePropertyDefinition.isMultiple()) {
                    simpleCmsPropertyType.setExportAsAnArray(true);
                }

                CmsPropertyTypeJAXBElement<SimpleCmsPropertyType> simpleCmsPropertyTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                        new QName(simplePropertyDefinition.getQualifiedName().getLocalPart()),
                        SimpleCmsPropertyType.class, null, simpleCmsPropertyType);

                if (value instanceof String) {
                    simpleCmsPropertyTypeJaxbElement.getValue().setContent((String) value);
                } else if (value instanceof Boolean) {
                    simpleCmsPropertyTypeJaxbElement.getValue().setContent(((Boolean) value).toString());
                } else if (value instanceof Double) {
                    simpleCmsPropertyTypeJaxbElement.getValue().setContent(((Double) value).toString());
                } else if (value instanceof Long) {
                    simpleCmsPropertyTypeJaxbElement.getValue().setContent(((Long) value).toString());
                } else if (value instanceof Calendar) {
                    Calendar calendar = (Calendar) value;

                    try {
                        if (((CalendarPropertyDefinition) simplePropertyDefinition).isDateTime()) {
                            GregorianCalendar gregCalendar = new GregorianCalendar(calendar.getTimeZone());
                            gregCalendar.setTimeInMillis(calendar.getTimeInMillis());

                            simpleCmsPropertyTypeJaxbElement.getValue()
                                    .setContent(df.newXMLGregorianCalendar(gregCalendar).toXMLFormat());
                        } else {
                            simpleCmsPropertyTypeJaxbElement.getValue()
                                    .setContent(df.newXMLGregorianCalendarDate(calendar.get(Calendar.YEAR),
                                            calendar.get(Calendar.MONTH) + 1, // Calendar.MONTH is zero based, XSD Date datatype's month field starts
                                            //   with JANUARY as 1.
                                            calendar.get(Calendar.DAY_OF_MONTH), DatatypeConstants.FIELD_UNDEFINED)
                                            .toXMLFormat());
                        }
                    } catch (Exception e) {
                        throw new CmsException("Property " + simpleCmsProperty.getFullPath() + " Calendar value "
                                + DateUtils.format(calendar), e);
                    }
                } else {
                    throw new CmsException("Property " + simpleCmsProperty.getFullPath() + " has value type "
                            + simplePropertyDefinition.getValueType() + " but contains value of type "
                            + value.getClass().getName());
                }

                addJaxbElementToCurrentParentComplexCmsPropertyType(simpleCmsPropertyTypeJaxbElement);

                break;
            case TopicReference:

                try {
                    TopicType topicType = marshalTopicReference(value);

                    if (marshalOutputTypeIsJSON() && simplePropertyDefinition.isMultiple()) {
                        topicType.setExportAsAnArray(true);
                    }

                    CmsPropertyTypeJAXBElement<TopicType> topicTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                            new QName(simplePropertyDefinition.getQualifiedName().getLocalPart()), TopicType.class,
                            null, topicType);

                    addJaxbElementToCurrentParentComplexCmsPropertyType(topicTypeJaxbElement);

                } catch (Exception e) {
                    throw new CmsException("Unable to marshal topic " + ((Topic) value).getName(), e);
                }

                break;
            case Binary:
                try {
                    BinaryChannelType binaryChannelType = getBinaryChannelAdapter().marshal((BinaryChannel) value);

                    if (marshalOutputTypeIsJSON() && simplePropertyDefinition.isMultiple()) {
                        binaryChannelType.setExportAsAnArray(true);
                    }

                    CmsPropertyTypeJAXBElement<BinaryChannelType> binaryChannelTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                            new QName(simplePropertyDefinition.getQualifiedName().getLocalPart()),
                            BinaryChannelType.class, null, binaryChannelType);

                    addJaxbElementToCurrentParentComplexCmsPropertyType(binaryChannelTypeJaxbElement);

                } catch (Exception e) {
                    throw new CmsException("Unable to marshal binary channel " + ((BinaryChannel) value).getName(),
                            e);
                }

                break;
            case ObjectReference:
                try {

                    logger.debug("\t Property is a reference to another object");

                    Marshaller objectReferenceMarshaller = CmsEntitySerialization.Context
                            .createMarshaller(marshalOutputTypeIsJSON() ? ResourceRepresentationType.JSON
                                    : ResourceRepresentationType.XML, prettyPrintIsEnabled());

                    //For now only porifle.title is provided. 
                    objectReferenceMarshaller.setProperty(AstroboaMarshaller.CMS_PROPERTIES_TO_BE_MARSHALLED,
                            Arrays.asList("profile.title"));

                    ContentObjectAdapter adapter = new ContentObjectAdapter();
                    adapter.setMarshaller(objectReferenceMarshaller, marshallBinaryContent, false);
                    objectReferenceMarshaller.setAdapter(adapter);

                    ContentObjectType contentObjectType = objectReferenceMarshaller
                            .getAdapter(ContentObjectAdapter.class).marshal((ContentObject) value);

                    if (marshalOutputTypeIsJSON() && simplePropertyDefinition.isMultiple()) {
                        contentObjectType.setExportAsAnArray(true);
                    }

                    CmsPropertyTypeJAXBElement<ContentObjectType> contentObjectReferenceTypeJaxbElement = new CmsPropertyTypeJAXBElement(
                            new QName(simplePropertyDefinition.getQualifiedName().getLocalPart()),
                            ContentObjectType.class, null, contentObjectType);

                    addJaxbElementToCurrentParentComplexCmsPropertyType(contentObjectReferenceTypeJaxbElement);

                } catch (Exception e) {
                    throw new CmsException("Unable to marshal contentObject " + ((ContentObject) value).getId(), e);
                }

                break;
            default:
                break;
            }

        }
    }

    private boolean prettyPrintIsEnabled() throws PropertyException {
        return BooleanUtils.isTrue((Boolean) marshaller.getProperty(Marshaller.JAXB_FORMATTED_OUTPUT));
    }

    private ResourceRepresentationType<?> retrieveOutput() {
        ResourceRepresentationType<?> resourceRepresentationType = ResourceRepresentationType.XML;

        if (marshaller != null && marshaller instanceof AstroboaMarshaller
                && ((AstroboaMarshaller) marshaller).outputTypeIsJSON()) {
            resourceRepresentationType = ResourceRepresentationType.JSON;
        }
        return resourceRepresentationType;
    }

    protected SpaceType marshalSpaceReference(Object value) throws Exception {
        return getSpaceAdapter().marshal((Space) value, retrieveOutput());
    }

    protected TopicType marshalTopicReference(Object value) throws Exception {
        return getTopicAdapter().marshal((Topic) value, retrieveOutput());
    }

    protected boolean propertyHasBeenMarkedForRemoval(CmsPropertyDefinition cmsPropertyDefinition) {

        return ((ComplexCmsPropertyImpl) contentObjectMarshalContext.getFirstComplexCmsPropertyInfo()
                .getCmsProperty()).cmsPropertyHasBeenLoadedAndRemoved(cmsPropertyDefinition.getName());

    }

    protected <T> CmsPropertyInfo getCmsPropertyInfoForChildProperty(
            SimpleCmsPropertyDefinition<T> simplePropertyDefinition) {

        if (((ComplexCmsProperty<?, ?>) contentObjectMarshalContext.getFirstComplexCmsPropertyInfo()
                .getCmsProperty()).hasValueForChildProperty(simplePropertyDefinition.getName())) {

            CmsProperty<?, ?> simpleCmsProperty = ((ComplexCmsProperty<?, ?>) contentObjectMarshalContext
                    .getFirstComplexCmsPropertyInfo().getCmsProperty())
                            .getChildProperty(simplePropertyDefinition.getName());

            CmsPropertyInfo cmsPropertyInfo = new CmsPropertyInfo();
            cmsPropertyInfo.setName(simpleCmsProperty.getName());
            cmsPropertyInfo.setFullPath(simpleCmsProperty.getFullPath());
            cmsPropertyInfo.setCmsProperty(simpleCmsProperty);

            //Then retrieve all its values
            if (simplePropertyDefinition.isMultiple()) {
                cmsPropertyInfo.getValues()
                        .addAll(((SimpleCmsProperty<?, ?, ?>) simpleCmsProperty).getSimpleTypeValues());
            } else {
                cmsPropertyInfo.addValue(((SimpleCmsProperty<?, ?, ?>) simpleCmsProperty).getSimpleTypeValue());
            }

            return cmsPropertyInfo;
        }

        return null;
    }

    private BinaryChannelAdapter getBinaryChannelAdapter() {
        if (marshaller.getAdapter(BinaryChannelAdapter.class) == null) {
            marshaller.setAdapter(new BinaryChannelAdapter(marshallBinaryContent));
        }

        return marshaller.getAdapter(BinaryChannelAdapter.class);
    }

    private TopicAdapter getTopicAdapter() {
        if (marshaller.getAdapter(TopicAdapter.class) == null) {
            marshaller.setAdapter(new TopicAdapter());
        }

        return marshaller.getAdapter(TopicAdapter.class);
    }

    private SpaceAdapter getSpaceAdapter() {
        if (marshaller.getAdapter(SpaceAdapter.class) == null) {
            marshaller.setAdapter(new SpaceAdapter());
        }

        return marshaller.getAdapter(SpaceAdapter.class);
    }

    private void addJaxbElementToCurrentParentComplexCmsPropertyType(CmsPropertyType cmsPropertyType) {

        if (contentObjectMarshalContext.getFirstComplexCmsPropertyType() == null) {
            contentObjectMarshalContext.getContentObjectType().addCmsProperty(cmsPropertyType);
        } else {
            contentObjectMarshalContext.getFirstComplexCmsPropertyType().addCmsProperty(cmsPropertyType);
        }
    }

    /**
     * If definition visited is or belongs to an aspect definition
     * then full path is required in contrary to other case where relative path to
     * content object type is required
     */
    public void enableAspectDefinitionVisit() {
        contentObjectMarshalContext.pushAspectsAreVisited(true);
    }

    public ContentObjectType getContentObjectType() {
        return contentObjectMarshalContext.getContentObjectType();
    }

    public Map<QName, String> getXsiSchemaLocationMap() {
        return xsiSchemaLocationMap;
    }

}