001/*
002 * (C) Copyright 2009-2016 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 *     Thomas Roger
018 */
019package org.nuxeo.ecm.platform.importer.properties;
020
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Calendar;
027import java.util.Collections;
028import java.util.List;
029import java.util.Map;
030import java.util.Properties;
031
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.common.utils.StringUtils;
035import org.nuxeo.ecm.core.api.DataModel;
036import org.nuxeo.ecm.core.api.DocumentModel;
037import org.nuxeo.ecm.core.api.NuxeoException;
038import org.nuxeo.ecm.core.api.PropertyException;
039import org.nuxeo.ecm.core.schema.types.primitives.DateType;
040
041/**
042 * Handle properties file creation from a {@code DocumentModel}.
043 * <p>
044 * Only support the types that {@code MetadataCollector} knows.
045 *
046 * @author <a href="mailto:troger@nuxeo.com">Thomas Roger</a>
047 */
048public class MetadataFile {
049
050    private static final Log log = LogFactory.getLog(MetadataFile.class);
051
052    protected DocumentModel doc;
053
054    protected Properties metadataProperties = new Properties();
055
056    /**
057     * Create a {@code MetadataFile} from a {@code DocumentModel}'s schemas.
058     *
059     * @return a new MetadataFile object
060     */
061    public static MetadataFile createFromDocument(DocumentModel doc) {
062        MetadataFile mdFile = new MetadataFile(doc);
063        mdFile.load();
064        return mdFile;
065    }
066
067    /**
068     * Create a {@code MetadataFile} from the listed schemas (with all properties) and the listed properties of a
069     * {@code DocumentModel}
070     *
071     * @return a new MetadataFile object
072     */
073    public static MetadataFile createFromSchemasAndProperties(DocumentModel doc, List<String> allPropertiesSchemas,
074            List<String> properties) {
075        MetadataFile mdFile = new MetadataFile(doc);
076        mdFile.load(allPropertiesSchemas, properties);
077        return mdFile;
078    }
079
080    /**
081     * Create a {@code MetadataFile} from the listed schemas (with all properties) of a {@code DocumentModel}
082     *
083     * @return a new MetadataFile object
084     */
085    public static MetadataFile createFromSchemas(DocumentModel doc, List<String> allPropertiesSchemas) {
086        return createFromSchemasAndProperties(doc, allPropertiesSchemas, Collections.<String> emptyList());
087    }
088
089    /**
090     * Create a {@code MetadataFile} from the listed properties of a {@code DocumentModel}
091     *
092     * @return a new MetadataFile object
093     */
094    public static MetadataFile createFromProperties(DocumentModel doc, List<String> properties) {
095        return createFromSchemasAndProperties(doc, Collections.<String> emptyList(), properties);
096    }
097
098    protected MetadataFile(DocumentModel doc) {
099        this.doc = doc;
100    }
101
102    protected void load(List<String> allPropertiesSchemas, List<String> properties) {
103        if (!metadataProperties.isEmpty()) {
104            return;
105        }
106
107        for (String schema : allPropertiesSchemas) {
108            addAllProperties(schema);
109        }
110
111        for (String property : properties) {
112            try {
113                addProperty(property, doc.getPropertyValue(property));
114            } catch (PropertyException e) {
115                String message = String.format("Property '%s' not found on document type: %s. Skipping it.", property,
116                        doc.getType());
117                log.debug(message);
118            }
119        }
120    }
121
122    protected void addAllProperties(String schema) {
123        DataModel dm = doc.getDataModel(schema);
124        if (dm != null) {
125            for (Map.Entry<String, Object> entry : dm.getMap().entrySet()) {
126                Object value = entry.getValue();
127                String propertyKey = entry.getKey();
128                addProperty(computePropertyKey(propertyKey, schema), value);
129            }
130        }
131    }
132
133    public void addProperty(String propertyKey, Object value) {
134        if (value instanceof String) {
135            metadataProperties.put(propertyKey, value);
136        } else if (value instanceof List) {
137            try {
138                List<String> list = (List<String>) value;
139                if (!list.isEmpty()) {
140                    if (list.size() == 1) {
141                        list = new ArrayList<>(list);
142                        list.add("");
143                    }
144                    metadataProperties.put(propertyKey, String.join(MetadataCollector.LIST_SEPARATOR, list));
145                }
146            } catch (ClassCastException e) {
147                // do nothing
148            }
149        } else if (value instanceof String[]) {
150            List<String> list = Arrays.asList((String[]) value);
151            if (!list.isEmpty()) {
152                if (list.size() == 1) {
153                    list = new ArrayList<>(list);
154                    list.add("");
155                }
156                metadataProperties.put(propertyKey, String.join(MetadataCollector.ARRAY_SEPARATOR, list));
157            }
158        } else if (value instanceof Calendar) {
159            metadataProperties.put(propertyKey, new DateType().encode(value));
160        } else if (value instanceof Number) {
161            metadataProperties.put(propertyKey, value.toString());
162        }
163    }
164
165    protected String computePropertyKey(String propertyKey, String schema) {
166        if (!propertyKey.contains(":")) {
167            propertyKey = schema + ":" + propertyKey;
168        }
169        return propertyKey;
170    }
171
172    protected void load() {
173        for (String schema : doc.getDeclaredSchemas()) {
174            addAllProperties(schema);
175        }
176    }
177
178    /**
179     * Write the properties file to the given {@code file}
180     */
181    public void writeTo(File file) {
182        FileOutputStream fos = null;
183        try {
184            fos = new FileOutputStream(file);
185            metadataProperties.store(fos, null);
186        } catch (IOException e) {
187            throw new NuxeoException("Unable to write the metadata properties to " + file.getAbsolutePath(), e);
188        } finally {
189            if (fos != null) {
190                try {
191                    fos.close();
192                } catch (IOException e) {
193                    // nothing to do...
194                }
195            }
196        }
197    }
198
199}