001/*
002 * (C) Copyright 2012-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 *     Florent Guillaume
018 */
019package org.nuxeo.ecm.platform.routing.core.impl;
020
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.Serializable;
024import java.util.Calendar;
025import java.util.HashMap;
026import java.util.LinkedHashMap;
027import java.util.Map;
028
029import org.apache.commons.lang3.StringUtils;
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
044import com.fasterxml.jackson.core.JsonFactory;
045import com.fasterxml.jackson.core.JsonGenerator;
046
047/**
048 * Helper to set/get variables on a document that are stored in a facet.
049 *
050 * @since 5.6
051 */
052public class GraphVariablesUtil {
053
054    private GraphVariablesUtil() {
055    }
056
057    protected static SchemaManager getSchemaManager() {
058        return Framework.getService(SchemaManager.class);
059    }
060
061    private static JsonFactory getFactory() {
062        JsonFactoryManager jsonFactoryManager = Framework.getService(JsonFactoryManager.class);
063        return jsonFactoryManager.getJsonFactory();
064    }
065
066    /**
067     * @since 7.2
068     */
069    public static Map<String, Serializable> getVariables(DocumentModel doc, String facetProp, boolean mapToJSON) {
070        String facet = (String) doc.getPropertyValue(facetProp);
071        Map<String, Serializable> map = new LinkedHashMap<>();
072        if (StringUtils.isBlank(facet)) {
073            return map;
074        }
075        CompositeType type = getSchemaManager().getFacet(facet);
076        if (type == null) {
077            return map;
078        }
079        boolean hasFacet = doc.hasFacet(facet);
080        for (Field f : type.getFields()) {
081            String name = f.getName().getLocalName();
082            Serializable value = hasFacet ? doc.getPropertyValue(name) : null;
083            if (value instanceof Calendar) {
084                if (mapToJSON) {
085                    value = DateParser.formatW3CDateTime(((Calendar) value).getTime());
086                } else {
087                    value = ((Calendar) value).getTime();
088                }
089            } else if (value instanceof Object[] && mapToJSON) {
090                try {
091                    Object[] objects = (Object[]) value;
092                    ByteArrayOutputStream out = new ByteArrayOutputStream();
093                    try (JsonGenerator jg = getFactory().createGenerator(out)) {
094                        jg.writeStartArray();
095                        for (Object object : objects) {
096                            jg.writeString(type.encode(object));
097                        }
098                        jg.writeEndArray();
099                    }
100                    value = out.toString("UTF-8");
101                } catch (IOException e) {
102                    throw new NuxeoException(e);
103                }
104            }
105            if (mapToJSON) {
106                map.put(name, value != null ? value.toString() : null);
107            } else {
108                map.put(name, value);
109            }
110        }
111        return map;
112    }
113
114    public static Map<String, Serializable> getVariables(DocumentModel doc, String facetProp) {
115        return getVariables(doc, facetProp, false);
116    }
117
118    public static void setVariables(DocumentModel doc, String facetProp, Map<String, Serializable> map) {
119        setVariables(doc, facetProp, map, true);
120    }
121
122    /**
123     * @since 7.2
124     */
125    public static void setVariables(DocumentModel doc, String facetProp, Map<String, Serializable> map,
126            final boolean save) {
127        if (map.containsKey(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON)
128                && (Boolean) map.get(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON)) {
129            Map<String, String> vars = new HashMap<>();
130            map.remove(DocumentRoutingConstants._MAP_VAR_FORMAT_JSON);
131            for (String key : map.keySet()) {
132                if (map.get(key) != null && !(map.get(key) instanceof String)) {
133                    throw new NuxeoException(
134                            "Trying to decode JSON variables: The parameter 'map' should contain only Strings as it contains the marker '_MAP_VAR_FORMAT_JSON' ");
135                }
136                vars.put(key, (String) map.get(key));
137            }
138            GraphVariablesUtil.setJSONVariables(doc, facetProp, vars, save);
139        } else {
140            String facet;
141            try {
142                facet = (String) doc.getPropertyValue(facetProp);
143            } catch (PropertyNotFoundException e) {
144                facet = facetProp;
145            }
146            if (StringUtils.isBlank(facet)) {
147                return;
148            }
149            CompositeType type = getSchemaManager().getFacet(facet);
150            if (type == null) {
151                return;
152            }
153            boolean hasFacet = doc.hasFacet(facet);
154            for (Field f : type.getFields()) {
155                String name = f.getName().getLocalName();
156                if (!map.containsKey(name)) {
157                    continue;
158                }
159                Serializable value = map.get(name);
160                if (value == null && !hasFacet) {
161                    continue;
162                }
163                if (!hasFacet) {
164                    doc.addFacet(facet);
165                    hasFacet = true;
166                }
167                doc.setPropertyValue(name, value);
168            }
169            if (save) {
170                CoreSession session = doc.getCoreSession();
171                session.saveDocument(doc);
172            }
173        }
174    }
175
176    /**
177     * Sets the variables of the workflow based on their JSON representation (especially for scalar lists).
178     *
179     * @since 5.9.3, 5.8.0-HF10
180     */
181    public static void setJSONVariables(DocumentModel doc, String facetProp, Map<String, String> map) {
182        setJSONVariables(doc, facetProp, map, true);
183    }
184
185    /**
186     * @since 7.2
187     */
188    public static void setJSONVariables(DocumentModel doc, String facetProp, Map<String, String> map,
189            final boolean save) {
190        // normally the variables in the map don't contain the schema prefix
191        Properties jsonProperties = new Properties();
192        String facet;
193        try {
194            facet = (String) doc.getPropertyValue(facetProp);
195        } catch (PropertyNotFoundException e) {
196            facet = facetProp;
197        }
198        if (StringUtils.isBlank(facet)) {
199            return;
200        }
201        CompositeType type = getSchemaManager().getFacet(facet);
202        boolean hasFacet = doc.hasFacet(facet);
203        for (Field f : type.getFields()) {
204            String name = f.getName().getLocalName();
205            if (!map.containsKey(name)) {
206                continue;
207            }
208            String value = map.get(name);
209            if (value == null && !hasFacet) {
210                continue;
211            }
212            if (!hasFacet) {
213                doc.addFacet(facet);
214                hasFacet = true;
215            }
216            jsonProperties.put(name, value);
217        }
218        CoreSession session = doc.getCoreSession();
219        try {
220            DocumentHelper.setJSONProperties(session, doc, jsonProperties);
221        } catch (IOException e) {
222            throw new NuxeoException(e);
223        }
224        if (save) {
225            session.saveDocument(doc);
226        }
227    }
228
229}