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