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