001    /**
002     * Copyright (c) 2000-2013 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.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
020    import com.liferay.portal.kernel.json.JSONArray;
021    import com.liferay.portal.kernel.json.JSONFactoryUtil;
022    import com.liferay.portal.kernel.json.JSONObject;
023    import com.liferay.portal.kernel.language.LanguageUtil;
024    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
025    import com.liferay.portal.kernel.template.Template;
026    import com.liferay.portal.kernel.template.TemplateConstants;
027    import com.liferay.portal.kernel.template.TemplateManagerUtil;
028    import com.liferay.portal.kernel.template.TemplateResource;
029    import com.liferay.portal.kernel.template.URLTemplateResource;
030    import com.liferay.portal.kernel.util.ArrayUtil;
031    import com.liferay.portal.kernel.util.CharPool;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HtmlUtil;
034    import com.liferay.portal.kernel.util.LocaleUtil;
035    import com.liferay.portal.kernel.util.LocalizationUtil;
036    import com.liferay.portal.kernel.util.StringBundler;
037    import com.liferay.portal.kernel.util.StringPool;
038    import com.liferay.portal.kernel.util.StringUtil;
039    import com.liferay.portal.kernel.util.Validator;
040    import com.liferay.portal.kernel.xml.Attribute;
041    import com.liferay.portal.kernel.xml.Document;
042    import com.liferay.portal.kernel.xml.DocumentException;
043    import com.liferay.portal.kernel.xml.Element;
044    import com.liferay.portal.kernel.xml.Node;
045    import com.liferay.portal.kernel.xml.SAXReaderUtil;
046    import com.liferay.portal.kernel.xml.XPath;
047    import com.liferay.portal.util.PortalUtil;
048    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
049    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
050    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
051    import com.liferay.portlet.dynamicdatamapping.service.DDMStorageLinkLocalServiceUtil;
052    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
053    import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
054    import com.liferay.portlet.dynamicdatamapping.storage.Field;
055    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
056    import com.liferay.util.freemarker.FreeMarkerTaglibFactoryUtil;
057    
058    import freemarker.ext.servlet.HttpRequestHashModel;
059    import freemarker.ext.servlet.ServletContextHashModel;
060    
061    import freemarker.template.ObjectWrapper;
062    import freemarker.template.TemplateHashModel;
063    
064    import java.io.IOException;
065    import java.io.Writer;
066    
067    import java.net.URL;
068    
069    import java.util.HashMap;
070    import java.util.List;
071    import java.util.Locale;
072    import java.util.Map;
073    
074    import javax.servlet.GenericServlet;
075    import javax.servlet.Servlet;
076    import javax.servlet.ServletException;
077    import javax.servlet.ServletRequest;
078    import javax.servlet.ServletResponse;
079    import javax.servlet.http.HttpServletRequest;
080    import javax.servlet.http.HttpServletResponse;
081    import javax.servlet.jsp.PageContext;
082    
083    /**
084     * @author Bruno Basto
085     * @author Eduardo Lundgren
086     * @author Brian Wing Shun Chan
087     * @author Marcellus Tavares
088     */
089    @DoPrivileged
090    public class DDMXSDImpl implements DDMXSD {
091    
092            public DDMXSDImpl() {
093                    String defaultTemplateId = _TPL_PATH + "alloy/text.ftl";
094    
095                    URL defaultTemplateURL = getResource(defaultTemplateId);
096    
097                    _defaultTemplateResource = new URLTemplateResource(
098                            defaultTemplateId, defaultTemplateURL);
099    
100                    String defaultReadOnlyTemplateId = _TPL_PATH + "readonly/default.ftl";
101    
102                    URL defaultReadOnlyTemplateURL = getResource(defaultReadOnlyTemplateId);
103    
104                    _defaultReadOnlyTemplateResource = new URLTemplateResource(
105                            defaultReadOnlyTemplateId, defaultReadOnlyTemplateURL);
106            }
107    
108            @Override
109            public String getFieldHTML(
110                            PageContext pageContext, Element element, Fields fields,
111                            String portletNamespace, String namespace, String mode,
112                            boolean readOnly, Locale locale)
113                    throws Exception {
114    
115                    Map<String, Object> freeMarkerContext = getFreeMarkerContext(
116                            pageContext, portletNamespace, namespace, element, locale);
117    
118                    if (fields != null) {
119                            freeMarkerContext.put("fields", fields);
120                    }
121    
122                    Map<String, Object> fieldStructure =
123                            (Map<String, Object>)freeMarkerContext.get("fieldStructure");
124    
125                    int fieldRepetition = 1;
126                    int offset = 0;
127    
128                    DDMFieldsCounter ddmFieldsCounter = getFieldsCounter(
129                            pageContext, fields, portletNamespace, namespace);
130    
131                    String name = element.attributeValue("name");
132    
133                    boolean fieldDisplayable = isFieldDisplayable(fields, name);
134    
135                    if (fieldDisplayable) {
136                            Field fieldsDisplayField = fields.get(DDMImpl.FIELDS_DISPLAY_NAME);
137    
138                            String[] fieldsDisplayValues = DDMUtil.getFieldsDisplayValues(
139                                            fieldsDisplayField);
140    
141                            Map<String, Object> parentFieldStructure =
142                                    (Map<String, Object>)freeMarkerContext.get(
143                                            "parentFieldStructure");
144    
145                            String parentFieldName = (String)parentFieldStructure.get("name");
146    
147                            offset = getFieldOffset(
148                                    fieldsDisplayValues, name, ddmFieldsCounter.get(name));
149    
150                            if (offset == fieldsDisplayValues.length) {
151                                    return StringPool.BLANK;
152                            }
153    
154                            fieldRepetition = countFieldRepetition(
155                                    fieldsDisplayValues, parentFieldName, offset);
156                    }
157    
158                    StringBundler sb = new StringBundler(fieldRepetition);
159    
160                    while (fieldRepetition > 0) {
161                            fieldStructure.put("fieldNamespace", StringUtil.randomId());
162                            fieldStructure.put("valueIndex", ddmFieldsCounter.get(name));
163    
164                            if (fieldDisplayable) {
165                                    ddmFieldsCounter.incrementKey(name);
166                            }
167    
168                            String childrenHTML = getHTML(
169                                    pageContext, element, fields, portletNamespace, namespace, mode,
170                                    readOnly, locale);
171    
172                            fieldStructure.put("children", childrenHTML);
173    
174                            boolean disabled = GetterUtil.getBoolean(
175                                    fieldStructure.get("disabled"), false);
176    
177                            if (disabled) {
178                                    readOnly = true;
179                            }
180    
181                            sb.append(
182                                    processFTL(
183                                            pageContext, element, mode, readOnly, freeMarkerContext));
184    
185                            fieldRepetition--;
186                    }
187    
188                    return sb.toString();
189            }
190    
191            @Override
192            public String getFieldHTMLByName(
193                            PageContext pageContext, long classNameId, long classPK,
194                            String fieldName, Fields fields, String portletNamespace,
195                            String namespace, String mode, boolean readOnly, Locale locale)
196                    throws Exception {
197    
198                    String xsd = getXSD(classNameId, classPK);
199    
200                    Document document = SAXReaderUtil.read(xsd);
201    
202                    String xPathExpression =
203                            "//dynamic-element[@name=".concat(
204                                    HtmlUtil.escapeXPathAttribute(fieldName)).concat("]");
205    
206                    XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
207    
208                    Node node = xPathSelector.selectSingleNode(document.getRootElement());
209    
210                    Element element = (Element)node.asXPathResult(node.getParent());
211    
212                    return getFieldHTML(
213                            pageContext, element, fields, portletNamespace, namespace, mode,
214                            readOnly, locale);
215            }
216    
217            @Override
218            public String getHTML(
219                            PageContext pageContext, DDMStructure ddmStructure, Fields fields,
220                            String portletNamespace, String namespace, boolean readOnly,
221                            Locale locale)
222                    throws Exception {
223    
224                    return getHTML(
225                            pageContext, ddmStructure.getXsd(), fields, portletNamespace,
226                            namespace, readOnly, locale);
227            }
228    
229            @Override
230            public String getHTML(
231                            PageContext pageContext, DDMTemplate ddmTemplate, Fields fields,
232                            String portletNamespace, String namespace, boolean readOnly,
233                            Locale locale)
234                    throws Exception {
235    
236                    return getHTML(
237                            pageContext, ddmTemplate.getScript(), fields, portletNamespace,
238                            namespace, ddmTemplate.getMode(), readOnly, locale);
239            }
240    
241            public String getHTML(
242                            PageContext pageContext, Element element, Fields fields,
243                            String portletNamespace, Locale locale)
244                    throws Exception {
245    
246                    return getHTML(
247                            pageContext, element, fields, portletNamespace, StringPool.BLANK,
248                            null, false, locale);
249            }
250    
251            public String getHTML(
252                            PageContext pageContext, Element element, Fields fields,
253                            String portletNamespace, String namespace, String mode,
254                            boolean readOnly, Locale locale)
255                    throws Exception {
256    
257                    List<Element> dynamicElementElements = element.elements(
258                            "dynamic-element");
259    
260                    StringBundler sb = new StringBundler(dynamicElementElements.size());
261    
262                    for (Element dynamicElementElement : dynamicElementElements) {
263                            sb.append(
264                                    getFieldHTML(
265                                            pageContext, dynamicElementElement, fields,
266                                            portletNamespace, namespace, mode, readOnly, locale));
267                    }
268    
269                    return sb.toString();
270            }
271    
272            public String getHTML(
273                            PageContext pageContext, Element element, String portletNamespace,
274                            Locale locale)
275                    throws Exception {
276    
277                    return getHTML(pageContext, element, null, portletNamespace, locale);
278            }
279    
280            @Override
281            public String getHTML(
282                            PageContext pageContext, String xml, Fields fields,
283                            String portletNamespace, Locale locale)
284                    throws Exception {
285    
286                    return getHTML(
287                            pageContext, xml, fields, portletNamespace, StringPool.BLANK,
288                            locale);
289            }
290    
291            @Override
292            public String getHTML(
293                            PageContext pageContext, String xml, Fields fields,
294                            String portletNamespace, String namespace, boolean readOnly,
295                            Locale locale)
296                    throws Exception {
297    
298                    return getHTML(
299                            pageContext, xml, fields, portletNamespace, namespace, null,
300                            readOnly, locale);
301            }
302    
303            @Override
304            public String getHTML(
305                            PageContext pageContext, String xml, Fields fields,
306                            String portletNamespace, String namespace, Locale locale)
307                    throws Exception {
308    
309                    return getHTML(
310                            pageContext, xml, fields, portletNamespace, namespace, false,
311                            locale);
312            }
313    
314            @Override
315            public String getHTML(
316                            PageContext pageContext, String xml, Fields fields,
317                            String portletNamespace, String namespace, String mode,
318                            boolean readOnly, Locale locale)
319                    throws Exception {
320    
321                    Document document = SAXReaderUtil.read(xml);
322    
323                    return getHTML(
324                            pageContext, document.getRootElement(), fields, portletNamespace,
325                            namespace, mode, readOnly, locale);
326            }
327    
328            @Override
329            public String getHTML(
330                            PageContext pageContext, String xml, String portletNamespace,
331                            Locale locale)
332                    throws Exception {
333    
334                    return getHTML(pageContext, xml, null, locale);
335            }
336    
337            @Override
338            public JSONArray getJSONArray(DDMStructure structure, String xsd)
339                    throws PortalException, SystemException {
340    
341                    JSONArray jsonArray = null;
342    
343                    if (Validator.isNull(xsd)) {
344                            jsonArray = getJSONArray(structure.getDocument());
345                    }
346                    else {
347                            jsonArray = getJSONArray(xsd);
348                    }
349    
350                    addStructureFieldAttributes(structure, jsonArray);
351    
352                    return jsonArray;
353            }
354    
355            @Override
356            public JSONArray getJSONArray(Document document) throws PortalException {
357                    return getJSONArray(document.getRootElement());
358            }
359    
360            @Override
361            public JSONArray getJSONArray(Element element) throws PortalException {
362                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
363    
364                    Document document = element.getDocument();
365    
366                    String defaultLanguageId = LocalizationUtil.getDefaultLanguageId(
367                            document.asXML());
368    
369                    List<Element> dynamicElementElements = element.elements(
370                            "dynamic-element");
371    
372                    for (Element dynamicElementElement : dynamicElementElements) {
373                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
374                            JSONObject localizationMapJSONObject =
375                                    JSONFactoryUtil.createJSONObject();
376    
377                            for (Attribute attribute : dynamicElementElement.attributes()) {
378                                    jsonObject.put(attribute.getName(), attribute.getValue());
379                            }
380    
381                            jsonObject.put("autoGeneratedName", false);
382                            jsonObject.put("id", dynamicElementElement.attributeValue("name"));
383    
384                            String type = jsonObject.getString("type");
385    
386                            List<Element> metadataElements = dynamicElementElement.elements(
387                                    "meta-data");
388    
389                            for (Element metadataElement : metadataElements) {
390                                    if (metadataElement == null) {
391                                            continue;
392                                    }
393    
394                                    String locale = metadataElement.attributeValue("locale");
395    
396                                    JSONObject localeMap = JSONFactoryUtil.createJSONObject();
397    
398                                    for (Element metadataEntryElement :
399                                                    metadataElement.elements()) {
400    
401                                            String attributeName = metadataEntryElement.attributeValue(
402                                                    "name");
403                                            String attributeValue = metadataEntryElement.getTextTrim();
404    
405                                            putMetadataValue(
406                                                    localeMap, attributeName, attributeValue, type);
407    
408                                            if (defaultLanguageId.equals(locale)) {
409                                                    putMetadataValue(
410                                                            jsonObject, attributeName, attributeValue, type);
411                                            }
412                                    }
413    
414                                    localizationMapJSONObject.put(locale, localeMap);
415                            }
416    
417                            jsonObject.put("localizationMap", localizationMapJSONObject);
418    
419                            JSONArray hiddenAttributesJSONArray =
420                                    JSONFactoryUtil.createJSONArray();
421    
422                            if (type.equals(DDMImpl.TYPE_CHECKBOX)) {
423                                    hiddenAttributesJSONArray.put("required");
424                            }
425    
426                            hiddenAttributesJSONArray.put("readOnly");
427    
428                            jsonObject.put("hiddenAttributes", hiddenAttributesJSONArray);
429    
430                            String key = "fields";
431    
432                            if (type.equals(DDMImpl.TYPE_RADIO) ||
433                                    type.equals(DDMImpl.TYPE_SELECT)) {
434    
435                                    key = "options";
436                            }
437    
438                            jsonObject.put(key, getJSONArray(dynamicElementElement));
439    
440                            jsonArray.put(jsonObject);
441                    }
442    
443                    return jsonArray;
444            }
445    
446            @Override
447            public JSONArray getJSONArray(String xml)
448                    throws PortalException, SystemException {
449    
450                    try {
451                            return getJSONArray(SAXReaderUtil.read(xml));
452                    }
453                    catch (DocumentException de) {
454                            throw new SystemException();
455                    }
456            }
457    
458            @Override
459            public String getSimpleFieldHTML(
460                            PageContext pageContext, Element element, Field field,
461                            String portletNamespace, String namespace, String mode,
462                            boolean readOnly, Locale locale)
463                    throws Exception {
464    
465                    Map<String, Object> freeMarkerContext = getFreeMarkerContext(
466                            pageContext, portletNamespace, namespace, element, locale);
467    
468                    freeMarkerContext.put("ignoreRepeatable", Boolean.TRUE);
469    
470                    Map<String, Object> fieldStructure =
471                            (Map<String, Object>)freeMarkerContext.get("fieldStructure");
472    
473                    DDMFieldsCounter ddmFieldsCounter = getFieldsCounter(
474                            pageContext, null, portletNamespace, namespace);
475    
476                    String name = element.attributeValue("name");
477    
478                    if ((field != null) && Validator.isNotNull(field.getValue())) {
479                            Fields fields = new Fields();
480    
481                            fields.put(field);
482    
483                            freeMarkerContext.put("fields", fields);
484                    }
485    
486                    fieldStructure.put("fieldNamespace", StringUtil.randomId());
487                    fieldStructure.put("valueIndex", ddmFieldsCounter.get(name));
488    
489                    List<Element> dynamicElementElements = element.elements(
490                            "dynamic-element");
491    
492                    StringBundler sb = new StringBundler(dynamicElementElements.size());
493    
494                    for (Element dynamicElementElement : dynamicElementElements) {
495                            String type = dynamicElementElement.attributeValue("type");
496    
497                            if (!type.equals("option")) {
498                                    sb.append(StringPool.BLANK);
499                            }
500                            else {
501                                    sb.append(
502                                            getSimpleFieldHTML(
503                                                    pageContext, dynamicElementElement, field,
504                                                    portletNamespace, namespace, mode, readOnly, locale));
505                            }
506                    }
507    
508                    fieldStructure.put("children", sb.toString());
509    
510                    return processFTL(
511                            pageContext, element, mode, readOnly, freeMarkerContext);
512            }
513    
514            @Override
515            public String getSimpleFieldHTMLByName(
516                            PageContext pageContext, long classNameId, long classPK,
517                            Field field, String portletNamespace, String namespace, String mode,
518                            boolean readOnly, Locale locale)
519                    throws Exception {
520    
521                    String xsd = getXSD(classNameId, classPK);
522    
523                    Document document = SAXReaderUtil.read(xsd);
524    
525                    String xPathExpression =
526                            "//dynamic-element[@name=".concat(
527                                    HtmlUtil.escapeXPathAttribute(field.getName())).concat("]");
528    
529                    XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
530    
531                    Node node = xPathSelector.selectSingleNode(document.getRootElement());
532    
533                    Element element = (Element)node.asXPathResult(node.getParent());
534    
535                    return getSimpleFieldHTML(
536                            pageContext, element, field, portletNamespace, namespace, mode,
537                            readOnly, locale);
538            }
539    
540            @Override
541            public String getXSD(long classNameId, long classPK)
542                    throws PortalException, SystemException {
543    
544                    if ((classNameId <= 0) || (classPK <= 0)) {
545                            return null;
546                    }
547    
548                    long ddmStructureClassNameId = PortalUtil.getClassNameId(
549                            DDMStructure.class);
550    
551                    long ddmTemplateClassNameId = PortalUtil.getClassNameId(
552                            DDMTemplate.class);
553    
554                    if (classNameId == ddmStructureClassNameId) {
555                            DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(
556                                    classPK);
557    
558                            return structure.getCompleteXsd();
559                    }
560                    else if (classNameId == ddmTemplateClassNameId) {
561                            DDMTemplate template = DDMTemplateLocalServiceUtil.getTemplate(
562                                    classPK);
563    
564                            return template.getScript();
565                    }
566    
567                    return null;
568            }
569    
570            protected JSONArray addStructureFieldAttributes(
571                    DDMStructure structure, JSONArray jsonArray) {
572    
573                    for (int i = 0; i < jsonArray.length(); i++) {
574                            JSONObject jsonObject = jsonArray.getJSONObject(i);
575    
576                            String fieldName = jsonObject.getString("name");
577    
578                            jsonObject.put(
579                                    "readOnlyAttributes",
580                                    getStructureFieldReadOnlyAttributes(structure, fieldName));
581                    }
582    
583                    return jsonArray;
584            }
585    
586            protected int countFieldRepetition(
587                    String[] fieldsDisplayValues, String parentFieldName, int offset) {
588    
589                    int total = 0;
590    
591                    String fieldName = fieldsDisplayValues[offset];
592    
593                    for (; offset < fieldsDisplayValues.length; offset++) {
594                            String fieldNameValue = fieldsDisplayValues[offset];
595    
596                            if (fieldNameValue.equals(fieldName)) {
597                                    total++;
598                            }
599    
600                            if (fieldNameValue.equals(parentFieldName)) {
601                                    break;
602                            }
603                    }
604    
605                    return total;
606            }
607    
608            protected Map<String, Object> getFieldContext(
609                    PageContext pageContext, String portletNamespace, String namespace,
610                    Element dynamicElementElement, Locale locale) {
611    
612                    Map<String, Map<String, Object>> fieldsContext = getFieldsContext(
613                            pageContext, portletNamespace, namespace);
614    
615                    String name = dynamicElementElement.attributeValue("name");
616    
617                    Map<String, Object> fieldContext = fieldsContext.get(name);
618    
619                    if (fieldContext != null) {
620                            return fieldContext;
621                    }
622    
623                    Document document = dynamicElementElement.getDocument();
624    
625                    String[] availableLanguageIds =
626                            LocalizationUtil.getAvailableLanguageIds(document);
627    
628                    String defaultLanguageId = LocalizationUtil.getDefaultLanguageId(
629                            document);
630    
631                    String editingLanguageId = LocaleUtil.toLanguageId(locale);
632    
633                    String structureLanguageId = editingLanguageId;
634    
635                    if (!ArrayUtil.contains(availableLanguageIds, editingLanguageId)) {
636                            structureLanguageId = defaultLanguageId;
637                    }
638    
639                    Element metadataElement =
640                            (Element)dynamicElementElement.selectSingleNode(
641                                    "meta-data[@locale='" + structureLanguageId + "']");
642    
643                    fieldContext = new HashMap<String, Object>();
644    
645                    if (metadataElement != null) {
646                            for (Element metadataEntry : metadataElement.elements()) {
647                                    fieldContext.put(
648                                            metadataEntry.attributeValue("name"),
649                                            metadataEntry.getText());
650                            }
651                    }
652    
653                    for (Attribute attribute : dynamicElementElement.attributes()) {
654                            fieldContext.put(attribute.getName(), attribute.getValue());
655                    }
656    
657                    boolean localizable = GetterUtil.getBoolean(
658                            dynamicElementElement.attributeValue("localizable"), true);
659    
660                    if (!localizable && !editingLanguageId.equals(defaultLanguageId)) {
661                            fieldContext.put("disabled", Boolean.TRUE.toString());
662                    }
663    
664                    fieldContext.put("fieldNamespace", StringUtil.randomId());
665    
666                    boolean checkRequired = GetterUtil.getBoolean(
667                            pageContext.getAttribute("checkRequired"), true);
668    
669                    if (!checkRequired) {
670                            fieldContext.put("required", Boolean.FALSE.toString());
671                    }
672    
673                    fieldsContext.put(name, fieldContext);
674    
675                    return fieldContext;
676            }
677    
678            protected int getFieldOffset(
679                    String[] fieldsDisplayValues, String name, int index) {
680    
681                    int offset = 0;
682    
683                    for (; offset < fieldsDisplayValues.length; offset++) {
684                            if (name.equals(fieldsDisplayValues[offset])) {
685                                    index--;
686    
687                                    if (index < 0) {
688                                            break;
689                                    }
690                            }
691                    }
692    
693                    return offset;
694            }
695    
696            protected Map<String, Map<String, Object>> getFieldsContext(
697                    PageContext pageContext, String portletNamespace, String namespace) {
698    
699                    String fieldsContextKey =
700                            portletNamespace + namespace + "fieldsContext";
701    
702                    Map<String, Map<String, Object>> fieldsContext =
703                            (Map<String, Map<String, Object>>)pageContext.getAttribute(
704                                    fieldsContextKey);
705    
706                    if (fieldsContext == null) {
707                            fieldsContext = new HashMap<String, Map<String, Object>>();
708    
709                            pageContext.setAttribute(fieldsContextKey, fieldsContext);
710                    }
711    
712                    return fieldsContext;
713            }
714    
715            protected DDMFieldsCounter getFieldsCounter(
716                    PageContext pageContext, Fields fields, String portletNamespace,
717                    String namespace) {
718    
719                    String fieldsCounterKey = portletNamespace + namespace + "fieldsCount";
720    
721                    DDMFieldsCounter ddmFieldsCounter =
722                            (DDMFieldsCounter)pageContext.getAttribute(fieldsCounterKey);
723    
724                    if (ddmFieldsCounter == null) {
725                            ddmFieldsCounter = new DDMFieldsCounter();
726    
727                            pageContext.setAttribute(fieldsCounterKey, ddmFieldsCounter);
728                    }
729    
730                    return ddmFieldsCounter;
731            }
732    
733            protected Map<String, Object> getFreeMarkerContext(
734                    PageContext pageContext, String portletNamespace, String namespace,
735                    Element dynamicElementElement, Locale locale) {
736    
737                    Map<String, Object> freeMarkerContext = new HashMap<String, Object>();
738    
739                    Map<String, Object> fieldContext = getFieldContext(
740                            pageContext, portletNamespace, namespace, dynamicElementElement,
741                            locale);
742    
743                    Map<String, Object> parentFieldContext = new HashMap<String, Object>();
744    
745                    Element parentElement = dynamicElementElement.getParent();
746    
747                    if (parentElement != null) {
748                            parentFieldContext = getFieldContext(
749                                    pageContext, portletNamespace, namespace, parentElement,
750                                    locale);
751                    }
752    
753                    freeMarkerContext.put("fieldStructure", fieldContext);
754                    freeMarkerContext.put("namespace", namespace);
755                    freeMarkerContext.put("parentFieldStructure", parentFieldContext);
756                    freeMarkerContext.put("portletNamespace", portletNamespace);
757                    freeMarkerContext.put(
758                            "requestedLanguageDir", LanguageUtil.get(locale, "lang.dir"));
759                    freeMarkerContext.put("requestedLocale", locale);
760    
761                    return freeMarkerContext;
762            }
763    
764            protected URL getResource(String name) {
765                    Class<?> clazz = getClass();
766    
767                    ClassLoader classLoader = clazz.getClassLoader();
768    
769                    return classLoader.getResource(name);
770            }
771    
772            protected JSONArray getStructureFieldReadOnlyAttributes(
773                    DDMStructure structure, String fieldName) {
774    
775                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
776    
777                    try {
778                            if (DDMStorageLinkLocalServiceUtil.getStructureStorageLinksCount(
779                                            structure.getStructureId()) > 0) {
780    
781                                    jsonArray.put("name");
782                            }
783                    }
784                    catch (Exception e) {
785                    }
786    
787                    return jsonArray;
788            }
789    
790            protected boolean isFieldDisplayable(Fields fields, String fieldName)
791                    throws Exception {
792    
793                    if (fields == null) {
794                            return false;
795                    }
796    
797                    Field fieldsDisplayField = fields.get(DDMImpl.FIELDS_DISPLAY_NAME);
798    
799                    if (fieldsDisplayField == null) {
800                            return false;
801                    }
802    
803                    String[] fieldsDisplayValues = DDMUtil.getFieldsDisplayValues(
804                            fieldsDisplayField);
805    
806                    return ArrayUtil.contains(fieldsDisplayValues, fieldName);
807            }
808    
809            protected String processFTL(
810                            PageContext pageContext, Element element, String mode,
811                            boolean readOnly, Map<String, Object> freeMarkerContext)
812                    throws Exception {
813    
814                    String fieldNamespace = element.attributeValue(
815                            "fieldNamespace", _DEFAULT_NAMESPACE);
816    
817                    TemplateResource templateResource = _defaultTemplateResource;
818    
819                    Map<String, Object> fieldStructure =
820                            (Map<String, Object>)freeMarkerContext.get("fieldStructure");
821    
822                    boolean fieldReadOnly = GetterUtil.getBoolean(
823                            fieldStructure.get("readOnly"));
824    
825                    if ((fieldReadOnly && Validator.isNotNull(mode) &&
826                             StringUtil.equalsIgnoreCase(
827                                    mode, DDMTemplateConstants.TEMPLATE_MODE_EDIT)) ||
828                            readOnly) {
829    
830                            fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;
831    
832                            templateResource = _defaultReadOnlyTemplateResource;
833                    }
834    
835                    String type = element.attributeValue("type");
836    
837                    String templateName = StringUtil.replaceFirst(
838                            type, fieldNamespace.concat(StringPool.DASH), StringPool.BLANK);
839    
840                    StringBundler resourcePath = new StringBundler(5);
841    
842                    resourcePath.append(_TPL_PATH);
843                    resourcePath.append(StringUtil.toLowerCase(fieldNamespace));
844                    resourcePath.append(CharPool.SLASH);
845                    resourcePath.append(templateName);
846                    resourcePath.append(_TPL_EXT);
847    
848                    String resource = resourcePath.toString();
849    
850                    URL url = getResource(resource);
851    
852                    if (url != null) {
853                            templateResource = new URLTemplateResource(resource, url);
854                    }
855    
856                    if (templateResource == null) {
857                            throw new Exception("Unable to load template resource " + resource);
858                    }
859    
860                    Template template = TemplateManagerUtil.getTemplate(
861                            TemplateConstants.LANG_TYPE_FTL, templateResource, false);
862    
863                    for (Map.Entry<String, Object> entry : freeMarkerContext.entrySet()) {
864                            template.put(entry.getKey(), entry.getValue());
865                    }
866    
867                    return processFTL(pageContext, template);
868            }
869    
870            /**
871             * @see com.liferay.taglib.util.ThemeUtil#includeFTL
872             */
873            protected String processFTL(PageContext pageContext, Template template)
874                    throws Exception {
875    
876                    HttpServletRequest request =
877                            (HttpServletRequest)pageContext.getRequest();
878    
879                    // FreeMarker variables
880    
881                    template.prepare(request);
882    
883                    // Tag libraries
884    
885                    HttpServletResponse response =
886                            (HttpServletResponse)pageContext.getResponse();
887    
888                    Writer writer = new UnsyncStringWriter();
889    
890                    // Portal JSP tag library factory
891    
892                    TemplateHashModel portalTaglib =
893                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
894                                    pageContext.getServletContext());
895    
896                    template.put("PortalJspTagLibs", portalTaglib);
897    
898                    // FreeMarker JSP tag library support
899    
900                    final Servlet servlet = (Servlet)pageContext.getPage();
901    
902                    GenericServlet genericServlet = null;
903    
904                    if (servlet instanceof GenericServlet) {
905                            genericServlet = (GenericServlet)servlet;
906                    }
907                    else {
908                            genericServlet = new GenericServlet() {
909    
910                                    @Override
911                                    public void service(
912                                                    ServletRequest servletRequest,
913                                                    ServletResponse servletResponse)
914                                            throws IOException, ServletException {
915    
916                                            servlet.service(servletRequest, servletResponse);
917                                    }
918    
919                            };
920    
921                            genericServlet.init(pageContext.getServletConfig());
922                    }
923    
924                    ServletContextHashModel servletContextHashModel =
925                            new ServletContextHashModel(
926                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
927    
928                    template.put("Application", servletContextHashModel);
929    
930                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
931                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
932    
933                    template.put("Request", httpRequestHashModel);
934    
935                    // Merge templates
936    
937                    template.processTemplate(writer);
938    
939                    return writer.toString();
940            }
941    
942            protected void putMetadataValue(
943                    JSONObject jsonObject, String attributeName, String attributeValue,
944                    String type) {
945    
946                    if (type.equals(DDMImpl.TYPE_RADIO) ||
947                            type.equals(DDMImpl.TYPE_SELECT)) {
948    
949                            if (attributeName.equals("predefinedValue")) {
950                                    try {
951                                            jsonObject.put(
952                                                    attributeName,
953                                                    JSONFactoryUtil.createJSONArray(attributeValue));
954                                    }
955                                    catch (Exception e) {
956                                    }
957    
958                                    return;
959                            }
960                    }
961    
962                    jsonObject.put(attributeName, attributeValue);
963            }
964    
965            private static final String _DEFAULT_NAMESPACE = "alloy";
966    
967            private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";
968    
969            private static final String _TPL_EXT = ".ftl";
970    
971            private static final String _TPL_PATH =
972                    "com/liferay/portlet/dynamicdatamapping/dependencies/";
973    
974            private TemplateResource _defaultReadOnlyTemplateResource;
975            private TemplateResource _defaultTemplateResource;
976    
977    }