001/*
002 * (C) Copyright 2006-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 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021package org.nuxeo.ecm.platform.scanimporter.service;
022
023import java.io.File;
024import java.io.IOException;
025import java.io.Serializable;
026import java.nio.charset.StandardCharsets;
027import java.text.ParseException;
028import java.util.ArrayList;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033import org.apache.commons.io.FileUtils;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.dom4j.Document;
037import org.dom4j.DocumentException;
038import org.dom4j.DocumentHelper;
039import org.dom4j.tree.DefaultElement;
040import org.jaxen.JaxenException;
041import org.jaxen.XPath;
042import org.jaxen.dom4j.Dom4jXPath;
043import org.nuxeo.ecm.core.api.Blob;
044import org.nuxeo.ecm.core.api.Blobs;
045import org.nuxeo.ecm.platform.scanimporter.processor.DocumentTypeMapper;
046import org.nuxeo.runtime.api.Framework;
047import org.nuxeo.runtime.model.ComponentInstance;
048import org.nuxeo.runtime.model.DefaultComponent;
049
050/**
051 * Component to provide service logic : - meta-data parsing - configuration management - extension points
052 *
053 * @author Thierry Delprat
054 */
055public class ScannedFileMapperComponent extends DefaultComponent implements ScannedFileMapperService {
056
057    private static final Log log = LogFactory.getLog(ScannedFileMapperComponent.class);
058
059    public static final String MAPPING_EP = "mapping";
060
061    public static final String CONFIG_EP = "config";
062
063    protected ScanFileMappingDescriptor mappingDesc = null;
064
065    protected ImporterConfig config = null;
066
067    @Override
068    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
069
070        if (MAPPING_EP.equals(extensionPoint)) {
071            mappingDesc = (ScanFileMappingDescriptor) contribution;
072        } else if (CONFIG_EP.equals(extensionPoint)) {
073            config = (ImporterConfig) contribution;
074        }
075    }
076
077    @Override
078    public ScanFileBlobHolder parseMetaData(File xmlFile) throws IOException {
079
080        Map<String, Serializable> data = new HashMap<>();
081
082        if (mappingDesc == null) {
083            return null;
084        }
085
086        String xmlData = FileUtils.readFileToString(xmlFile, StandardCharsets.UTF_8);
087
088        Document xmlDoc;
089        try {
090            xmlDoc = DocumentHelper.parseText(xmlData);
091        } catch (DocumentException e) {
092            throw new IOException(e);
093        }
094
095        for (ScanFileFieldMapping fieldMap : mappingDesc.getFieldMappings()) {
096
097            List<?> nodes;
098            try {
099                XPath xpath = new Dom4jXPath(fieldMap.getSourceXPath());
100                nodes = xpath.selectNodes(xmlDoc);
101            } catch (JaxenException e) {
102                throw new IOException(e);
103            }
104            if (nodes.size() == 1) {
105                DefaultElement elem = (DefaultElement) nodes.get(0);
106                String value;
107                if ("TEXT".equals(fieldMap.getSourceAttribute())) {
108                    value = elem.getText();
109                } else {
110                    value = elem.attribute(fieldMap.getSourceAttribute()).getValue();
111                }
112
113                String target = fieldMap.getTargetXPath();
114                if ("string".equalsIgnoreCase(fieldMap.getTargetType())) {
115                    data.put(target, value);
116                    continue;
117                } else if ("integer".equalsIgnoreCase(fieldMap.getTargetType())) {
118                    data.put(target, Integer.valueOf(value));
119                    continue;
120                } else if ("double".equalsIgnoreCase(fieldMap.getTargetType())) {
121                    data.put(target, Double.valueOf(value));
122                    continue;
123                } else if ("date".equalsIgnoreCase(fieldMap.getTargetType())) {
124                    try {
125                        data.put(target, fieldMap.getDateFormat().parse(value));
126                    } catch (ParseException e) {
127                        throw new IOException(e);
128                    }
129                    continue;
130                } else if ("boolean".equalsIgnoreCase(fieldMap.getTargetType())) {
131                    data.put(target, Boolean.valueOf(value));
132                    continue;
133                }
134                log.error("Unknown target type, please look the scan importer configuration: "
135                        + fieldMap.getTargetType());
136            }
137            log.error("Mulliple or no element(s) found for: " + fieldMap.sourceXPath + " for "
138                    + xmlFile.getAbsolutePath());
139
140        }
141
142        List<Blob> blobs = new ArrayList<>();
143
144        for (ScanFileBlobMapping blobMap : mappingDesc.getBlobMappings()) {
145            List<?> nodes;
146            try {
147                XPath xpath = new Dom4jXPath(blobMap.getSourceXPath());
148                nodes = xpath.selectNodes(xmlDoc);
149            } catch (JaxenException e) {
150                throw new IOException(e);
151            }
152            for (Object node : nodes) {
153                DefaultElement elem = (DefaultElement) node;
154                String filePath = elem.attributeValue(blobMap.getSourcePathAttribute());
155                String fileName = elem.attributeValue(blobMap.getSourceFilenameAttribute());
156
157                // Mainly for tests
158                if (filePath.startsWith("$TMP")) {
159                    filePath = filePath.replace("$TMP", Framework.getProperty("nuxeo.import.tmpdir"));
160                }
161
162                File file = new File(filePath);
163                if (file.exists()) {
164                    Blob blob = Blobs.createBlob(file);
165                    if (fileName != null) {
166                        blob.setFilename(fileName);
167                    } else {
168                        blob.setFilename(file.getName());
169                    }
170                    String target = blobMap.getTargetXPath();
171                    if (target == null) {
172                        blobs.add(blob);
173                    } else {
174                        data.put(target, (Serializable) blob);
175                    }
176                } else {
177                    log.error("File " + file.getAbsolutePath() + " is referenced by " + xmlFile.getAbsolutePath()
178                            + " but was not found");
179                }
180            }
181        }
182
183        String targetType = getTargetLeafType();
184        DocumentTypeMapper mapper = mappingDesc.getTargetLeafTypeMapper();
185        if (mapper != null) {
186            targetType = mapper.getTargetDocumentType(xmlDoc, xmlFile);
187        }
188        return new ScanFileBlobHolder(blobs, data, targetType);
189    }
190
191    public ScanFileMappingDescriptor getMappingDesc() {
192        return mappingDesc;
193    }
194
195    @Override
196    public String getTargetContainerType() {
197        if (mappingDesc == null) {
198            return ScanFileMappingDescriptor.DEFAULT_CONTAINER_TYPE;
199        }
200        return mappingDesc.getTargetContainerType();
201    }
202
203    @Override
204    public String getTargetLeafType() {
205        if (mappingDesc == null) {
206            return ScanFileMappingDescriptor.DEFAULT_LEAF_TYPE;
207        }
208        return mappingDesc.getTargetLeafType();
209    }
210
211    @Override
212    public ImporterConfig getImporterConfig() {
213        return config;
214    }
215}