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