001/*
002 * (C) Copyright 2006-2011 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 *     bstefanescu
018 */
019package org.nuxeo.ecm.automation.core.util;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.Reader;
024import java.io.StringReader;
025import java.util.HashMap;
026import java.util.Iterator;
027import java.util.Map;
028import java.util.Map.Entry;
029
030import com.google.common.base.Objects;
031import org.codehaus.jackson.JsonNode;
032import org.codehaus.jackson.map.ObjectMapper;
033import org.nuxeo.ecm.automation.core.Constants;
034import org.nuxeo.runtime.api.Framework;
035
036/**
037 * Inline properties file content. This class exists to have a real type for parameters accepting properties content.
038 *
039 * @see Constants
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042public class Properties extends HashMap<String, String> {
043
044    private static final long serialVersionUID = 1L;
045
046    public static final String PROPERTIES_MULTILINE_ESCAPE = "nuxeo" + ".automation.properties.multiline.escape";
047
048    protected static final String multiLineEscape = Objects.firstNonNull(
049            Framework.getProperty(PROPERTIES_MULTILINE_ESCAPE), "true");
050
051    public Properties() {
052    }
053
054    public Properties(int size) {
055        super(size);
056    }
057
058    public Properties(Map<String, String> props) {
059        super(props);
060    }
061
062    public Properties(String content) throws IOException {
063        StringReader reader = new StringReader(content);
064        loadProperties(reader, this);
065    }
066
067    /**
068     * Constructs a Properties map based on a Json node.
069     *
070     * @param node
071     * @throws IOException
072     * @since 5.7.3
073     */
074    public Properties(JsonNode node) throws IOException {
075        Iterator<Entry<String, JsonNode>> fields = node.getFields();
076        ObjectMapper om = new ObjectMapper();
077        while (fields.hasNext()) {
078            Entry<String, JsonNode> entry = fields.next();
079            String key = entry.getKey();
080            JsonNode subNode = entry.getValue();
081            put(key, extractValueFromNode(subNode, om));
082        }
083    }
084
085    /**
086     * @param om
087     * @param subNode
088     * @return
089     * @throws IOException
090     * @since 5.8-HF01
091     */
092    private String extractValueFromNode(JsonNode node, ObjectMapper om) throws IOException {
093        if (!node.isNull()) {
094            return node.isContainerNode() ? om.writeValueAsString(node) : node.getValueAsText();
095        } else {
096            return null;
097        }
098    }
099
100    public static Map<String, String> loadProperties(Reader reader) throws IOException {
101        Map<String, String> map = new HashMap<String, String>();
102        loadProperties(reader, map);
103        return map;
104    }
105
106    public static void loadProperties(Reader reader, Map<String, String> map) throws IOException {
107        BufferedReader in = new BufferedReader(reader);
108        String line = in.readLine();
109        String prevLine = null;
110        while (line != null) {
111            line = line.trim();
112            if (line.startsWith("#") || line.length() == 0) {
113                prevLine = null;
114                line = in.readLine();
115                continue;
116            }
117            if (line.endsWith("\\") && Boolean.valueOf(multiLineEscape)) {
118                line = line.substring(0, line.length() - 1);
119                prevLine = prevLine != null ? prevLine + line : line;
120                line = in.readLine();
121                continue;
122            }
123            if (prevLine != null) {
124                line = prevLine + "\n" + line;
125            }
126            prevLine = null;
127            setPropertyLine(map, line);
128            line = in.readLine();
129        }
130        if (prevLine != null) {
131            setPropertyLine(map, prevLine);
132        }
133    }
134
135    protected static void setPropertyLine(Map<String, String> map, String line) throws IOException {
136        int i = line.indexOf('=');
137        if (i == -1) {
138            throw new IOException("Invalid property line: " + line);
139        }
140        map.put(line.substring(0, i).trim(), line.substring(i + 1).trim());
141    }
142
143}