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