001/*
002 * (C) Copyright 2006-2018 Nuxeo (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 */
021
022package org.nuxeo.ecm.platform.importer.properties;
023
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.IOException;
027import java.io.Serializable;
028import java.util.ArrayList;
029import java.util.Collections;
030import java.util.Enumeration;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034import java.util.Map.Entry;
035import java.util.Properties;
036import java.util.concurrent.locks.ReentrantReadWriteLock;
037import java.util.regex.Pattern;
038
039import org.apache.commons.io.FilenameUtils;
040import org.apache.commons.lang3.StringUtils;
041
042/**
043 * Collects meta-data from a FileSystem and manage inheritence
044 *
045 * @author Thierry Delprat
046 */
047public class MetadataCollector {
048
049    protected boolean staticInherit = true;
050
051    protected boolean useIntrospection = false;
052
053    public static final String DATE_FORMAT = "MM/dd/yyyy";
054
055    public static final String LIST_SEPARATOR = "|";
056
057    public static final String REGEXP_LIST_SEPARATOR = "\\|";
058
059    public static final String ARRAY_SEPARATOR = "||";
060
061    public static final String REGEXP_ARRAY_SEPARATOR = "\\|\\|";
062
063    protected Map<String, Map<String, Serializable>> collectedMetadata = new HashMap<>();
064
065    protected ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
066
067    public void addPropertiesFromStrings(String contextPath, Map<String, String> properties) {
068        Map<String, Serializable> collectedProperties = new HashMap<>();
069        for (Entry<String, String> es : properties.entrySet()) {
070            String name = es.getKey();
071            Serializable value = parseFromString(name, es.getValue());
072            collectedProperties.put(name, value);
073        }
074        addProperties(contextPath, collectedProperties);
075    }
076
077    public void addProperties(String contextPath, Map<String, Serializable> collectedProperties) {
078        try {
079            lock.writeLock().lock();
080            contextPath = FilenameUtils.normalizeNoEndSeparator(contextPath);
081            if (staticInherit) {
082                File file = new File(contextPath);
083                while (!StringUtils.isEmpty(file.getParent())) {
084                    file = file.getParentFile();
085                    Map<String, Serializable> parentProperties = collectedMetadata.get(file.toString());
086                    if (parentProperties != null) {
087                        for (String name : parentProperties.keySet()) {
088                            if (!collectedProperties.containsKey(name)) {
089                                collectedProperties.put(name, parentProperties.get(name));
090                            }
091                        }
092                    }
093                }
094            }
095            collectedMetadata.put(contextPath, collectedProperties);
096        } finally {
097            lock.writeLock().unlock();
098        }
099
100    }
101
102    protected static Pattern numPattern = Pattern.compile("([0-9\\,\\.\\+\\-]+)");
103
104    protected Serializable parseFromString(String name, String value) {
105
106        Serializable prop = value;
107        if (useIntrospection) {
108            throw new UnsupportedOperationException("Introspection mode not available");
109        } else {
110            if (value.contains(ARRAY_SEPARATOR)) {
111                prop = value.split(REGEXP_ARRAY_SEPARATOR);
112            } else if (value.contains(LIST_SEPARATOR)) {
113                List<Serializable> lstprop = new ArrayList<>();
114                String[] parts = value.split(REGEXP_LIST_SEPARATOR);
115                for (String part : parts) {
116                    lstprop.add(parseFromString(name, part));
117                }
118                prop = (Serializable) lstprop;
119            }
120        }
121        return prop;
122    }
123
124    public Serializable getProperty(String contextPath, String name) {
125
126        Map<String, Serializable> props = getProperties(contextPath);
127        if (props != null) {
128            try {
129                lock.readLock().lock();
130                return props.get(name);
131            } finally {
132                lock.readLock().unlock();
133            }
134        } else {
135            return null;
136        }
137    }
138
139    public Map<String, Serializable> getProperties(String contextPath) {
140
141        contextPath = FilenameUtils.normalizeNoEndSeparator(contextPath);
142
143        try {
144            lock.readLock().lock();
145            Map<String, Serializable> props = collectedMetadata.get(contextPath);
146
147            if (props == null) {
148                File file = new File(contextPath);
149                while (props == null && !StringUtils.isEmpty(file.getParent())) {
150                    file = file.getParentFile();
151                    props = collectedMetadata.get(file.getPath());
152                }
153            }
154
155            if (props != null) {
156                props = Collections.unmodifiableMap(props);
157            }
158            return props;
159        } finally {
160            lock.readLock().unlock();
161        }
162    }
163
164    public void addPropertyFile(File propertyFile) throws IOException {
165
166        Properties mdProperties = new Properties();
167        try (var in = new FileInputStream(propertyFile)) {
168            mdProperties.load(in);
169        }
170
171        Map<String, String> stringMap = new HashMap<>();
172        Enumeration<?> names = mdProperties.propertyNames();
173        while (names.hasMoreElements()) {
174            String name = (String) names.nextElement();
175            stringMap.put(name, mdProperties.getProperty(name));
176        }
177        String contextPath = new File(propertyFile.getPath()).getParent();
178        addPropertiesFromStrings(contextPath, stringMap);
179    }
180
181}