001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.dynamicdatamapping.util;
016    
017    import com.liferay.portal.kernel.freemarker.FreeMarkerContext;
018    import com.liferay.portal.kernel.freemarker.FreeMarkerEngineUtil;
019    import com.liferay.portal.kernel.freemarker.FreeMarkerVariablesUtil;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
021    import com.liferay.portal.kernel.json.JSONArray;
022    import com.liferay.portal.kernel.json.JSONException;
023    import com.liferay.portal.kernel.json.JSONFactoryUtil;
024    import com.liferay.portal.kernel.json.JSONObject;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.kernel.util.CharPool;
027    import com.liferay.portal.kernel.util.LocaleUtil;
028    import com.liferay.portal.kernel.util.LocalizationUtil;
029    import com.liferay.portal.kernel.util.StringBundler;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.xml.Attribute;
033    import com.liferay.portal.kernel.xml.Document;
034    import com.liferay.portal.kernel.xml.DocumentException;
035    import com.liferay.portal.kernel.xml.Element;
036    import com.liferay.portal.kernel.xml.SAXReaderUtil;
037    import com.liferay.portal.util.PortalUtil;
038    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
039    import com.liferay.util.freemarker.FreeMarkerTaglibFactoryUtil;
040    
041    import freemarker.ext.servlet.HttpRequestHashModel;
042    import freemarker.ext.servlet.ServletContextHashModel;
043    
044    import freemarker.template.ObjectWrapper;
045    import freemarker.template.TemplateHashModel;
046    
047    import java.io.IOException;
048    import java.io.Writer;
049    
050    import java.util.HashMap;
051    import java.util.List;
052    import java.util.Locale;
053    import java.util.Map;
054    
055    import javax.servlet.GenericServlet;
056    import javax.servlet.Servlet;
057    import javax.servlet.ServletException;
058    import javax.servlet.ServletRequest;
059    import javax.servlet.ServletResponse;
060    import javax.servlet.http.HttpServletRequest;
061    import javax.servlet.http.HttpServletResponse;
062    import javax.servlet.jsp.PageContext;
063    
064    /**
065     * @author Bruno Basto
066     * @author Eduardo Lundgren
067     * @author Brian Wing Shun Chan
068     */
069    public class DDMXSDImpl implements DDMXSD {
070    
071            public String getHTML(
072                            PageContext pageContext, Document document, Locale locale)
073                    throws Exception {
074    
075                    return getHTML(pageContext, document.getRootElement(), locale);
076            }
077    
078            public String getHTML(
079                            PageContext pageContext, Document document, Fields fields,
080                            Locale locale)
081                    throws Exception {
082    
083                    return getHTML(pageContext, document.getRootElement(), fields, locale);
084            }
085    
086            public String getHTML(
087                            PageContext pageContext, Document document, Fields fields,
088                            String namespace, boolean readOnly, Locale locale)
089                    throws Exception {
090    
091                    return getHTML(
092                            pageContext, document.getRootElement(), fields, namespace,
093                            readOnly, locale);
094            }
095    
096            public String getHTML(
097                            PageContext pageContext, Element element, Locale locale)
098                    throws Exception {
099    
100                    return getHTML(pageContext, element, null, locale);
101            }
102    
103            public String getHTML(
104                            PageContext pageContext, Element element, Fields fields,
105                            Locale locale)
106                    throws Exception {
107    
108                    return getHTML(
109                            pageContext, element, fields, StringPool.BLANK, false, locale);
110            }
111    
112            public String getHTML(
113                            PageContext pageContext, Element element, Fields fields,
114                            String namespace, boolean readOnly, Locale locale)
115                    throws Exception {
116    
117                    StringBundler sb = new StringBundler();
118    
119                    HttpServletRequest request =
120                            (HttpServletRequest)pageContext.getRequest();
121    
122                    String portletId = PortalUtil.getPortletId(request);
123    
124                    String portletNamespace = PortalUtil.getPortletNamespace(portletId);
125    
126                    List<Element> dynamicElementElements = element.elements(
127                            "dynamic-element");
128    
129                    for (Element dynamicElementElement : dynamicElementElements) {
130                            FreeMarkerContext freeMarkerContext = getFreeMarkerContext(
131                                    dynamicElementElement, locale);
132    
133                            freeMarkerContext.put("portletNamespace", portletNamespace);
134                            freeMarkerContext.put("namespace", namespace);
135    
136                            if (fields != null) {
137                                    freeMarkerContext.put("fields", fields);
138                            }
139    
140                            Map<String, Object> field =
141                                    (Map<String, Object>)freeMarkerContext.get("fieldStructure");
142    
143                            String childrenHTML = getHTML(
144                                    pageContext, dynamicElementElement, fields, namespace,
145                                    readOnly, locale);
146    
147                            field.put("children", childrenHTML);
148    
149                            String fieldNamespace = dynamicElementElement.attributeValue(
150                                    "fieldNamespace", _DEFAULT_NAMESPACE);
151    
152                            if (readOnly) {
153                                    fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;
154                            }
155    
156                            String type = dynamicElementElement.attributeValue("type");
157    
158                            String templateName = StringUtil.replaceFirst(
159                                    type, fieldNamespace.concat(StringPool.DASH), StringPool.BLANK);
160    
161                            StringBundler resourcePath = new StringBundler(5);
162    
163                            resourcePath.append(_TPL_PATH);
164                            resourcePath.append(fieldNamespace.toLowerCase());
165                            resourcePath.append(CharPool.SLASH);
166                            resourcePath.append(templateName);
167                            resourcePath.append(_TPL_EXT);
168    
169                            sb.append(
170                                    processFTL(
171                                            pageContext, freeMarkerContext, resourcePath.toString(),
172                                            readOnly));
173                    }
174    
175                    return sb.toString();
176            }
177    
178            public String getHTML(PageContext pageContext, String xml, Locale locale)
179                    throws Exception {
180    
181                    return getHTML(pageContext, xml, null, locale);
182            }
183    
184            public String getHTML(
185                            PageContext pageContext, String xml, Fields fields, Locale locale)
186                    throws Exception {
187    
188                    return getHTML(pageContext, SAXReaderUtil.read(xml), fields, locale);
189            }
190    
191            public String getHTML(
192                            PageContext pageContext, String xml, Fields fields,
193                            String namespace, Locale locale)
194                    throws Exception {
195    
196                    return getHTML(
197                            pageContext, SAXReaderUtil.read(xml), fields, namespace, false,
198                            locale);
199            }
200    
201            public String getHTML(
202                            PageContext pageContext, String xml, Fields fields,
203                            String namespace, boolean readOnly, Locale locale)
204                    throws Exception {
205    
206                    return getHTML(
207                            pageContext, SAXReaderUtil.read(xml), fields, namespace, readOnly,
208                            locale);
209            }
210    
211            public JSONArray getJSONArray(Document document) throws JSONException {
212                    return getJSONArray(document.getRootElement());
213            }
214    
215            public JSONArray getJSONArray(Element element) throws JSONException {
216                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
217    
218                    Document document = element.getDocument();
219    
220                    String defaultLocale = LocalizationUtil.getDefaultLocale(
221                            document.asXML());
222    
223                    List<Element> dynamicElementElements = element.elements(
224                            "dynamic-element");
225    
226                    for (Element dynamicElementElement : dynamicElementElements) {
227                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
228                            JSONObject localizationMapJSONObject =
229                                    JSONFactoryUtil.createJSONObject();
230    
231                            List<Element> metadataElements = dynamicElementElement.elements(
232                                    "meta-data");
233    
234                            for (Element metadataElement : metadataElements) {
235                                    if (metadataElement == null) {
236                                            continue;
237                                    }
238    
239                                    String locale = metadataElement.attributeValue("locale");
240    
241                                    JSONObject localeMap = JSONFactoryUtil.createJSONObject();
242    
243                                    localizationMapJSONObject.put(locale, localeMap);
244    
245                                    for (Element metadataEntryElement :
246                                                    metadataElement.elements()) {
247    
248                                            String attributeName = metadataEntryElement.attributeValue(
249                                                    "name");
250                                            String attributeValue = metadataEntryElement.getText();
251    
252                                            localeMap.put(attributeName, attributeValue);
253    
254                                            if (defaultLocale.equals(locale)) {
255                                                    jsonObject.put(attributeName, attributeValue);
256                                            }
257                                    }
258                            }
259    
260                            jsonObject.put("localizationMap", localizationMapJSONObject);
261    
262                            for (Attribute attribute : dynamicElementElement.attributes()) {
263                                    jsonObject.put(attribute.getName(), attribute.getValue());
264                            }
265    
266                            jsonObject.put("id", dynamicElementElement.attributeValue("name"));
267    
268                            String type = jsonObject.getString("type");
269    
270                            String key = "fields";
271    
272                            if (type.equals(_TYPE_RADIO) || type.equals(_TYPE_SELECT)) {
273                                    key = "options";
274                            }
275    
276                            jsonObject.put(key, getJSONArray(dynamicElementElement));
277    
278                            jsonArray.put(jsonObject);
279                    }
280    
281                    return jsonArray;
282            }
283    
284            public JSONArray getJSONArray(String xml)
285                    throws DocumentException, JSONException {
286    
287                    return getJSONArray(SAXReaderUtil.read(xml));
288            }
289    
290            protected Map<String, Object> getFieldContext(
291                    Element dynamicElementElement, Locale locale) {
292    
293                    Document document = dynamicElementElement.getDocument();
294    
295                    String[] availableLocales = LocalizationUtil.getAvailableLocales(
296                            document.asXML());
297    
298                    String defaultLanguageId = LocalizationUtil.getDefaultLocale(
299                            document.asXML());
300    
301                    String languageId = LocaleUtil.toLanguageId(locale);
302    
303                    if (!ArrayUtil.contains(availableLocales, languageId)) {
304                            languageId = defaultLanguageId;
305                    }
306    
307                    Element metadataElement =
308                            (Element)dynamicElementElement.selectSingleNode(
309                                    "meta-data[@locale='" + languageId + "']");
310    
311                    Map<String, Object> field = new HashMap<String, Object>();
312    
313                    if (metadataElement != null) {
314                            for (Element metadataEntry : metadataElement.elements()) {
315                                    field.put(
316                                            metadataEntry.attributeValue("name"),
317                                            metadataEntry.getText());
318                            }
319                    }
320    
321                    for (Attribute attribute : dynamicElementElement.attributes()) {
322                            field.put(attribute.getName(), attribute.getValue());
323                    }
324    
325                    return field;
326            }
327    
328            protected FreeMarkerContext getFreeMarkerContext(
329                    Element dynamicElementElement, Locale locale) {
330    
331                    FreeMarkerContext freeMarkerContext =
332                            FreeMarkerEngineUtil.getWrappedStandardToolsContext();
333    
334                    Map<String, Object> fieldContext = getFieldContext(
335                            dynamicElementElement, locale);
336    
337                    Map<String, Object> parentFieldContext = new HashMap<String, Object>();
338    
339                    Element parentElement = dynamicElementElement.getParent();
340    
341                    if (parentElement != null) {
342                            parentFieldContext = getFieldContext(parentElement, locale);
343                    }
344    
345                    freeMarkerContext.put("fieldStructure", fieldContext);
346                    freeMarkerContext.put("parentFieldStructure", parentFieldContext);
347    
348                    return freeMarkerContext;
349            }
350    
351            /**
352             * @see com.liferay.taglib.util.ThemeUtil#includeFTL
353             */
354            protected String processFTL(
355                            PageContext pageContext, FreeMarkerContext freeMarkerContext,
356                            String resourcePath, boolean readOnly)
357                    throws Exception {
358    
359                    if (!FreeMarkerEngineUtil.resourceExists(resourcePath)) {
360                            if (readOnly) {
361                                    resourcePath = _TPL_DEFAULT_READ_ONLY_PATH;
362                            }
363                            else {
364                                    resourcePath = _TPL_DEFAULT_PATH;
365                            }
366                    }
367    
368                    HttpServletRequest request =
369                            (HttpServletRequest)pageContext.getRequest();
370    
371                    // FreeMarker variables
372    
373                    FreeMarkerVariablesUtil.insertVariables(freeMarkerContext, request);
374    
375                    // Tag libraries
376    
377                    HttpServletResponse response =
378                            (HttpServletResponse)pageContext.getResponse();
379    
380                    Writer writer = new UnsyncStringWriter();
381    
382                    // Portal JSP tag library factory
383    
384                    TemplateHashModel portalTaglib =
385                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
386                                    pageContext.getServletContext());
387    
388                    freeMarkerContext.put("PortalJspTagLibs", portalTaglib);
389    
390                    // FreeMarker JSP tag library support
391    
392                    final Servlet servlet = (Servlet)pageContext.getPage();
393    
394                    GenericServlet genericServlet = null;
395    
396                    if (servlet instanceof GenericServlet) {
397                            genericServlet = (GenericServlet)servlet;
398                    }
399                    else {
400                            genericServlet = new GenericServlet() {
401    
402                                    @Override
403                                    public void service(
404                                                    ServletRequest servletRequest,
405                                                    ServletResponse servletResponse)
406                                            throws ServletException, IOException {
407    
408                                            servlet.service(servletRequest, servletResponse);
409                                    }
410    
411                            };
412    
413                            genericServlet.init(pageContext.getServletConfig());
414                    }
415    
416                    ServletContextHashModel servletContextHashModel =
417                            new ServletContextHashModel(
418                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
419    
420                    freeMarkerContext.put("Application", servletContextHashModel);
421    
422                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
423                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
424    
425                    freeMarkerContext.put("Request", httpRequestHashModel);
426    
427                    // Merge templates
428    
429                    FreeMarkerEngineUtil.mergeTemplate(
430                            resourcePath, freeMarkerContext, writer);
431    
432                    return writer.toString();
433            }
434    
435            private static final String _DEFAULT_NAMESPACE = "alloy";
436    
437            private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";
438    
439            private static final String _TPL_DEFAULT_PATH =
440                    "com/liferay/portlet/dynamicdatamapping/dependencies/alloy/text.ftl";
441    
442            private static final String _TPL_DEFAULT_READ_ONLY_PATH =
443                    "com/liferay/portlet/dynamicdatamapping/dependencies/readonly/" +
444                            "default.ftl";
445    
446            private static final String _TPL_EXT = ".ftl";
447    
448            private static final String _TPL_PATH =
449                    "com/liferay/portlet/dynamicdatamapping/dependencies/";
450    
451            private static final String _TYPE_RADIO = "radio";
452    
453            private static final String _TYPE_SELECT = "select";
454    
455    }