org.betaconceptframework.astroboa.engine.jcr.io.Serializer.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.engine.jcr.io.Serializer.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 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.betaconceptframework.astroboa.engine.jcr.io;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BetaConceptNamespaceConstants;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.Space;
import org.betaconceptframework.astroboa.api.model.Taxonomy;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.ValueType;
import org.betaconceptframework.astroboa.api.model.definition.BinaryPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.CalendarPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.definition.CmsDefinition;
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.LocalizableCmsDefinition;
import org.betaconceptframework.astroboa.api.model.definition.SimpleCmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.io.FetchLevel;
import org.betaconceptframework.astroboa.api.model.io.SerializationConfiguration;
import org.betaconceptframework.astroboa.api.model.io.SerializationReport;
import org.betaconceptframework.astroboa.commons.comparator.PropertyRepresentingXmlAttributeComparator;
import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder;
import org.betaconceptframework.astroboa.engine.jcr.io.SerializationBean.CmsEntityType;
import org.betaconceptframework.astroboa.engine.jcr.io.contenthandler.ExportContentHandler;
import org.betaconceptframework.astroboa.engine.jcr.io.contenthandler.JsonExportContentHandler;
import org.betaconceptframework.astroboa.engine.jcr.io.contenthandler.XmlExportContentHandler;
import org.betaconceptframework.astroboa.engine.jcr.util.CmsRepositoryEntityUtils;
import org.betaconceptframework.astroboa.engine.jcr.util.JackrabbitDependentUtils;
import org.betaconceptframework.astroboa.engine.jcr.util.JcrNodeUtils;
import org.betaconceptframework.astroboa.model.impl.BinaryChannelImpl;
import org.betaconceptframework.astroboa.model.impl.definition.ComplexCmsPropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.impl.definition.SimpleCmsPropertyDefinitionImpl;
import org.betaconceptframework.astroboa.model.impl.io.SerializationReportImpl;
import org.betaconceptframework.astroboa.model.impl.item.CmsBuiltInItem;
import org.betaconceptframework.astroboa.model.impl.item.ItemUtils;
import org.betaconceptframework.astroboa.model.impl.item.JcrBuiltInItem;
import org.betaconceptframework.astroboa.model.impl.item.JcrNamespaceConstants;
import org.betaconceptframework.astroboa.model.jaxb.MarshalUtils;
import org.betaconceptframework.astroboa.service.dao.DefinitionServiceDao;
import org.betaconceptframework.astroboa.util.CmsConstants;
import org.betaconceptframework.astroboa.util.PropertyPath;
import org.betaconceptframework.astroboa.util.ResourceApiURLUtils;
import org.betaconceptframework.astroboa.util.UrlProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.helpers.AttributesImpl;

/**
 * Class responsible to serialize JCR nodes according to 
 * Astroboa content model.
 * 
 * It mimics the behavior of JCR exporter but it traverses
 * jcr nodes and properties according to Astroboa content model.
 * 
 * It uses {@link XmlExportContentHandler} to directly write xml content
 * instead of first constructing attributes list and then passing them
 * to ContentHandler
 * 
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
public class Serializer {

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

    private ExportContentHandler exportContentHandler;

    private CmsRepositoryEntityUtils cmsRepositoryEntityUtils;

    private List<String> processedCmsRepositoryEntityIdentifiers = new ArrayList<String>();

    private final static String BCCMS_PREFIX_WITH_SEMICOLON = BetaConceptNamespaceConstants.ASTROBOA_PREFIX
            + CmsConstants.QNAME_PREFIX_SEPARATOR;

    private static final String NT_PREFIX_WITH_SEMI_COLON = JcrNamespaceConstants.NT_PREFIX
            + CmsConstants.QNAME_PREFIX_SEPARATOR;

    private static final String JCR_MIX_PREFIX_WITH_SEMI_COLON = JcrNamespaceConstants.MIX_PREFIX
            + CmsConstants.QNAME_PREFIX_SEPARATOR;

    private static final String JCR_PREFIX_WITH_SEMI_COLON = JcrNamespaceConstants.JCR_PREFIX
            + CmsConstants.QNAME_PREFIX_SEPARATOR;

    private DatatypeFactory df;

    private Map<String, String> prefixesPerType;

    private SerializationReport serializationReport;

    //private String qNameOfEntityCurrentlySerialized = null;

    private Session session;

    private DefinitionServiceDao definitionServiceDao;

    private Deque<LocalizableCmsDefinition> parentPropertyDefinitionQueue = new ArrayDeque<LocalizableCmsDefinition>();

    private List<String> propertyPathsWhoseValuesWillBeIncludedInTheSerialization = new ArrayList<String>();

    private List<String> propertyPathsWhoseValuesWillBeIncludedInTheSerializationOfObjectReferences = Arrays
            .asList("profile.title");

    private boolean objectReferenceIsSerialized = false;

    private AttributesImpl rootElementAttributes;

    private boolean useTheSameNameForAllObjects;

    private enum NodeType {
        Taxonomy, ToBeIgnored, ContentObject, Other, Topic, Space
    }

    private SerializationConfiguration serializationConfiguration;

    private Comparator<CmsPropertyDefinition> cmsPropertyDefinitionComparator = new PropertyRepresentingXmlAttributeComparator();

    public Serializer(OutputStream out, CmsRepositoryEntityUtils cmsRepositoryEntityUtils, Session session,
            SerializationConfiguration serializationConfiguration) throws Exception {

        this.serializationConfiguration = serializationConfiguration;

        if (this.serializationConfiguration == null) {
            //Default value to avoid NPE
            this.serializationConfiguration = SerializationConfiguration.repository().build();
        }

        createNewExportContentHandler(out);

        this.cmsRepositoryEntityUtils = cmsRepositoryEntityUtils;

        this.session = session;

        if (this.session == null) {
            throw new CmsException("Cannot initialize serializer because no JCR session has been provided");
        }

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

    private void createNewExportContentHandler(OutputStream out) throws IOException {

        if (serializationConfiguration.isXMLRepresentationTypeEnabled()) {
            exportContentHandler = new XmlExportContentHandler(out, serializationConfiguration.prettyPrint());
        } else if (serializationConfiguration.isJSONRepresentationTypeEnabled()) {
            exportContentHandler = new JsonExportContentHandler(out, true,
                    serializationConfiguration.prettyPrint());
        } else {
            logger.warn("Resource Representation {} is not valid within export context. Export to XML is chosen",
                    serializationConfiguration.getResourceRepresentationType());
            exportContentHandler = new XmlExportContentHandler(out, serializationConfiguration.prettyPrint());
        }
    }

    /**
     * Node to be serialized represents a root node, usually root node of all taxonomies, or root node of all users
     * or root node of all content objects and this method will serialize all its children nodes.
     * @param node
     * @throws Exception
     */
    public void serializeChildrenOfRootNode(Node node) throws Exception {
        serializeNode(node, false, FetchLevel.FULL, false, false);
    }

    /**
     * Node to be serialized represents a resource item in a collection
     * @param node
     * @param shouldVisitChildren(fetchLevel)
     * @param nodeRepresentsResourceCollectionItem
     * @throws Exception
     */
    public void serializeResourceCollectionItem(Node node, FetchLevel fetchLevel, boolean nodeRepresentsRootElement)
            throws Exception {
        serializeNode(node, false, fetchLevel, true, nodeRepresentsRootElement);
    }

    public void end() throws Exception {
        exportContentHandler.end();
    }

    public void start(AttributesImpl rootElementAttributes) throws Exception {
        exportContentHandler.start();
        this.rootElementAttributes = rootElementAttributes;
    }

    public void start() throws Exception {
        exportContentHandler.start();
    }

    public void serializeNode(Node node, boolean nodeRepresentsCmsProperty, FetchLevel fetchLevel,
            boolean nodeRepresentsResourceCollectionItem, boolean nodeRepresentsRootElement) throws Exception {

        if (logger.isDebugEnabled()) {
            logger.debug("Serializing node {}", node.getPath());
        }

        NodeType nodeType = determineNodeType(node);

        switch (nodeType) {
        case ToBeIgnored:

            serializeChildNodes(node, false, fetchLevel);

            break;
        case ContentObject:

            serializeContentObjectNode(node, nodeRepresentsResourceCollectionItem);

            break;
        case Taxonomy:

            serializeTaxonomyNode(node, fetchLevel, nodeRepresentsResourceCollectionItem,
                    nodeRepresentsRootElement);

            break;
        case Topic:
            serializeTopicNode(node, fetchLevel, true, nodeRepresentsResourceCollectionItem,
                    nodeRepresentsRootElement, false);

            break;
        case Space:
            serializeSpaceNode(node, fetchLevel, nodeRepresentsResourceCollectionItem, nodeRepresentsRootElement,
                    false);

            break;

        default:

            serializeCustomNode(node, nodeRepresentsCmsProperty, false);

            break;
        }

    }

    public void serializeSpaceNode(Node node, FetchLevel fetchLevel, boolean nodeRepresentsResourceCollectionItem,
            boolean nodeRepresentsRootElement, boolean parentSpaceNode) throws Exception {

        String spaceQName = CmsBuiltInItem.Space.getLocalPart();

        if (CmsBuiltInItem.OrganizationSpace.getJcrName().equals(node.getName())) {
            spaceQName = CmsBuiltInItem.OrganizationSpace.getLocalPart();
        }

        if (nodeRepresentsRootElement) {
            spaceQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":" + spaceQName;
        } else if (nodeRepresentsResourceCollectionItem) {
            if (outputIsJSON()) {
                //When serializing a collection of resources
                //the name of the element which represents the space
                //is CmsConstants.RESOURCE when output is JSON 
                spaceQName = CmsConstants.RESOURCE;
            } else {
                //Node represents a space which appears
                //as a root child of a resource representation element.
                //In XML corresponding element should be prefixed
                //as its parent (resourceCollection) does not share the
                //same namespace
                spaceQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":" + spaceQName;
            }
        } else if (parentSpaceNode) {
            spaceQName = CmsConstants.PARENT_SPACE;
        }

        String spaceCmsIdentifier = retrieveCmsIdentifier(node);

        if (cmsIdentifierAlreadyProcessed(spaceCmsIdentifier)) {

            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(spaceQName, spaceCmsIdentifier);

            if (node.hasProperty(CmsBuiltInItem.Name.getJcrName())) {

                writeAttribute(CmsBuiltInItem.Name.getLocalPart(),
                        node.getProperty(CmsBuiltInItem.Name.getJcrName()).getString());
            }

            addUrlForEntityRepresentedByNode(node);

            processLocalization(node);

            closeEntity(spaceQName);

        } else {
            startedNewEntitySerialization(spaceQName);

            openEntityWithAttribute(spaceQName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), spaceCmsIdentifier);

            markCmsIdentifierProcessed(spaceCmsIdentifier);

            serializeBuiltInProperties(node, parentSpaceNode);

            if (!parentSpaceNode && node.getParent().isNodeType(CmsBuiltInItem.Space.getJcrName())) {
                serializeSpaceNode(node.getParent(), FetchLevel.ENTITY, false, false, true);
            }

            if (!parentSpaceNode && shouldVisitChildren(fetchLevel)
                    && node.hasNode(CmsBuiltInItem.Space.getJcrName())) {
                NodeIterator childSpaceNodes = node.getNodes(CmsBuiltInItem.Space.getJcrName());

                if (childSpaceNodes.getSize() > 0) {

                    FetchLevel visitDepthForChildSpaces = fetchLevel == FetchLevel.FULL ? FetchLevel.FULL
                            : FetchLevel.ENTITY;

                    openEntityWithNoAttributes(CmsConstants.CHILD_SPACES);

                    while (childSpaceNodes.hasNext()) {
                        serializeSpaceNode(childSpaceNodes.nextNode(), visitDepthForChildSpaces, false, false,
                                false);
                    }

                    closeEntity(CmsConstants.CHILD_SPACES);
                }
            }

            closeEntity(spaceQName);

            finishedEntitySerialization(CmsEntityType.SPACE, spaceQName);

        }

    }

    public void serializeTopicNode(Node node, FetchLevel fetchLevel, boolean serializeTaxonomyNode,
            boolean nodeRepresentsResourceCollectionItem, boolean nodeRepresentsRootElement,
            boolean parentTopicNode) throws Exception {

        String topicCmsIdentifier = retrieveCmsIdentifier(node);

        String topicQName = CmsBuiltInItem.Topic.getLocalPart();

        if (nodeRepresentsRootElement) {
            topicQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":"
                    + CmsBuiltInItem.Topic.getLocalPart();
        } else if (nodeRepresentsResourceCollectionItem) {

            if (outputIsJSON()) {
                //When serializing a collection of resources
                //the name of the element which represents the topic
                //is CmsConstants.RESOURCE when output is JSON 
                topicQName = CmsConstants.RESOURCE;
            } else {
                //Node represents a topic which appears
                //as a root child of a resource representation element.
                //In XML corresponding element should be prefixed
                //as its parent (resourceCollection) does not share the
                //same namespace
                topicQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":"
                        + CmsBuiltInItem.Topic.getLocalPart();
            }
        } else if (parentTopicNode) {
            topicQName = CmsConstants.PARENT_TOPIC;
        }

        if (cmsIdentifierAlreadyProcessed(topicCmsIdentifier)) {

            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(topicQName, topicCmsIdentifier);

            serializeBasicTopicInformation(node);

            if (CmsConstants.PARENT_TOPIC == topicQName) {
                addOwnerAsElement(node);
            }

            closeEntity(topicQName);
        } else {
            startedNewEntitySerialization(topicQName);

            openEntityWithAttribute(topicQName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), topicCmsIdentifier);

            markCmsIdentifierProcessed(topicCmsIdentifier);

            serializeBuiltInProperties(node, parentTopicNode);

            if (serializeTaxonomyNode) {
                try {
                    Node taxonomyNode = JcrNodeUtils.getTaxonomyJcrNode(node.getParent(), false);

                    if (taxonomyNode != null) {
                        serializeTaxonomyNode(taxonomyNode, FetchLevel.ENTITY, false, false);
                    } else {
                        logger.warn("Unable to serialize taxonomy for topic {}", node.getPath());
                    }
                } catch (Exception e) {
                    logger.warn("Unable to serialize taxonomy for topic " + node.getPath(), e);
                }
            }

            //Serialize Parent Node if parent node is not a taxonomy
            if (!parentTopicNode && !node.getParent().isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) {
                serializeTopicNode(node.getParent(), FetchLevel.ENTITY, false, false, false, true);
            }

            if (!parentTopicNode && shouldVisitChildren(fetchLevel)
                    && node.hasNode(CmsBuiltInItem.Topic.getJcrName())) {
                NodeIterator childTopicNodes = node.getNodes(CmsBuiltInItem.Topic.getJcrName());

                if (childTopicNodes.getSize() > 0) {

                    FetchLevel visitDepthForChildTopics = fetchLevel == FetchLevel.FULL ? FetchLevel.FULL
                            : FetchLevel.ENTITY;

                    openEntityWithNoAttributes(CmsConstants.CHILD_TOPICS);

                    while (childTopicNodes.hasNext()) {
                        serializeTopicNode(childTopicNodes.nextNode(), visitDepthForChildTopics,
                                serializeTaxonomyNode, false, false, false);
                    }

                    closeEntity(CmsConstants.CHILD_TOPICS);
                }
            }

            closeEntity(topicQName);

            finishedEntitySerialization(CmsEntityType.TOPIC, topicQName);
        }

    }

    private void informContentHandlerWhetherEntityIsAnArray(boolean entityRepresentsAnArray) throws Exception {

        if (entityRepresentsAnArray && outputIsJSON()) {
            //This is an indication to JSON Export Content Handler that this object should be exported
            //as an array
            writeAttribute(CmsConstants.EXPORT_AS_AN_ARRAY_INSTRUCTION, "true");
        }
    }

    private boolean shouldVisitChildren(FetchLevel fetchLevel) {
        return fetchLevel != null && fetchLevel != FetchLevel.ENTITY;
    }

    private boolean outputIsJSON() {
        return serializationConfiguration.isJSONRepresentationTypeEnabled();
    }

    private void serializeChildNodes(Node node, boolean childNodesAreCmsProperties, FetchLevel fetchLevel)
            throws Exception {

        final NodeIterator childNodes = node.getNodes();

        while (childNodes.hasNext()) {
            serializeNode(childNodes.nextNode(), childNodesAreCmsProperties, fetchLevel, false, false);
        }
    }

    private void serializeAspects(Node contentObjectJcrNode) throws Exception {
        if (contentObjectJcrNode.hasProperty(CmsBuiltInItem.Aspects.getJcrName())) {
            Value[] aspects = contentObjectJcrNode.getProperty(CmsBuiltInItem.Aspects.getJcrName()).getValues();

            for (Value aspect : aspects) {
                if (contentObjectJcrNode.hasNode(aspect.getString())) {
                    serializeCustomNode(contentObjectJcrNode.getNode(aspect.getString()), true, true);
                }
            }
        }

    }

    private void serializeCustomNode(Node node, boolean nodeRepresentsCmsProperty, boolean nodeRepresentsAnAspect)
            throws Exception {

        final String nodeName = node.getName();

        if (nodeRepresentsCmsProperty && !shouldSerializeProperty(nodeName)) {
            return;
        }

        String qName = processQName(nodeName);

        String nodeCmsIdentifier = retrieveCmsIdentifier(node);

        boolean removeDefinitionFromParentPropertyDefinitionQueue = false;

        if (cmsIdentifierAlreadyProcessed(nodeCmsIdentifier)) {

            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(qName, nodeCmsIdentifier);

            if (nodeRepresentsCmsProperty) {
                addToParentPropertyDefinitionQueueDefinitionForName(nodeName, false);
                removeDefinitionFromParentPropertyDefinitionQueue = true;
            }

            closeEntity(qName);
        } else {
            if (!nodeRepresentsCmsProperty && qName.endsWith("repositoryUser")) {
                startedNewEntitySerialization(qName);
            }

            boolean exportCommonAttributes = !nodeRepresentsCmsProperty
                    || propertyDefinitionDefinesCommonAttributes(nodeName);

            if (nodeCmsIdentifier != null && exportCommonAttributes) {
                openEntityWithAttribute(qName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), nodeCmsIdentifier);
            } else {
                openEntityWithNoAttributes(qName);
            }

            if (nodeRepresentsAnAspect) {
                addXsiTypeAttribute(retrievePrefixedQName(qName));
            }

            if (nodeRepresentsCmsProperty) {

                boolean multiple = propertyCanHaveMultiplevalues(nodeName);

                informContentHandlerWhetherEntityIsAnArray(multiple);

                if (node.isNodeType(CmsBuiltInItem.BinaryChannel.getJcrName())) {
                    serializeBinaryChannelNode(node, multiple, nodeName);
                } else {
                    serializeChildCmsProperties(node);
                }
            } else {
                serializeBuiltInProperties(node, false);

                serializeChildNodes(node, nodeRepresentsCmsProperty, FetchLevel.FULL);
            }

            closeEntity(qName);

            if (!nodeRepresentsCmsProperty && qName.endsWith("repositoryUser")) {
                finishedEntitySerialization(CmsEntityType.REPOSITORY_USER, qName);
            }

        }

        if (removeDefinitionFromParentPropertyDefinitionQueue) {
            removeTheHeadDefinitionFromParentPropertyDefinitionQueue();
        }

    }

    private boolean propertyCanHaveMultiplevalues(String name) throws Exception {

        LocalizableCmsDefinition cmsDefinition = retrieveCmsDefinition(name, false);

        if (cmsDefinition == null) {
            logger.warn(
                    "Could not find definition for property {}. Cannot decide whether this property can have multiple values or not. Serialization will continue and consider this property as a single-value property",
                    name);
            return false;
        }

        return cmsDefinition instanceof CmsPropertyDefinition
                && ((CmsPropertyDefinition) cmsDefinition).isMultiple();
    }

    private boolean propertyDefinitionDefinesCommonAttributes(String name) throws Exception {

        LocalizableCmsDefinition cmsDefinition = retrieveCmsDefinition(name, false);

        if (cmsDefinition == null) {
            logger.warn(
                    "Could not find definition for property {}. Cannot decide whether this property defines common attributes or not. Serialization will continue and consider that this property does not define any common attribute");
            return false;
        }

        return cmsDefinition.getValueType() == ValueType.Binary
                || (cmsDefinition.getValueType() == ValueType.Complex
                        && ((ComplexCmsPropertyDefinitionImpl) cmsDefinition).commonAttributesAreDefined());
    }

    private void serializeBinaryChannelNode(Node node, boolean multiple, String propertyName) throws Exception {

        Node contentObjectNode = retrieveContentObjectNodeFromNode(node);

        String mimeType = null;
        String sourceFilename = null;

        if (node.hasProperty(JcrBuiltInItem.JcrEncoding.getJcrName())) {
            writeAttribute(CmsBuiltInItem.Encoding.getLocalPart(),
                    node.getProperty(JcrBuiltInItem.JcrEncoding.getJcrName()).getString());
        }

        if (node.hasProperty(JcrBuiltInItem.JcrMimeType.getJcrName())) {
            mimeType = node.getProperty(JcrBuiltInItem.JcrMimeType.getJcrName()).getString();
            writeAttribute(CmsBuiltInItem.MimeType.getLocalPart(), mimeType);
        }

        if (node.hasProperty(JcrBuiltInItem.JcrLastModified.getJcrName())) {

            String dateTime = convertCalendarToXMLFormat(
                    node.getProperty(JcrBuiltInItem.JcrLastModified.getJcrName()).getDate(), true);

            writeAttribute("lastModificationDate", dateTime);
        }

        if (node.hasProperty(CmsBuiltInItem.SourceFileName.getJcrName())) {
            sourceFilename = node.getProperty(CmsBuiltInItem.SourceFileName.getJcrName()).getString();
            writeAttribute(CmsBuiltInItem.SourceFileName.getLocalPart(), sourceFilename);
        }

        String url = createResourceApiURLForBinaryChannel(contentObjectNode, node, multiple, propertyName);

        writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME, url);

        if (serializationConfiguration.serializeBinaryContent()
                && node.hasProperty(JcrBuiltInItem.JcrData.getJcrName())) {
            exportContentHandler.closeOpenElement();

            openEntityWithNoAttributes("content");

            exportContentHandler.closeOpenElement();

            serializeBinaryValue(node.getProperty(JcrBuiltInItem.JcrData.getJcrName()).getValue());

            closeEntity("content");
        }
    }

    private String createResourceApiURLForBinaryChannel(Node contentObjectNode, Node binaryChannelNode,
            boolean multiple, String propertyName) throws Exception {

        //Create a fake BinaryChannel and use its method
        BinaryChannelImpl binaryChannel = new BinaryChannelImpl();

        if (binaryChannelNode.hasProperty(CmsBuiltInItem.CmsIdentifier.getJcrName())) {
            binaryChannel
                    .setId(binaryChannelNode.getProperty(CmsBuiltInItem.CmsIdentifier.getJcrName()).getString());
        }
        binaryChannel.setAuthenticationToken(AstroboaClientContextHolder.getActiveAuthenticationToken());
        binaryChannel.setRepositoryId(AstroboaClientContextHolder.getActiveRepositoryId());

        if (contentObjectNode.hasProperty(CmsBuiltInItem.CmsIdentifier.getJcrName())) {
            binaryChannel.setContentObjectId(
                    contentObjectNode.getProperty(CmsBuiltInItem.CmsIdentifier.getJcrName()).getString());
        }

        if (contentObjectNode.hasProperty(CmsBuiltInItem.SystemName.getJcrName())) {
            binaryChannel.setContentObjectSystemName(
                    contentObjectNode.getProperty(CmsBuiltInItem.SystemName.getJcrName()).getString());
        }

        binaryChannel.setBinaryPropertyPermanentPath(
                retrievePermanentPathForBinaryNode(binaryChannelNode, propertyName));

        if (multiple) {
            binaryChannel.binaryPropertyIsMultiValued();
        }

        return binaryChannel.buildResourceApiURL(null, null, null, null, null, false, false);

    }

    private String retrievePermanentPathForBinaryNode(Node binaryChannelNode, String propertyName)
            throws Exception {

        String permanentPath = propertyName;

        LocalizableCmsDefinition binaryPropertyDefinition = retrieveCmsDefinition(propertyName, false);

        if (binaryPropertyDefinition == null || !(binaryPropertyDefinition instanceof BinaryPropertyDefinition)) {
            logger.warn("Could not find definition for property {}. "
                    + " Permanent path will be calculated based on binary channel node path");
        }

        Node complexPropertyNode = binaryChannelNode.getParent();
        CmsDefinition complexPropertyDefinition = (binaryPropertyDefinition != null
                && binaryPropertyDefinition instanceof BinaryPropertyDefinition)
                        ? ((BinaryPropertyDefinition) binaryPropertyDefinition).getParentDefinition()
                        : null;
        boolean complexPropertyDefinitionIsMultiple = complexPropertyDefinition != null
                && complexPropertyDefinition instanceof CmsPropertyDefinition
                && ((CmsPropertyDefinition) complexPropertyDefinition).isMultiple();

        while (complexPropertyNode != null
                && !complexPropertyNode.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) {

            if (!complexPropertyDefinitionIsMultiple) {
                permanentPath = complexPropertyNode.getName() + CmsConstants.PERIOD_DELIM + permanentPath;
            } else {
                //Must provide identifier or index 
                if (complexPropertyNode.hasProperty(CmsBuiltInItem.CmsIdentifier.getJcrName())) {
                    permanentPath = complexPropertyNode.getName() + CmsConstants.LEFT_BRACKET
                            + complexPropertyNode.getProperty(CmsBuiltInItem.CmsIdentifier.getJcrName()).getString()
                            + CmsConstants.RIGHT_BRACKET + CmsConstants.PERIOD_DELIM + permanentPath;
                } else {
                    //Get its index. Check for order property
                    long index = 0;

                    if (complexPropertyNode.hasProperty(CmsBuiltInItem.Order.getJcrName())) {
                        index = complexPropertyNode.getProperty(CmsBuiltInItem.Order.getJcrName()).getLong();
                    } else {
                        index = complexPropertyNode.getIndex();
                    }

                    if (index > 0) {
                        permanentPath = complexPropertyNode.getName() + CmsConstants.LEFT_BRACKET + index
                                + CmsConstants.RIGHT_BRACKET + CmsConstants.PERIOD_DELIM + permanentPath;
                    } else {
                        permanentPath = complexPropertyNode.getName() + CmsConstants.PERIOD_DELIM + permanentPath;
                    }
                }
            }

            complexPropertyNode = complexPropertyNode.getParent();
            complexPropertyDefinition = (complexPropertyDefinition != null
                    && complexPropertyDefinition instanceof CmsPropertyDefinition)
                            ? ((CmsPropertyDefinition) complexPropertyDefinition).getParentDefinition()
                            : null;
            complexPropertyDefinitionIsMultiple = complexPropertyDefinition != null
                    && complexPropertyDefinition instanceof CmsPropertyDefinition
                    && ((CmsPropertyDefinition) complexPropertyDefinition).isMultiple();

        }

        return permanentPath;
    }

    private Node retrieveContentObjectNodeFromNode(Node node) throws RepositoryException {

        Node contentObjectNode = node;

        while (contentObjectNode != null
                && !contentObjectNode.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) {
            contentObjectNode = contentObjectNode.getParent();

            if (contentObjectNode == null || contentObjectNode.isNodeType(CmsBuiltInItem.SYSTEM.getJcrName())) {
                return null;
            }
        }

        return contentObjectNode;
    }

    private void serializeBinaryValue(Value value)
            throws Exception, ValueFormatException, RepositoryException, PathNotFoundException {

        String content = JackrabbitDependentUtils.serializeBinaryValue(value);
        exportContentHandler.writeContent(content.toCharArray(), 0, content.length());
    }

    /*
     * Checks whether property should be serialized or not
     */
    private boolean shouldSerializeProperty(String propertyName) {

        //Get property path
        String propertyPath = retrieveFullPathForProperty(propertyName);

        if (objectReferenceIsSerialized) {
            return MarshalUtils.propertyShouldBeMarshalled(
                    propertyPathsWhoseValuesWillBeIncludedInTheSerializationOfObjectReferences, propertyName,
                    propertyPath);
        } else {
            return MarshalUtils.propertyShouldBeMarshalled(propertyPathsWhoseValuesWillBeIncludedInTheSerialization,
                    propertyName, propertyPath);
        }
    }

    //Method responsible to serialize properties of
    // a complex cms property in the order specified in XSD
    //Child properties can either be a jcr property or a node
    private void serializeChildCmsProperties(Node node) throws Exception {

        if (node == null) {
            logger.warn("Could not serialize a null jcr node");
            return;
        }

        addToParentPropertyDefinitionQueueDefinitionForName(node.getName(),
                node.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName()));

        Map<String, CmsPropertyDefinition> orderedChildCmsPropertyNames = retrieveChildPropertyDefinitionsPerName(
                node);

        if (orderedChildCmsPropertyNames != null && !orderedChildCmsPropertyNames.isEmpty()) {

            long numberOfJcrPropertiesLeftToProcess = node.getProperties().getSize();
            long numberOfJcrNodesLeftToProcess = node.getNodes().getSize();

            String objectPath = createObjectPath(node);

            for (Entry<String, CmsPropertyDefinition> cmsPropertyDefinitionEntry : orderedChildCmsPropertyNames
                    .entrySet()) {

                if (numberOfJcrNodesLeftToProcess == 0 && numberOfJcrPropertiesLeftToProcess == 0) {
                    //No more jcr items left, thus no more properties exist for this node.
                    //No point in iterating the rest of Cms Properties 
                    break;
                }

                String cmsPropertyName = cmsPropertyDefinitionEntry.getKey();

                //TODO : Properties which have not been saved but have default value, 
                //should exist in the serialization? This is the case when serialization is done through
                // xml() and json() methods.These methods use Astroboa API which renders a property
                //and provides the default value if this property is not found in repository
                //This case stands only for optional properties with default values

                if (cmsPropertyDefinitionEntry.getValue().getValueType() != ValueType.Complex
                        && cmsPropertyDefinitionEntry.getValue().getValueType() != ValueType.Binary
                        && numberOfJcrPropertiesLeftToProcess > 0) {

                    if (node.hasProperty(cmsPropertyName)) {
                        serializeProperty(node.getProperty(cmsPropertyName), objectPath);
                        numberOfJcrPropertiesLeftToProcess--;
                    }
                } else if (numberOfJcrNodesLeftToProcess > 0) {

                    if (node.hasNode(cmsPropertyName)) {

                        NodeIterator childNodes = node.getNodes(cmsPropertyName);

                        if (childNodes.getSize() == 1) {
                            serializeCustomNode(childNodes.nextNode(), true, false);
                            numberOfJcrNodesLeftToProcess--;
                        } else {
                            Map<Integer, Node> nodesPerOrder = new TreeMap<Integer, Node>();

                            int unknownIndex = 10000;

                            while (childNodes.hasNext()) {
                                Node childNode = childNodes.nextNode();

                                if (childNode.hasProperty(CmsBuiltInItem.Order.getJcrName())) {
                                    try {

                                        int index = (int) childNode.getProperty(CmsBuiltInItem.Order.getJcrName())
                                                .getLong() - 1;

                                        if (nodesPerOrder.containsKey(index)) {
                                            nodesPerOrder.put(unknownIndex++, childNode);
                                        } else {
                                            nodesPerOrder.put(index, childNode);
                                        }

                                    } catch (Exception e) {
                                        logger.warn("Node " + childNode.getPath()
                                                + " did not have a valid order value and therefore corresponding cms property will be added at the end of the list",
                                                e);

                                        nodesPerOrder.put(unknownIndex++, childNode);
                                    }
                                } else {
                                    nodesPerOrder.put(unknownIndex++, childNode);
                                }
                            }

                            for (Node child : nodesPerOrder.values()) {
                                serializeCustomNode(child, true, false);
                                numberOfJcrNodesLeftToProcess--;
                            }
                        }
                    }
                }
            }
        } else {
            //   Could not serialize properties in order. Follow the default procedure
            serializeProperties(node);

            serializeChildNodes(node, true, FetchLevel.FULL);
        }

        removeTheHeadDefinitionFromParentPropertyDefinitionQueue();
    }

    private String createObjectPath(Node node) throws RepositoryException, PathNotFoundException {

        String objectPath = node.getPath();

        if (node.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())
                && node.hasProperty(CmsBuiltInItem.SystemName.getJcrName())) {

            objectPath += "[systemName=" + node.getProperty(CmsBuiltInItem.SystemName.getJcrName()) + "]";
        }

        return objectPath;
    }

    private Map<String, CmsPropertyDefinition> retrieveChildPropertyDefinitionsPerName(Node node) throws Exception {

        LocalizableCmsDefinition currentDefinition = parentPropertyDefinitionQueue.peek();

        if (currentDefinition != null) {

            Map<String, CmsPropertyDefinition> definitions = new LinkedHashMap<String, CmsPropertyDefinition>();
            List<CmsPropertyDefinition> listOfDefinitionEntries = new ArrayList<CmsPropertyDefinition>();

            if (currentDefinition instanceof ComplexCmsPropertyDefinition) {
                listOfDefinitionEntries.addAll(((ComplexCmsPropertyDefinition) currentDefinition)
                        .getChildCmsPropertyDefinitions().values());
            } else if (currentDefinition instanceof ContentObjectTypeDefinition) {
                listOfDefinitionEntries.addAll(
                        ((ContentObjectTypeDefinition) currentDefinition).getPropertyDefinitions().values());
            }

            //Sort entries
            Collections.sort(listOfDefinitionEntries, cmsPropertyDefinitionComparator);

            for (CmsPropertyDefinition definition : listOfDefinitionEntries) {
                definitions.put(definition.getName(), definition);
            }

            return definitions;

        }

        return null;
    }

    private void removeTheHeadDefinitionFromParentPropertyDefinitionQueue() {
        parentPropertyDefinitionQueue.poll();
    }

    private void addToParentPropertyDefinitionQueueDefinitionForName(String name, boolean nameRefersToAContentType)
            throws Exception {

        LocalizableCmsDefinition cmsPropertyDefinition = retrieveCmsDefinition(name, nameRefersToAContentType);

        if (cmsPropertyDefinition != null) {
            parentPropertyDefinitionQueue.push(cmsPropertyDefinition);
        }

    }

    private LocalizableCmsDefinition retrieveCmsDefinition(String name, boolean nameRefersToAContentType)
            throws Exception {

        LocalizableCmsDefinition currentDefinition = parentPropertyDefinitionQueue.peek();

        LocalizableCmsDefinition cmsPropertyDefinition = null;

        if (currentDefinition != null && !nameRefersToAContentType) {

            if (currentDefinition instanceof ComplexCmsPropertyDefinition) {

                cmsPropertyDefinition = ((ComplexCmsPropertyDefinition) currentDefinition)
                        .getChildCmsPropertyDefinition(name);
            } else if (currentDefinition instanceof ContentObjectTypeDefinition) {
                cmsPropertyDefinition = ((ContentObjectTypeDefinition) currentDefinition)
                        .getCmsPropertyDefinition(name);
            }
        } else {
            if (!nameRefersToAContentType) {
                cmsPropertyDefinition = definitionServiceDao.getCmsPropertyDefinition(name);
            }

            if (cmsPropertyDefinition == null) {
                cmsPropertyDefinition = definitionServiceDao.getContentObjectTypeDefinition(name);
            }

        }
        return cmsPropertyDefinition;
    }

    protected String processQName(String qName) {
        if (qName != null && qName.startsWith(BCCMS_PREFIX_WITH_SEMICOLON)) {
            return qName.replaceFirst(BCCMS_PREFIX_WITH_SEMICOLON, "");
        }

        return qName;
    }

    private void processLocalization(Node node) throws Exception {

        if (node.hasNode(CmsBuiltInItem.Localization.getJcrName())) {
            Node localizationJcrNode = node.getNode(CmsBuiltInItem.Localization.getJcrName());

            PropertyIterator locales = localizationJcrNode
                    .getProperties(ItemUtils.createNewBetaConceptItem(CmsConstants.ANY_NAME).getJcrName());

            if (locales.getSize() > 0) {
                openEntityWithNoAttributes(CmsBuiltInItem.Localization.getLocalPart());

                if (outputIsJSON()) {
                    serializeLocalizationForJSONFormat(locales);
                } else {
                    serializeLocalizationForXMLFormat(locales);
                }

                closeEntity(CmsBuiltInItem.Localization.getLocalPart());
            }
        }
    }

    private void serializeLocalizationForXMLFormat(PropertyIterator locales)
            throws RepositoryException, ValueFormatException, Exception {
        while (locales.hasNext()) {
            Property localeProperty = locales.nextProperty();

            String locale = localeProperty.getName().replaceAll(BetaConceptNamespaceConstants.ASTROBOA_PREFIX + ":",
                    "");

            String localizedLabel = localeProperty.getString();

            openEntityWithAttribute(CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME,
                    CmsConstants.LANG_ATTRIBUTE_NAME_WITH_PREFIX, locale);

            char[] charArray = localizedLabel.toCharArray();
            writeElementContent(charArray);

            closeEntity(CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME);
        }
    }

    private void serializeLocalizationForJSONFormat(PropertyIterator locales)
            throws RepositoryException, ValueFormatException, Exception {

        openEntityWithNoAttributes(CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME);

        while (locales.hasNext()) {
            Property localeProperty = locales.nextProperty();

            String locale = localeProperty.getName().replaceAll(BetaConceptNamespaceConstants.ASTROBOA_PREFIX + ":",
                    "");

            String localizedLabel = localeProperty.getString();

            writeAttribute(locale, localizedLabel);

        }
        closeEntity(CmsConstants.LOCALIZED_LABEL_ELEMENT_NAME);
    }

    public void serializeTaxonomyNode(Node node, FetchLevel fetchLevel,
            boolean nodeRepresentsResourceCollectionItem, boolean nodeRepresentsRootElement) throws Exception {

        String taxonomyCmsIdentifier = retrieveCmsIdentifier(node);

        String taxonomyQName = CmsBuiltInItem.Taxonomy.getLocalPart();

        if (nodeRepresentsRootElement) {
            taxonomyQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":"
                    + CmsBuiltInItem.Taxonomy.getLocalPart();
        }

        if (nodeRepresentsResourceCollectionItem) {
            if (outputIsJSON()) {
                //When serializing a collection of resources
                //the name of the element which represents the taxonomy
                //is CmsConstants.RESOURCE when output is JSON 
                taxonomyQName = CmsConstants.RESOURCE;
            } else {
                //Node represents a taxonomy which appears
                //as a root child of a resource representation element.
                //In XML corresponding element should be prefixed
                //as its parent (resourceCollection) does not share the
                //same namespace
                taxonomyQName = BetaConceptNamespaceConstants.ASTROBOA_MODEL_DEFINITION_PREFIX + ":"
                        + CmsBuiltInItem.Taxonomy.getLocalPart();
            }
        }

        if (cmsIdentifierAlreadyProcessed(taxonomyCmsIdentifier)) {

            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(taxonomyQName,
                    taxonomyCmsIdentifier);

            //Taxonomy name is the name of the node. Very SPECIAL CASE
            writeAttribute(CmsBuiltInItem.Name.getLocalPart(), node.getName());

            addUrlForEntityRepresentedByNode(node);

            processLocalization(node);

            closeEntity(taxonomyQName);
        } else {
            startedNewEntitySerialization(taxonomyQName);

            openEntityWithAttribute(taxonomyQName, CmsBuiltInItem.CmsIdentifier.getLocalPart(),
                    taxonomyCmsIdentifier);

            markCmsIdentifierProcessed(taxonomyCmsIdentifier);

            //Taxonomy name is the name of the node. Very SPECIAL CASE
            writeAttribute(CmsBuiltInItem.Name.getLocalPart(), node.getName());

            serializeBuiltInProperties(node, false);

            if (shouldVisitChildren(fetchLevel) && node.hasNode(CmsBuiltInItem.Topic.getJcrName())) {
                NodeIterator rootTopicNodes = node.getNodes(CmsBuiltInItem.Topic.getJcrName());

                if (rootTopicNodes.getSize() > 0) {

                    openEntityWithNoAttributes(CmsConstants.ROOT_TOPICS);

                    FetchLevel visitDepthForRootTopics = fetchLevel == FetchLevel.FULL ? FetchLevel.FULL
                            : FetchLevel.ENTITY;

                    while (rootTopicNodes.hasNext()) {
                        serializeTopicNode(rootTopicNodes.nextNode(), visitDepthForRootTopics, true, false, false,
                                false);
                    }

                    closeEntity(CmsConstants.ROOT_TOPICS);
                }
            }

            closeEntity(taxonomyQName);

            finishedEntitySerialization(CmsEntityType.TAXONOMY, taxonomyQName);
        }

    }

    public void serializeContentObjectNode(Node node, boolean nodeRepresentsResourceCollectionItem)
            throws Exception {

        String cmsIdentifier = retrieveCmsIdentifier(node);

        String contentObjectQName = retrievePrefixedQName(node.getName());

        if (nodeRepresentsResourceCollectionItem) {
            //When serializing a collection of resources
            //the name of the element which represents the content object
            //is CmsConstants.RESOURCE when output is JSON or when 
            //user has requested that all xml elements which represent the content object
            //will be named after the same name, which is the name "resource"
            if (outputIsJSON() || useTheSameNameForAllObjects) {
                contentObjectQName = CmsConstants.RESOURCE;
            }
        }

        if (cmsIdentifierAlreadyProcessed(cmsIdentifier)) {

            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(contentObjectQName, cmsIdentifier);

            serializeBasicContentObjectInformation(cmsIdentifier, node);

            closeEntity(contentObjectQName);
        } else {
            startedNewEntitySerialization(contentObjectQName);

            openEntityWithAttribute(contentObjectQName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), cmsIdentifier);

            markCmsIdentifierProcessed(cmsIdentifier);

            //addXsiTypeAttribute(retrievePrefixedQName(contentObjectQName));

            serializeBuiltInProperties(node, false);

            serializeChildCmsProperties(node);

            serializeAspects(node);

            closeEntity(contentObjectQName);

            finishedEntitySerialization(CmsEntityType.OBJECT, contentObjectQName);
        }

    }

    private void finishedEntitySerialization(CmsEntityType cmsEntity, String qName) {

        //if (StringUtils.equals(qNameOfEntityCurrentlySerialized, qName)){

        increaseNumberOfSerializedEntities(cmsEntity);

        //   qNameOfEntityCurrentlySerialized = null;
        //}
    }

    private void startedNewEntitySerialization(String qName) {

        //if (qNameOfEntityCurrentlySerialized == null){
        //   qNameOfEntityCurrentlySerialized = qName;
        //}
    }

    private void addXsiTypeAttribute(String qName) throws Exception {
        if (!outputIsJSON()) {
            writeAttribute("xsi:type", qName);
        }

    }

    private void increaseNumberOfSerializedEntities(CmsEntityType entity) {
        if (serializationReport != null) {
            switch (entity) {
            case TAXONOMY:
                ((SerializationReportImpl) serializationReport).increaseNumberOfCompletedSerializedTaxonomies(1);
                break;
            case OBJECT:
                ((SerializationReportImpl) serializationReport).increaseNumberOfCompletedSerializedObjects(1);
                break;
            case REPOSITORY_USER:
                ((SerializationReportImpl) serializationReport)
                        .increaseNumberOfCompletedSerializedRepositoryUsers(1);
                break;
            case SPACE:
                ((SerializationReportImpl) serializationReport).increaseNumberOfCompletedSerializedSpaces(1);
                break;
            case TOPIC:
                ((SerializationReportImpl) serializationReport).increaseNumberOfCompletedSerializedTopics(1);
                break;

            default:
                break;
            }

        }
    }

    private String retrievePrefixedQName(String qName) {
        if (prefixesPerType != null && qName != null && prefixesPerType.containsKey(qName)) {
            return prefixesPerType.get(qName) + CmsConstants.QNAME_PREFIX_SEPARATOR + qName;
        }

        return qName;
    }

    private String retrieveCmsIdentifier(Node node) throws RepositoryException {
        String cmsIdentifier = null;

        if (cmsRepositoryEntityUtils.hasCmsIdentifier(node)) {
            cmsIdentifier = cmsRepositoryEntityUtils.getCmsIdentifier(node);
        }
        return cmsIdentifier;
    }

    private boolean cmsIdentifierAlreadyProcessed(String cmsIdentifier) {

        return cmsIdentifier != null && processedCmsRepositoryEntityIdentifiers.contains(cmsIdentifier);
    }

    private void serializeBuiltInProperties(Node node, boolean nodeRepresentsParentNodeOfTopicOrSpace)
            throws Exception {

        PropertyIterator builtInProperties = node
                .getProperties(BCCMS_PREFIX_WITH_SEMICOLON + CmsConstants.ANY_NAME);

        boolean addOwner = false;

        while (builtInProperties.hasNext()) {
            Property property = builtInProperties.nextProperty();

            if (property.getName().equals(CmsBuiltInItem.Name.getJcrName())) {
                writeAttribute(CmsBuiltInItem.Name.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.SystemName.getJcrName())) {
                writeAttribute(CmsBuiltInItem.SystemName.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.ContentObjectTypeName.getJcrName())) {
                writeAttribute(CmsBuiltInItem.ContentObjectTypeName.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.AllowsReferrerContentObjects.getJcrName())) {
                if (!nodeRepresentsParentNodeOfTopicOrSpace) {
                    writeAttribute(CmsBuiltInItem.AllowsReferrerContentObjects.getLocalPart(),
                            String.valueOf(property.getBoolean()));
                }
            } else if (property.getName().equals(CmsBuiltInItem.Encoding.getJcrName())) {
                writeAttribute(CmsBuiltInItem.Encoding.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.ExternalId.getJcrName())) {
                writeAttribute(CmsBuiltInItem.ExternalId.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.Label.getJcrName())) {
                writeAttribute(CmsBuiltInItem.Label.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.MimeType.getJcrName())) {
                writeAttribute(CmsBuiltInItem.MimeType.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.Order.getJcrName())
                    && (node.isNodeType(CmsBuiltInItem.Space.getJcrName())
                            || node.isNodeType(CmsBuiltInItem.Topic.getJcrName()))) {
                if (!nodeRepresentsParentNodeOfTopicOrSpace) {
                    writeAttribute(CmsBuiltInItem.Order.getLocalPart(), property.getString());
                }
            } else if (property.getName().equals(CmsBuiltInItem.SourceFileName.getJcrName())) {
                writeAttribute(CmsBuiltInItem.SourceFileName.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.Size.getJcrName())) {
                writeAttribute(CmsBuiltInItem.Size.getLocalPart(), property.getString());
            } else if (property.getName().equals(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())) {
                //Do not add owner if entity to be serialized is content object and specific 
                //properties must be serialized
                if (CollectionUtils.isEmpty(propertyPathsWhoseValuesWillBeIncludedInTheSerialization)
                        || propertyPathsWhoseValuesWillBeIncludedInTheSerialization
                                .contains(CmsConstants.OWNER_ELEMENT_NAME)) {
                    addOwner = true;
                }
            }

        }

        //Add url for this node
        addUrlForEntityRepresentedByNode(node);

        if (!nodeRepresentsParentNodeOfTopicOrSpace) {
            addNumberOfChildren(node);
        }

        processLocalization(node);

        if (addOwner) {
            addOwnerAsElement(node);
        }
    }

    private void addUrlForEntityRepresentedByNode(Node node) throws Exception {

        UrlProperties urlProperties = new UrlProperties();
        urlProperties.setRelative(false);
        urlProperties.setResourceRepresentationType(serializationConfiguration.getResourceRepresentationType());

        if (node.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) {
            if (node.hasProperty(CmsBuiltInItem.SystemName.getJcrName())) {

                urlProperties.setFriendly(true);
                urlProperties.setName(node.getProperty(CmsBuiltInItem.SystemName.getJcrName()).getString());

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(ContentObject.class, urlProperties));
            } else {

                urlProperties.setFriendly(false);
                urlProperties.setIdentifier(cmsRepositoryEntityUtils.getCmsIdentifier(node));

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(ContentObject.class, urlProperties));
            }
        } else if (node.isNodeType(CmsBuiltInItem.Topic.getJcrName())) {

            if (node.hasProperty(CmsBuiltInItem.Name.getJcrName())) {

                urlProperties.setFriendly(true);
                urlProperties.setName(node.getProperty(CmsBuiltInItem.Name.getJcrName()).getString());

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(Topic.class, urlProperties));
            } else {
                urlProperties.setFriendly(false);
                urlProperties.setIdentifier(cmsRepositoryEntityUtils.getCmsIdentifier(node));

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(Topic.class, urlProperties));
            }

        } else if (node.isNodeType(CmsBuiltInItem.Space.getJcrName())) {

            if (node.hasProperty(CmsBuiltInItem.Name.getJcrName())) {

                urlProperties.setFriendly(true);
                urlProperties.setName(node.getProperty(CmsBuiltInItem.Name.getJcrName()).getString());

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(Space.class, urlProperties));
            } else {

                urlProperties.setFriendly(false);
                urlProperties.setIdentifier(cmsRepositoryEntityUtils.getCmsIdentifier(node));

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(Space.class, urlProperties));
            }

        } else if (node.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) {

            urlProperties.setFriendly(true);
            urlProperties.setName(node.getName());

            writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                    ResourceApiURLUtils.generateUrlForType(Taxonomy.class, urlProperties));
        }

    }

    private void addNumberOfChildren(Node node) throws RepositoryException, Exception {
        if (node.isNodeType(CmsBuiltInItem.Topic.getJcrName())
                || node.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) {
            if (node.hasNode(CmsBuiltInItem.Topic.getJcrName())) {
                writeAttribute(CmsConstants.NUMBER_OF_CHILDREN_ATTRIBUTE_NAME,
                        String.valueOf(node.getNodes(CmsBuiltInItem.Topic.getJcrName()).getSize()));
            } else {
                writeAttribute(CmsConstants.NUMBER_OF_CHILDREN_ATTRIBUTE_NAME, "0");
            }
        } else if (node.isNodeType(CmsBuiltInItem.Space.getJcrName())) {
            if (node.hasNode(CmsBuiltInItem.Space.getJcrName())) {
                writeAttribute(CmsConstants.NUMBER_OF_CHILDREN_ATTRIBUTE_NAME,
                        String.valueOf(node.getNodes(CmsBuiltInItem.Space.getJcrName()).getSize()));
            } else {
                writeAttribute(CmsConstants.NUMBER_OF_CHILDREN_ATTRIBUTE_NAME, "0");
            }
        }
    }

    private void serializeProperties(Node node) throws Exception {

        PropertyIterator properties = node.getProperties();

        String objectPath = createObjectPath(node);

        while (properties.hasNext()) {
            Property property = properties.nextProperty();

            //Do not process any property which starts with jcr, nt or bccms
            String propertyName = property.getName();

            if (propertyName.startsWith(JCR_MIX_PREFIX_WITH_SEMI_COLON)
                    || propertyName.startsWith(JCR_PREFIX_WITH_SEMI_COLON)
                    || propertyName.startsWith(NT_PREFIX_WITH_SEMI_COLON)
                    || propertyName.startsWith(BCCMS_PREFIX_WITH_SEMICOLON)) {
                continue;
            }

            serializeProperty(property, objectPath);

        }

    }

    private void serializeProperty(Property property, String objectPath) throws Exception {
        String propertyName = property.getName();

        if (shouldSerializeProperty(propertyName)) {

            CmsPropertyDefinition propertyDefinition = retrieveDefinitionForChildProperty(propertyName);

            if (propertyDefinition == null) {
                logger.warn("Could not serialize property {}. No definition found. Property path in JCR is {}",
                        propertyName, property.getPath());
                return;
            }

            final ValueType valueType = propertyDefinition.getValueType();

            boolean dateTimePattern = propertyDefinition != null && ValueType.Date == valueType
                    && ((CalendarPropertyDefinition) propertyDefinition).isDateTime();

            /*
             * There are some cases where users might change property's cardinality 
             * from multiple to single value or vice versa.
             * If user does not perform any kind of migration, there will be an inconsistency in the way 
             * property values are stored in JCR. 
             * For example, if a user decides to change property 'language' from single to multi value, 
             * then new values will be stored inside an array in contrary to the old values.
             * 
             * JCR specifies different methods for retrieving values of a property (getValue() for single
             * and getValues() for multiple) and it throws an exception if you call getValues() on a 
             * single value property. 
             * 
             * In our example, in order to get old values you need to call method getValue() and in order
             * to retrieve new values you need to call getValues()!!! This is due to the fact that JCR
             * stores property definition among other things.
             * 
             * We must detect this type of inconsistency but instead of throwing an exception, 
             * we detect which method we should call and just issue a warning. In the case where
             * cardinality is changed from multivalue to single value, we serialize only the first value
             * because we must conform to the property' definition in the XSD.
             */

            boolean userHasDefinedPropertyAsMultiple = propertyDefinition != null
                    && propertyDefinition.isMultiple();
            boolean jcrHasMarkedPropertyAsMultiple = property.getDefinition() != null
                    && property.getDefinition().isMultiple();
            boolean exportAsAnAttribute = propertyDefinition instanceof SimpleCmsPropertyDefinition
                    && ((SimpleCmsPropertyDefinitionImpl) propertyDefinition).isRepresentsAnXmlAttribute();

            if (jcrHasMarkedPropertyAsMultiple) {

                for (Value value : property.getValues()) {
                    writeValueForProperty(propertyName, value, valueType, dateTimePattern,
                            userHasDefinedPropertyAsMultiple, objectPath, exportAsAnAttribute);

                    if (!userHasDefinedPropertyAsMultiple) {
                        logger.warn("Property " + propertyDefinition.getFullPath()
                                + " has been defined as a single value property. Property's instance "
                                + property.getPath()
                                + " has been marked as multiple. Probably this property used to be a multi value property but its cardinality"
                                + " has changed. Astroboa will serialize only the first value in order to be consistent with current definition in XSD.");
                        break;
                    }
                }
            } else {
                writeValueForProperty(propertyName, property.getValue(), valueType, dateTimePattern,
                        userHasDefinedPropertyAsMultiple, objectPath, exportAsAnAttribute);

                if (userHasDefinedPropertyAsMultiple) {
                    logger.warn("Property " + propertyDefinition.getFullPath()
                            + " has been defined as a multi value property. Property's instance "
                            + property.getPath()
                            + " has been marked as single. Probably this property used to be a single value property but its cardinality"
                            + " has changed. Property value will be serialized normally");

                }
            }
        }
    }

    private String retrieveFullPathForProperty(String propertyName) {

        if (!parentPropertyDefinitionQueue.isEmpty()) {
            LocalizableCmsDefinition currentDefinition = parentPropertyDefinitionQueue.peek();

            if (currentDefinition instanceof CmsPropertyDefinition) {

                CmsPropertyDefinition rootDefinition = (CmsPropertyDefinition) currentDefinition;

                boolean rootDefinitionIsATypeDefinition = false;

                while (rootDefinition != null) {

                    if (rootDefinition.getParentDefinition() instanceof ContentObjectTypeDefinition) {
                        rootDefinitionIsATypeDefinition = true;
                        break;
                    }

                    rootDefinition = (CmsPropertyDefinition) rootDefinition.getParentDefinition();

                }

                if (rootDefinitionIsATypeDefinition) {
                    return PropertyPath.createFullPropertyPath(
                            ((CmsPropertyDefinition) currentDefinition).getPath(), propertyName);
                } else {
                    return PropertyPath.createFullPropertyPath(
                            ((CmsPropertyDefinition) currentDefinition).getFullPath(), propertyName);
                }
            }
        }

        return propertyName;
    }

    private CmsPropertyDefinition retrieveDefinitionForChildProperty(String propertyName) {

        if (!parentPropertyDefinitionQueue.isEmpty()) {
            LocalizableCmsDefinition currentDefinition = parentPropertyDefinitionQueue.peek();

            if (currentDefinition instanceof ComplexCmsPropertyDefinition) {
                return ((ComplexCmsPropertyDefinition) currentDefinition)
                        .getChildCmsPropertyDefinition(propertyName);
            } else if (currentDefinition instanceof ContentObjectTypeDefinition) {
                return ((ContentObjectTypeDefinition) currentDefinition).getCmsPropertyDefinition(propertyName);
            }
        }

        return null;
    }

    private String convertCalendarToXMLFormat(Calendar calendar, boolean dateTimePattern) {
        if (dateTimePattern) {
            GregorianCalendar gregCalendar = new GregorianCalendar(calendar.getTimeZone());
            gregCalendar.setTimeInMillis(calendar.getTimeInMillis());

            return df.newXMLGregorianCalendar(gregCalendar).toXMLFormat();
        } else {
            return 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();
        }
    }

    private void writeValueForProperty(String propertyName, Value value, ValueType valueType,
            boolean dateTimePattern, boolean propertyMayHaveMultipltValues, String objectPath,
            boolean exportAsAnAttribute) throws Exception {
        if (value != null) {

            if (exportAsAnAttribute) {

                if (ValueType.Date == valueType) {
                    String dateValue = convertCalendarToXMLFormat(value.getDate(), dateTimePattern);
                    writeAttribute(propertyName, dateValue);
                } else {
                    writeAttribute(propertyName, value.getString());
                }
            } else {
                if (ValueType.TopicReference == valueType) {
                    addTopicReferenceElement(propertyName, value.getString(), propertyMayHaveMultipltValues,
                            objectPath);
                } else if (ValueType.ObjectReference == valueType) {
                    addContentObjectReferenceElement(propertyName, value.getString(), propertyMayHaveMultipltValues,
                            objectPath);
                } else {

                    if (propertyMayHaveMultipltValues && outputIsJSON()) {
                        openEntityWithAttribute(propertyName, CmsConstants.EXPORT_AS_AN_ARRAY_INSTRUCTION, "true");
                    } else {
                        openEntityWithNoAttributes(propertyName);
                    }

                    if (ValueType.Binary == valueType) {
                        exportContentHandler.closeOpenElement();

                        serializeBinaryValue(value);

                    } else if (ValueType.Date == valueType) {
                        char[] ch = convertCalendarToXMLFormat(value.getDate(), dateTimePattern).toCharArray();
                        writeElementContent(ch);
                    } else {
                        char[] ch = value.getString().toCharArray();
                        writeElementContent(ch);
                    }

                    closeEntity(propertyName);
                }
            }
        }
    }

    private void writeElementContent(char[] ch) throws Exception {
        exportContentHandler.writeContent(ch, 0, ch.length);
    }

    private void addTopicReferenceElement(String propertyName, String topicId,
            boolean referenceMayHaveMultipleValues, String objectPath) throws Exception {

        if (cmsIdentifierAlreadyProcessed(topicId)) {
            serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(propertyName, topicId);
        } else {
            openEntityWithAttribute(propertyName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), topicId);
        }

        Node topicJcrNode = cmsRepositoryEntityUtils.retrieveUniqueNodeForTopic(session, topicId);

        if (topicJcrNode == null) {
            logger.warn(
                    "Could not serialize value {} for property {} of object {} because no topic was found with this identifier '{}'",
                    new Object[] { topicId, propertyName, objectPath, topicId });
            return;
        }

        markCmsIdentifierProcessed(topicId);

        if (referenceMayHaveMultipleValues) {
            informContentHandlerWhetherEntityIsAnArray(true);
        }

        serializeBasicTopicInformation(topicJcrNode);

        closeEntity(propertyName);
    }

    private void serializeBasicTopicInformation(Node topicJcrNode)
            throws RepositoryException, Exception, ValueFormatException, PathNotFoundException {

        if (topicJcrNode != null) {

            if (topicJcrNode.hasProperty(CmsBuiltInItem.Name.getJcrName())) {

                writeAttribute(CmsBuiltInItem.Name.getLocalPart(),
                        topicJcrNode.getProperty(CmsBuiltInItem.Name.getJcrName()).getString());
            }

            addUrlForEntityRepresentedByNode(topicJcrNode);

            processLocalization(topicJcrNode);
        }
    }

    private void addContentObjectReferenceElement(String propertyName, String contentObjectId,
            boolean referenceMayHaveMultipleValues, String objectPath) throws Exception {

        Node contentObjectJcrNode = cmsRepositoryEntityUtils.retrieveUniqueNodeForContentObject(session,
                contentObjectId);

        if (contentObjectJcrNode == null) {
            logger.warn(
                    "Could not serialize value {} for property {} of object {} because no object was found with this identifier '{}'",
                    new Object[] { contentObjectId, propertyName, objectPath, contentObjectId });
            return;
        }

        openEntityWithAttribute(propertyName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), contentObjectId);

        if (referenceMayHaveMultipleValues) {
            informContentHandlerWhetherEntityIsAnArray(true);
        }

        serializeBasicContentObjectInformation(contentObjectId, contentObjectJcrNode);

        objectReferenceIsSerialized = true;
        serializeChildCmsProperties(contentObjectJcrNode);
        serializeAspects(contentObjectJcrNode);
        objectReferenceIsSerialized = false;

        closeEntity(propertyName);

    }

    private void serializeBasicContentObjectInformation(String contentObjectId, Node contentObjectJcrNode)
            throws Exception, RepositoryException, ValueFormatException, PathNotFoundException {

        boolean systemNameFound = false;

        UrlProperties urlProperties = new UrlProperties();
        urlProperties.setRelative(false);
        urlProperties.setResourceRepresentationType(serializationConfiguration.getResourceRepresentationType());

        if (contentObjectJcrNode != null) {

            //ContentObject SystemName
            if (contentObjectJcrNode.hasProperty(CmsBuiltInItem.SystemName.getJcrName())) {

                final String systemName = contentObjectJcrNode.getProperty(CmsBuiltInItem.SystemName.getJcrName())
                        .getString();

                writeAttribute(CmsBuiltInItem.SystemName.getLocalPart(), systemName);

                urlProperties.setFriendly(true);
                urlProperties.setName(systemName);

                writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                        ResourceApiURLUtils.generateUrlForType(ContentObject.class, urlProperties));

                systemNameFound = true;
            }

            //ContentObjecType name
            if (contentObjectJcrNode.hasProperty(CmsBuiltInItem.ContentObjectTypeName.getJcrName())) {

                writeAttribute(CmsBuiltInItem.ContentObjectTypeName.getLocalPart(), contentObjectJcrNode
                        .getProperty(CmsBuiltInItem.ContentObjectTypeName.getJcrName()).getString());
            }
        }

        if (!systemNameFound) {

            urlProperties.setFriendly(false);
            urlProperties.setIdentifier(contentObjectId);

            writeAttribute(CmsConstants.URL_ATTRIBUTE_NAME,
                    ResourceApiURLUtils.generateUrlForType(ContentObject.class, urlProperties));
        }
    }

    private void addOwnerAsElement(Node node) throws Exception {

        String ownerCmsIdentifier = null;

        if (node.hasProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName())) {
            ownerCmsIdentifier = node.getProperty(CmsBuiltInItem.OwnerCmsIdentifier.getJcrName()).getString();
        }

        if (ownerCmsIdentifier != null) {
            if (cmsIdentifierAlreadyProcessed(ownerCmsIdentifier)) {

                serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(CmsConstants.OWNER_ELEMENT_NAME,
                        ownerCmsIdentifier);

            } else {
                openEntityWithAttribute(CmsConstants.OWNER_ELEMENT_NAME,
                        CmsBuiltInItem.CmsIdentifier.getLocalPart(), ownerCmsIdentifier);

                markCmsIdentifierProcessed(ownerCmsIdentifier);
            }

            Node ownerJcrNode = cmsRepositoryEntityUtils.retrieveUniqueNodeForRepositoryUser(session,
                    ownerCmsIdentifier);

            if (ownerJcrNode != null) {

                if (ownerJcrNode.hasProperty(CmsBuiltInItem.ExternalId.getJcrName())) {
                    writeAttribute(CmsBuiltInItem.ExternalId.getLocalPart(),
                            ownerJcrNode.getProperty(CmsBuiltInItem.ExternalId.getJcrName()).getString());
                }

                if (ownerJcrNode.hasProperty(CmsBuiltInItem.Label.getJcrName())) {
                    writeAttribute(CmsBuiltInItem.Label.getLocalPart(),
                            ownerJcrNode.getProperty(CmsBuiltInItem.Label.getJcrName()).getString());
                }
            }

            closeEntity(CmsConstants.OWNER_ELEMENT_NAME);
        }
    }

    private void markCmsIdentifierProcessed(String cmsIdentifier) {
        processedCmsRepositoryEntityIdentifiers.add(cmsIdentifier);
    }

    private NodeType determineNodeType(Node node) throws Exception {

        if (node.isNodeType(CmsBuiltInItem.StructuredContentObject.getJcrName())) {
            return NodeType.ContentObject;
        } else if (node.isNodeType(CmsBuiltInItem.GenericYearFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.GenericMonthFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.GenericDayFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.GenericHourFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.GenericMinuteFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.GenericSecondFolder.getJcrName())
                || node.isNodeType(CmsBuiltInItem.ContentObjectRoot.getJcrName())
                || node.isNodeType(CmsBuiltInItem.RepositoryUserRoot.getJcrName())
                || node.isNodeType(CmsBuiltInItem.TaxonomyRoot.getJcrName())) {
            return NodeType.ToBeIgnored;
        } else if (node.isNodeType(CmsBuiltInItem.Taxonomy.getJcrName())) {
            return NodeType.Taxonomy;
        } else if (node.isNodeType(CmsBuiltInItem.Topic.getJcrName())) {
            return NodeType.Topic;
        } else if (node.isNodeType(CmsBuiltInItem.Space.getJcrName())) {
            return NodeType.Space;
        }

        if (node.getParent() != null
                && StringUtils.equals(node.getParent().getName(), CmsBuiltInItem.ContentObjectRoot.getJcrName())) {
            return NodeType.ToBeIgnored;
        }

        return NodeType.Other;
    }

    private void serializeCmsRepositoryEntityIdentifierForAnAlreadySerializedEntity(String entityName,
            String entityId) throws Exception {

        openEntityWithAttribute(entityName, CmsBuiltInItem.CmsIdentifier.getLocalPart(), entityId);

    }

    public void writeAttribute(String attributeName, String attributeValue) throws Exception {
        exportContentHandler.writeAttribute(attributeName, attributeValue);
    }

    public void openEntityWithNoAttributes(String elementQName) throws Exception {

        if (rootElementAttributes != null) {
            exportContentHandler.startElement(elementQName, rootElementAttributes);
            rootElementAttributes = null;
        } else {
            exportContentHandler.startElementWithNoAttributes(elementQName);
        }
    }

    public void openEntityWithAttribute(String elementQName, String attributeName, String attributeValue)
            throws Exception {

        if (rootElementAttributes != null) {
            IOUtils.addAttribute(rootElementAttributes, attributeName, attributeValue);
            exportContentHandler.startElement(elementQName, rootElementAttributes);
            rootElementAttributes = null;
        } else {
            exportContentHandler.startElementWithOnlyOneAttribute(elementQName, attributeName, attributeValue);
        }
    }

    public void openEntity(String elementQName, AttributesImpl atts) throws Exception {

        if (rootElementAttributes != null) {
            for (int i = 0; i < atts.getLength(); i++) {
                IOUtils.addAttribute(rootElementAttributes, atts.getLocalName(i), atts.getValue(i));
            }
            exportContentHandler.startElement(elementQName, rootElementAttributes);
            rootElementAttributes = null;
        } else {
            exportContentHandler.startElement(elementQName, atts);
        }
    }

    public void closeEntity(String elementQName) throws Exception {

        exportContentHandler.endElement(elementQName);
    }

    public void setPrefixesPerType(Map<String, String> prefixesPerType) {

        this.prefixesPerType = prefixesPerType;
    }

    public void setSerializationReport(SerializationReport serializationReport) {

        this.serializationReport = serializationReport;
    }

    public void setDefinitionServiceDao(DefinitionServiceDao definitionServiceDao) {

        this.definitionServiceDao = definitionServiceDao;
    }

    public void setPropertyPathsWhoseValuesWillBeIncludedInTheSerialization(
            List<String> propertyPathsWhoseValuesWillBeIncludedInTheSerialization) {
        this.propertyPathsWhoseValuesWillBeIncludedInTheSerialization = propertyPathsWhoseValuesWillBeIncludedInTheSerialization;
    }

    public void useTheSameNameForAllObjects(boolean useTheSameNameForAllObjects) {
        this.useTheSameNameForAllObjects = useTheSameNameForAllObjects;
    }

}