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