001/*
002 * (C) Copyright 2015 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-2.1.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 *     Nicolas Chapurlat <nchapurlat@nuxeo.com>
016 */
017
018/*
019 import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_PROPERTIES;
020 import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.HEADER_PREFIX;
021
022 import java.util.Arrays;
023 import java.util.Collections;
024 import java.util.List;
025 import java.util.Locale;
026 import java.util.Map;
027 import java.util.Set;
028 import java.util.TreeSet;
029 import java.util.concurrent.ConcurrentHashMap;
030 import java.util.concurrent.CopyOnWriteArrayList;
031
032 import org.nuxeo.common.utils.StringUtils;
033 import org.nuxeo.ecm.core.io.registry.MarshallingConstants;
034 */
035
036package org.nuxeo.ecm.core.io.registry.context;
037
038import static org.nuxeo.ecm.core.io.marshallers.json.document.DocumentModelJsonWriter.ENTITY_TYPE;
039import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_ENRICHERS;
040import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.EMBED_PROPERTIES;
041import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.FETCH_PROPERTIES;
042import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.HEADER_PREFIX;
043import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.TRANSLATE_PROPERTIES;
044import static org.nuxeo.ecm.core.io.registry.MarshallingConstants.WRAPPED_CONTEXT;
045
046import java.util.ArrayList;
047import java.util.Arrays;
048import java.util.Collections;
049import java.util.HashMap;
050import java.util.List;
051import java.util.Locale;
052import java.util.Map;
053import java.util.Set;
054import java.util.TreeSet;
055import java.util.concurrent.ConcurrentHashMap;
056import java.util.concurrent.CopyOnWriteArrayList;
057
058import org.apache.commons.lang.StringUtils;
059import org.nuxeo.ecm.core.api.CoreInstance;
060import org.nuxeo.ecm.core.api.CoreSession;
061import org.nuxeo.ecm.core.api.DocumentModel;
062import org.nuxeo.ecm.core.io.registry.MarshallingConstants;
063import org.nuxeo.ecm.core.io.registry.MarshallingException;
064
065/**
066 * A thread-safe {@link RenderingContext} implementation. Please use {@link RenderingContext.CtxBuilder} to create
067 * instance of {@link RenderingContext}.
068 *
069 * @since 7.2
070 */
071public class RenderingContextImpl implements RenderingContext {
072
073    private String baseUrl = DEFAULT_URL;
074
075    private Locale locale = DEFAULT_LOCALE;
076
077    private CoreSession session = null;
078
079    private final Map<String, List<Object>> parameters = new ConcurrentHashMap<String, List<Object>>();
080
081    private RenderingContextImpl() {
082    }
083
084    @Override
085    public Locale getLocale() {
086        return locale;
087    }
088
089    @Override
090    public String getBaseUrl() {
091        return baseUrl;
092    }
093
094    @Override
095    public SessionWrapper getSession(DocumentModel document) {
096        if (document != null) {
097            CoreSession docSession = null;
098            try {
099                docSession = document.getCoreSession();
100            } catch (UnsupportedOperationException e) {
101                // do nothing
102            }
103            if (docSession != null) {
104                return new SessionWrapper(docSession, false);
105            }
106        }
107        if (session != null) {
108            return new SessionWrapper(session, false);
109        }
110        String repoNameFound = getParameter("X-NXRepository");
111        if (StringUtils.isBlank(repoNameFound)) {
112            repoNameFound = getParameter("nxrepository");
113            if (StringUtils.isBlank(repoNameFound)) {
114                try {
115                    repoNameFound = document.getRepositoryName();
116                } catch (UnsupportedOperationException e) {
117                    // do nothing
118                }
119            }
120        }
121        if (!StringUtils.isBlank(repoNameFound)) {
122            CoreSession session = CoreInstance.openCoreSession(repoNameFound);
123            return new SessionWrapper(session, true);
124        }
125        throw new MarshallingException("Unable to create a new session");
126    }
127
128    @Override
129    public void setExistingSession(CoreSession session) {
130        this.session = session;
131    }
132
133    @Override
134    public Set<String> getProperties() {
135        return getSplittedParameterValues(EMBED_PROPERTIES);
136    }
137
138    @Override
139    public Set<String> getFetched(String entity) {
140        return getSplittedParameterValues(FETCH_PROPERTIES, entity);
141    }
142
143    @Override
144    public Set<String> getTranslated(String entity) {
145        return getSplittedParameterValues(TRANSLATE_PROPERTIES, entity);
146    }
147
148    @Override
149    public Set<String> getEnrichers(String entity) {
150        return getSplittedParameterValues(EMBED_ENRICHERS, entity);
151    }
152
153    @SuppressWarnings("deprecation")
154    private Set<String> getSplittedParameterValues(String category, String... subCategories) {
155        if (category == null) {
156            return Collections.emptySet();
157        }
158        String paramKey = category;
159        for (String subCategory : subCategories) {
160            paramKey += "." + subCategory;
161        }
162        paramKey = paramKey.toLowerCase();
163        List<Object> dirty = getParameters(paramKey);
164        dirty.addAll(getParameters(HEADER_PREFIX + paramKey));
165        // backward compatibility, supports X-NXDocumentProperties and X-NXContext-Category
166        if (EMBED_PROPERTIES.toLowerCase().equals(paramKey)) {
167            dirty.addAll(getParameters(MarshallingConstants.DOCUMENT_PROPERTIES_HEADER));
168        } else if ((EMBED_ENRICHERS + "." + ENTITY_TYPE).toLowerCase().equals(paramKey)) {
169            dirty.addAll(getParameters(MarshallingConstants.NXCONTENT_CATEGORY_HEADER));
170        }
171        Set<String> result = new TreeSet<String>();
172        for (Object value : dirty) {
173            if (value instanceof String && value != null) {
174                for (String cleaned : org.nuxeo.common.utils.StringUtils.split((String) value, ',', true)) {
175                    result.add(cleaned);
176                }
177            }
178        }
179        return result;
180    }
181
182    private <T> T getWrappedEntity(String name) {
183        return WrappedContext.getEntity(this, name);
184    }
185
186    @Override
187    public WrappedContext wrap() {
188        return WrappedContext.create(this);
189    }
190
191    @Override
192    public <T> T getParameter(String name) {
193        if (StringUtils.isEmpty(name)) {
194            return null;
195        }
196        String realName = name.toLowerCase().trim();
197        List<Object> values = parameters.get(realName);
198        if (values != null && values.size() > 0) {
199            @SuppressWarnings("unchecked")
200            T value = (T) values.get(0);
201            return value;
202        }
203        if (WRAPPED_CONTEXT.toLowerCase().equals(realName)) {
204            return null;
205        } else {
206            return getWrappedEntity(realName);
207        }
208    }
209
210    @Override
211    public boolean getBooleanParameter(String name) {
212        Object result = getParameter(name);
213        if (result == null) {
214            return false;
215        } else if (result instanceof Boolean) {
216            return (Boolean) result;
217        } else if (result instanceof String) {
218            try {
219                return Boolean.valueOf((String) result);
220            } catch (Exception e) {
221                return false;
222            }
223        }
224        return false;
225    }
226
227    @Override
228    public <T> List<T> getParameters(String name) {
229        if (StringUtils.isEmpty(name)) {
230            return null;
231        }
232        String realName = name.toLowerCase().trim();
233        @SuppressWarnings("unchecked")
234        List<T> values = (List<T>) parameters.get(realName);
235        if (values != null) {
236            return new ArrayList<T>(values);
237        } else {
238            return new ArrayList<T>();
239        }
240    }
241
242    @Override
243    public Map<String, List<Object>> getAllParameters() {
244        // make a copy of the local parameters
245        Map<String, List<Object>> unModifiableParameters = new HashMap<String, List<Object>>();
246        for (Map.Entry<String, List<Object>> entry : parameters.entrySet()) {
247            String key = entry.getKey();
248            List<Object> value = entry.getValue();
249            if (value == null) {
250                unModifiableParameters.put(key, null);
251            } else {
252                unModifiableParameters.put(key, new ArrayList<Object>(value));
253            }
254        }
255        return unModifiableParameters;
256    }
257
258    @Override
259    public void setParameterValues(String name, Object... values) {
260        if (StringUtils.isEmpty(name)) {
261            return;
262        }
263        String realName = name.toLowerCase().trim();
264        if (values.length == 0) {
265            parameters.remove(realName);
266            return;
267        }
268        setParameterListValues(realName, Arrays.asList(values));
269    }
270
271    @Override
272    public void setParameterListValues(String name, List<Object> values) {
273        if (StringUtils.isEmpty(name)) {
274            return;
275        }
276        String realName = name.toLowerCase().trim();
277        if (values == null) {
278            parameters.remove(realName);
279        }
280        parameters.put(realName, new CopyOnWriteArrayList<Object>(values));
281    }
282
283    @Override
284    public void addParameterValues(String name, Object... values) {
285        addParameterListValues(name, Arrays.asList(values));
286    }
287
288    @Override
289    public void addParameterListValues(String name, List<Object> values) {
290        if (StringUtils.isEmpty(name)) {
291            return;
292        }
293        String realName = name.toLowerCase().trim();
294        if (values == null) {
295            return;
296        }
297        List<Object> currentValues = parameters.get(realName);
298        if (currentValues == null) {
299            currentValues = new CopyOnWriteArrayList<Object>();
300            parameters.put(realName, currentValues);
301        }
302        for (Object value : values) {
303            currentValues.add(value);
304        }
305    }
306
307    static RenderingContextBuilder builder() {
308        return new RenderingContextBuilder();
309    }
310
311    public static final class RenderingContextBuilder {
312
313        private RenderingContextImpl ctx;
314
315        RenderingContextBuilder() {
316            ctx = new RenderingContextImpl();
317        }
318
319        public RenderingContextBuilder base(String url) {
320            ctx.baseUrl = url;
321            return this;
322        }
323
324        public RenderingContextBuilder locale(Locale locale) {
325            ctx.locale = locale;
326            return this;
327        }
328
329        public RenderingContextBuilder session(CoreSession session) {
330            ctx.session = session;
331            return this;
332        }
333
334        public RenderingContextBuilder param(String name, Object value) {
335            ctx.addParameterValues(name, value);
336            return this;
337        }
338
339        public RenderingContextBuilder paramValues(String name, Object... values) {
340            ctx.addParameterValues(name, values);
341            return this;
342        }
343
344        public RenderingContextBuilder paramList(String name, List<Object> values) {
345            ctx.addParameterListValues(name, values);
346            return this;
347        }
348
349        public RenderingContextBuilder properties(String... schemaName) {
350            return paramValues(EMBED_PROPERTIES, (Object[]) schemaName);
351        }
352
353        public RenderingContextBuilder fetch(String entityType, String... propertyName) {
354            return paramValues(FETCH_PROPERTIES + "." + entityType, (Object[]) propertyName);
355        }
356
357        public RenderingContextBuilder translate(String entityType, String... propertyName) {
358            return paramValues(TRANSLATE_PROPERTIES + "." + entityType, (Object[]) propertyName);
359        }
360
361        public RenderingContextBuilder fetchInDoc(String... propertyName) {
362            return paramValues(FETCH_PROPERTIES + "." + ENTITY_TYPE, (Object[]) propertyName);
363        }
364
365        public RenderingContextBuilder enrichDoc(String... enricherName) {
366            return paramValues(EMBED_ENRICHERS + "." + ENTITY_TYPE, (Object[]) enricherName);
367        }
368
369        public RenderingContextBuilder enrich(String entityType, String... enricherName) {
370            return paramValues(EMBED_ENRICHERS + "." + ENTITY_TYPE, (Object[]) enricherName);
371        }
372
373        public RenderingContextBuilder depth(DepthValues value) {
374            ctx.setParameterValues(MarshallingConstants.MAX_DEPTH_PARAM, value.name());
375            return this;
376        }
377
378        public RenderingContext get() {
379            return ctx;
380        }
381
382    }
383
384}