001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     ataillefer
016 */
017
018package org.nuxeo.ecm.core.io.impl;
019
020import java.io.IOException;
021import java.util.List;
022import java.util.Map;
023
024import org.dom4j.Element;
025import org.dom4j.QName;
026import org.nuxeo.common.collections.PrimitiveArrays;
027import org.nuxeo.common.utils.Path;
028import org.nuxeo.ecm.core.api.Blob;
029import org.nuxeo.ecm.core.api.DocumentModel;
030import org.nuxeo.ecm.core.schema.Namespace;
031import org.nuxeo.ecm.core.schema.TypeConstants;
032import org.nuxeo.ecm.core.schema.types.ComplexType;
033import org.nuxeo.ecm.core.schema.types.Field;
034import org.nuxeo.ecm.core.schema.types.ListType;
035import org.nuxeo.ecm.core.schema.types.Type;
036import org.nuxeo.ecm.core.schema.types.primitives.BinaryType;
037import org.nuxeo.ecm.core.schema.types.primitives.BooleanType;
038import org.nuxeo.ecm.core.schema.types.primitives.DateType;
039import org.nuxeo.ecm.core.schema.types.primitives.DoubleType;
040import org.nuxeo.ecm.core.schema.types.primitives.IntegerType;
041import org.nuxeo.ecm.core.schema.types.primitives.LongType;
042import org.nuxeo.ecm.core.schema.types.primitives.StringType;
043
044/**
045 * A representation for an exported document aware of property types.
046 *
047 * @author <a href="mailto:ataillefer@nuxeo.com">Antoine Taillefer</a>
048 * @since 5.6
049 */
050public class TypedExportedDocumentImpl extends ExportedDocumentImpl {
051
052    private static final String TYPE_ATTRIBUTE = "type";
053
054    private static final String COMPLEX_TYPE_ID = "complex";
055
056    private static final String SCALAR_LIST_TYPE_ID = "scalarList";
057
058    private static final String COMPLEX_LIST_TYPE_ID = "complexList";
059
060    private static final String CONTENT_LIST_TYPE_ID = "contentList";
061
062    public TypedExportedDocumentImpl() {
063        super();
064    }
065
066    /**
067     * Instantiates a new typed exported document impl.
068     *
069     * @param doc the doc
070     * @param path the path to use for this document this is used to remove full paths
071     * @param inlineBlobs the inline blobs
072     * @throws IOException Signals that an I/O exception has occurred.
073     */
074    public TypedExportedDocumentImpl(DocumentModel doc, Path path, boolean inlineBlobs) throws IOException {
075        super(doc, path, inlineBlobs);
076    }
077
078    /**
079     * Instantiates a new typed exported document impl.
080     *
081     * @param doc the doc
082     * @throws IOException Signals that an I/O exception has occurred.
083     */
084    public TypedExportedDocumentImpl(DocumentModel doc) throws IOException {
085        super(doc, false);
086    }
087
088    /**
089     * Instantiates a new typed exported document impl.
090     *
091     * @param doc the doc
092     * @param inlineBlobs the inline blobs
093     * @throws IOException Signals that an I/O exception has occurred.
094     */
095    public TypedExportedDocumentImpl(DocumentModel doc, boolean inlineBlobs) throws IOException {
096        super(doc, doc.getPath(), inlineBlobs);
097    }
098
099    /**
100     * Here we do what super does but add the "type" attribute to the XML elements.
101     */
102    @Override
103    @SuppressWarnings("rawtypes")
104    protected void readProperty(Element parent, Namespace targetNs, Field field, Object value, boolean inlineBlobs)
105            throws IOException {
106        Type type = field.getType();
107        QName name = QName.get(field.getName().getLocalName(), targetNs.prefix, targetNs.uri);
108        Element element = parent.addElement(name);
109
110        // extract the element content
111        if (type.isSimpleType()) {
112            element.addAttribute(TYPE_ATTRIBUTE, getSimpleTypeId(type));
113            if (value != null) {
114                element.addText(type.encode(value));
115            }
116        } else if (type.isComplexType()) {
117            ComplexType ctype = (ComplexType) type;
118            if (TypeConstants.isContentType(ctype)) {
119                element.addAttribute(TYPE_ATTRIBUTE, TypeConstants.CONTENT);
120                if (value != null) {
121                    readBlob(element, ctype, (Blob) value, inlineBlobs);
122                }
123            } else {
124                element.addAttribute(TYPE_ATTRIBUTE, COMPLEX_TYPE_ID);
125                if (value != null) {
126                    readComplex(element, ctype, (Map) value, inlineBlobs);
127                }
128            }
129        } else if (type.isListType()) {
130            String typeId;
131            ListType listType = ((ListType) type);
132            // Scalar list
133            if (listType.isScalarList()) {
134                typeId = SCALAR_LIST_TYPE_ID;
135            }
136            // Content list
137            else if (TypeConstants.isContentType(listType.getFieldType())) {
138                typeId = CONTENT_LIST_TYPE_ID;
139            }
140            // Complex list
141            else {
142                typeId = COMPLEX_LIST_TYPE_ID;
143            }
144            element.addAttribute(TYPE_ATTRIBUTE, typeId);
145            if (value != null) {
146                if (value instanceof List) {
147                    readList(element, (ListType) type, (List) value, inlineBlobs);
148                } else if (value.getClass().getComponentType() != null) {
149                    readList(element, (ListType) type, PrimitiveArrays.toList(value), inlineBlobs);
150                } else {
151                    throw new IllegalArgumentException("A value of list type is neither list neither array: " + value);
152                }
153            }
154        }
155    }
156
157    /**
158     * Gets the simple type id.
159     *
160     * @param type the type
161     * @return the simple type id
162     */
163    protected String getSimpleTypeId(Type type) {
164
165        String typeId = StringType.ID;
166
167        if (BooleanType.INSTANCE == type) {
168            typeId = BooleanType.ID;
169        } else if (DateType.INSTANCE == type) {
170            typeId = DateType.ID;
171        } else if (LongType.INSTANCE == type) {
172            typeId = LongType.ID;
173        } else if (IntegerType.INSTANCE == type) {
174            typeId = IntegerType.ID;
175        } else if (DoubleType.INSTANCE == type) {
176            typeId = DoubleType.ID;
177        } else if (BinaryType.INSTANCE == type) {
178            typeId = BinaryType.ID;
179        }
180        return typeId;
181
182    }
183
184}