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