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