001/*
002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Florent Guillaume
016 */
017package org.nuxeo.ecm.platform.routing.core.impl;
018
019import java.io.ByteArrayOutputStream;
020import java.io.IOException;
021import java.io.Serializable;
022import java.util.Calendar;
023import java.util.HashMap;
024import java.util.LinkedHashMap;
025import java.util.Map;
026
027import org.apache.commons.lang.StringUtils;
028import org.codehaus.jackson.JsonFactory;
029import org.codehaus.jackson.JsonGenerator;
030import org.nuxeo.ecm.automation.core.util.DocumentHelper;
031import org.nuxeo.ecm.automation.core.util.Properties;
032import org.nuxeo.ecm.core.api.CoreSession;
033import org.nuxeo.ecm.core.api.DocumentModel;
034import org.nuxeo.ecm.core.api.NuxeoException;
035import org.nuxeo.ecm.core.api.model.PropertyNotFoundException;
036import org.nuxeo.ecm.core.schema.SchemaManager;
037import org.nuxeo.ecm.core.schema.types.CompositeType;
038import org.nuxeo.ecm.core.schema.types.Field;
039import org.nuxeo.ecm.core.schema.utils.DateParser;
040import org.nuxeo.ecm.platform.routing.api.DocumentRoutingConstants;
041import org.nuxeo.ecm.webengine.JsonFactoryManager;
042import org.nuxeo.runtime.api.Framework;
043
044/**
045 * Helper to set/get variables on a document that are stored in a facet.
046 *
047 * @since 5.6
048 */
049public class GraphVariablesUtil {
050
051    private GraphVariablesUtil() {
052    }
053
054    protected static SchemaManager getSchemaManager() {
055        return Framework.getLocalService(SchemaManager.class);
056    }
057
058    private static JsonFactory getFactory() {
059        JsonFactoryManager jsonFactoryManager = Framework.getLocalService(JsonFactoryManager.class);
060        return jsonFactoryManager.getJsonFactory();
061    }
062
063    /**
064     * @since 7.2
065     */
066    public static Map<String, Serializable> getVariables(DocumentModel doc, String facetProp, boolean mapToJSON) {
067        String facet = (String) doc.getPropertyValue(facetProp);
068        Map<String, Serializable> map = new LinkedHashMap<String, Serializable>();
069        if (StringUtils.isBlank(facet)) {
070            return map;
071        }
072        CompositeType type = getSchemaManager().getFacet(facet);
073        if (type == null) {
074            return map;
075        }
076        boolean hasFacet = doc.hasFacet(facet);
077        for (Field f : type.getFields()) {
078            String name = f.getName().getLocalName();
079            Serializable value = hasFacet ? doc.getPropertyValue(name) : null;
080            if (value instanceof Calendar) {
081                if (mapToJSON) {
082                    value = DateParser.formatW3CDateTime(((Calendar) value).getTime());
083                } else {
084                    value = ((Calendar) value).getTime();
085                }
086            } else if (value instanceof Object[] && mapToJSON) {
087                try {
088                    Object[] objects = (Object[]) value;
089                    ByteArrayOutputStream out = new ByteArrayOutputStream();
090                    JsonGenerator jg = getFactory().createJsonGenerator(out);
091                    jg.writeStartArray();
092                    for (Object object : objects) {
093                        jg.writeString(type.encode(object));
094                    }
095                    jg.writeEndArray();
096                    jg.flush();
097                    jg.close();
098                    value = out.toString("UTF-8");
099                } catch (IOException e) {
100                    throw new NuxeoException(e);
101                }
102            }
103            if (mapToJSON) {
104                map.put(name, value != null ? value.toString() : null);
105            } else {
106                map.put(name, value);
107            }
108        }
109        return map;
110    }
111
112    public static Map<String, Serializable> getVariables(DocumentModel doc, String facetProp) {
113        return getVariables(doc, facetProp, false);
114    }
115
116    public static void setVariables(DocumentModel doc, String facetProp, Map<String, Serializable> map) {
117        setVariables(doc, facetProp, map, true);
118    }
119
120    /**
121     * @since 7.2
122     */
123    public static void setVariables(DocumentModel doc, String facetProp, Map<String, Serializable> map, final boolean save) {
124        if (map.containsKey(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON)
125                && (Boolean) map.get(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON)) {
126            Map<String, String> vars = new HashMap<String, String>();
127            map.remove(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON);
128            for (String key : map.keySet()) {
129                if (map.get(key) != null && !(map.get(key) instanceof String)) {
130                    throw new NuxeoException(
131                            "Trying to decode JSON variables: The parameter 'map' should contain only Strings as it contains the marker '_MAP_VAR_FORMAT_JSON' ");
132                }
133                vars.put(key, (String) map.get(key));
134            }
135            GraphVariablesUtil.setJSONVariables(doc, facetProp, vars, save);
136        } else {
137            String facet = null;
138            try {
139                facet = (String) doc.getPropertyValue(facetProp);
140            } catch (PropertyNotFoundException e) {
141                facet = facetProp;
142            }
143            if (StringUtils.isBlank(facet)) {
144                return;
145            }
146            CompositeType type = getSchemaManager().getFacet(facet);
147            if (type == null) {
148                return;
149            }
150            boolean hasFacet = doc.hasFacet(facet);
151            for (Field f : type.getFields()) {
152                String name = f.getName().getLocalName();
153                if (!map.containsKey(name)) {
154                    continue;
155                }
156                Serializable value = map.get(name);
157                if (value == null && !hasFacet) {
158                    continue;
159                }
160                if (!hasFacet) {
161                    doc.addFacet(facet);
162                    hasFacet = true;
163                }
164                doc.setPropertyValue(name, value);
165            }
166            if (save) {
167                CoreSession session = doc.getCoreSession();
168                session.saveDocument(doc);
169            }
170        }
171    }
172
173    /**
174     * Sets the variables of the workflow based on their JSON representation (especially for scalar lists).
175     *
176     * @param doc
177     * @param facetProp
178     * @param map
179     * @param save
180     * @since 5.9.3, 5.8.0-HF10
181     */
182    public static void setJSONVariables(DocumentModel doc, String facetProp, Map<String, String> map) {
183        setJSONVariables(doc, facetProp, map, true);
184    }
185
186
187    /**
188     * @since 7.2
189     */
190    public static void setJSONVariables(DocumentModel doc, String facetProp, Map<String, String> map, final boolean save) {
191        // normally the variables in the map don't contain the schema prefix
192        Properties jsonProperties = new Properties();
193        String facet = null;
194        try {
195            facet = (String) doc.getPropertyValue(facetProp);
196        } catch (PropertyNotFoundException e) {
197            facet = facetProp;
198        }
199        if (StringUtils.isBlank(facet)) {
200            return;
201        }
202        CompositeType type = getSchemaManager().getFacet(facet);
203        if (type == null) {
204        }
205        boolean hasFacet = doc.hasFacet(facet);
206        for (Field f : type.getFields()) {
207            String name = f.getName().getLocalName();
208            if (!map.containsKey(name)) {
209                continue;
210            }
211            String value = map.get(name);
212            if (value == null && !hasFacet) {
213                continue;
214            }
215            if (!hasFacet) {
216                doc.addFacet(facet);
217                hasFacet = true;
218            }
219            jsonProperties.put(name, value);
220        }
221        CoreSession session = doc.getCoreSession();
222        try {
223            DocumentHelper.setJSONProperties(session, doc, jsonProperties);
224        } catch (IOException e) {
225            throw new NuxeoException(e);
226        }
227        if (save) {
228            session.saveDocument(doc);
229        }
230    }
231
232}