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                            hiddenAttributesJSONArray.put("readOnly");
438    
439                            jsonObject.put("hiddenAttributes", hiddenAttributesJSONArray);
440    
441                            String key = "fields";
442    
443                            if (type.equals(DDMImpl.TYPE_RADIO) ||
444                                    type.equals(DDMImpl.TYPE_SELECT)) {
445    
446                                    key = "options";
447                            }
448    
449                            jsonObject.put(key, getJSONArray(dynamicElementElement));
450    
451                            jsonArray.put(jsonObject);
452                    }
453    
454                    return jsonArray;
455            }
456    
457            @Override
458            public JSONArray getJSONArray(String xml)
459                    throws PortalException, SystemException {
460    
461                    try {
462                            return getJSONArray(SAXReaderUtil.read(xml));
463                    }
464                    catch (DocumentException de) {
465                            throw new SystemException();
466                    }
467            }
468    
469            @Override
470            public String getSimpleFieldHTML(
471                            PageContext pageContext, Element element, Field field,
472                            String portletNamespace, String namespace, String mode,
473                            boolean readOnly, Locale locale)
474                    throws Exception {
475    
476                    Map<String, Object> freeMarkerContext = getFreeMarkerContext(
477                            pageContext, portletNamespace, namespace, element, locale);
478    
479                    freeMarkerContext.put("ignoreRepeatable", Boolean.TRUE);
480    
481                    Map<String, Object> fieldStructure =
482                            (Map<String, Object>)freeMarkerContext.get("fieldStructure");
483    
484                    DDMFieldsCounter ddmFieldsCounter = getFieldsCounter(
485                            pageContext, null, portletNamespace, namespace);
486    
487                    String name = element.attributeValue("name");
488    
489                    if ((field != null) && Validator.isNotNull(field.getValue())) {
490                            Fields fields = new Fields();
491    
492                            fields.put(field);
493    
494                            freeMarkerContext.put("fields", fields);
495                    }
496    
497                    fieldStructure.put("fieldNamespace", StringUtil.randomId());
498                    fieldStructure.put("valueIndex", ddmFieldsCounter.get(name));
499    
500                    List<Element> dynamicElementElements = element.elements(
501                            "dynamic-element");
502    
503                    StringBundler sb = new StringBundler(dynamicElementElements.size());
504    
505                    for (Element dynamicElementElement : dynamicElementElements) {
506                            String type = dynamicElementElement.attributeValue("type");
507    
508                            if (!type.equals("option")) {
509                                    sb.append(StringPool.BLANK);
510                            }
511                            else {
512                                    sb.append(
513                                            getSimpleFieldHTML(
514                                                    pageContext, dynamicElementElement, field,
515                                                    portletNamespace, namespace, mode, readOnly, locale));
516                            }
517                    }
518    
519                    fieldStructure.put("children", sb.toString());
520    
521                    return processFTL(
522                            pageContext, element, mode, readOnly, freeMarkerContext);
523            }
524    
525            @Override
526            public String getSimpleFieldHTMLByName(
527                            PageContext pageContext, long classNameId, long classPK,
528                            Field field, String portletNamespace, String namespace, String mode,
529                            boolean readOnly, Locale locale)
530                    throws Exception {
531    
532                    String xsd = getXSD(classNameId, classPK);
533    
534                    Document document = SAXReaderUtil.read(xsd);
535    
536                    String xPathExpression =
537                            "//dynamic-element[@name=".concat(
538                                    HtmlUtil.escapeXPathAttribute(field.getName())).concat("]");
539    
540                    XPath xPathSelector = SAXReaderUtil.createXPath(xPathExpression);
541    
542                    Node node = xPathSelector.selectSingleNode(document.getRootElement());
543    
544                    Element element = (Element)node.asXPathResult(node.getParent());
545    
546                    return getSimpleFieldHTML(
547                            pageContext, element, field, portletNamespace, namespace, mode,
548                            readOnly, locale);
549            }
550    
551            @Override
552            public String getXSD(long classNameId, long classPK)
553                    throws PortalException, SystemException {
554    
555                    if ((classNameId <= 0) || (classPK <= 0)) {
556                            return null;
557                    }
558    
559                    long ddmStructureClassNameId = PortalUtil.getClassNameId(
560                            DDMStructure.class);
561    
562                    long ddmTemplateClassNameId = PortalUtil.getClassNameId(
563                            DDMTemplate.class);
564    
565                    if (classNameId == ddmStructureClassNameId) {
566                            DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(
567                                    classPK);
568    
569                            return structure.getCompleteXsd();
570                    }
571                    else if (classNameId == ddmTemplateClassNameId) {
572                            DDMTemplate template = DDMTemplateLocalServiceUtil.getTemplate(
573                                    classPK);
574    
575                            return template.getScript();
576                    }
577    
578                    return null;
579            }
580    
581            protected JSONArray addStructureFieldAttributes(
582                    DDMStructure structure, JSONArray jsonArray) {
583    
584                    for (int i = 0; i < jsonArray.length(); i++) {
585                            JSONObject jsonObject = jsonArray.getJSONObject(i);
586    
587                            String fieldName = jsonObject.getString("name");
588    
589                            jsonObject.put(
590                                    "readOnlyAttributes",
591                                    getStructureFieldReadOnlyAttributes(structure, fieldName));
592                    }
593    
594                    return jsonArray;
595            }
596    
597            protected int countFieldRepetition(
598                    String[] fieldsDisplayValues, String parentFieldName, int offset) {
599    
600                    int total = 0;
601    
602                    String fieldName = fieldsDisplayValues[offset];
603    
604                    for (; offset < fieldsDisplayValues.length; offset++) {
605                            String fieldNameValue = fieldsDisplayValues[offset];
606    
607                            if (fieldNameValue.equals(fieldName)) {
608                                    total++;
609                            }
610    
611                            if (fieldNameValue.equals(parentFieldName)) {
612                                    break;
613                            }
614                    }
615    
616                    return total;
617            }
618    
619            protected Map<String, Object> getFieldContext(
620                    PageContext pageContext, String portletNamespace, String namespace,
621                    Element dynamicElementElement, Locale locale) {
622    
623                    Map<String, Map<String, Object>> fieldsContext = getFieldsContext(
624                            pageContext, portletNamespace, namespace);
625    
626                    String name = dynamicElementElement.attributeValue("name");
627    
628                    Map<String, Object> fieldContext = fieldsContext.get(name);
629    
630                    if (fieldContext != null) {
631                            return fieldContext;
632                    }
633    
634                    Document document = dynamicElementElement.getDocument();
635    
636                    HttpServletRequest request =
637                            (HttpServletRequest)pageContext.getRequest();
638    
639                    String defaultLanguageId = request.getParameter("defaultLanguageId");
640    
641                    if (Validator.isNull(defaultLanguageId)) {
642                            defaultLanguageId = GetterUtil.getString(
643                                    pageContext.getAttribute("contentDefaultLanguageId"));
644                    }
645    
646                    String editingLanguageId = LocaleUtil.toLanguageId(locale);
647    
648                    if (Validator.isNull(defaultLanguageId)) {
649                            defaultLanguageId = editingLanguageId;
650                    }
651    
652                    String structureLanguageId = editingLanguageId;
653    
654                    String[] availableLanguageIds =
655                            LocalizationUtil.getAvailableLanguageIds(document);
656    
657                    if (!ArrayUtil.contains(availableLanguageIds, editingLanguageId)) {
658                            structureLanguageId = LocalizationUtil.getDefaultLanguageId(
659                                    document);
660                    }
661    
662                    Element metadataElement =
663                            (Element)dynamicElementElement.selectSingleNode(
664                                    "meta-data[@locale='" + structureLanguageId + "']");
665    
666                    fieldContext = new HashMap<String, Object>();
667    
668                    if (metadataElement != null) {
669                            for (Element metadataEntry : metadataElement.elements()) {
670                                    fieldContext.put(
671                                            metadataEntry.attributeValue("name"),
672                                            metadataEntry.getText());
673                            }
674                    }
675    
676                    for (Attribute attribute : dynamicElementElement.attributes()) {
677                            fieldContext.put(attribute.getName(), attribute.getValue());
678                    }
679    
680                    boolean localizable = GetterUtil.getBoolean(
681                            dynamicElementElement.attributeValue("localizable"), true);
682    
683                    if (!localizable && !editingLanguageId.equals(defaultLanguageId)) {
684                            fieldContext.put("disabled", Boolean.TRUE.toString());
685                    }
686    
687                    fieldContext.put("fieldNamespace", StringUtil.randomId());
688    
689                    boolean checkRequired = GetterUtil.getBoolean(
690                            pageContext.getAttribute("checkRequired"), true);
691    
692                    if (!checkRequired) {
693                            fieldContext.put("required", Boolean.FALSE.toString());
694                    }
695    
696                    fieldsContext.put(name, fieldContext);
697    
698                    return fieldContext;
699            }
700    
701            protected int getFieldOffset(
702                    String[] fieldsDisplayValues, String name, int index) {
703    
704                    int offset = 0;
705    
706                    for (; offset < fieldsDisplayValues.length; offset++) {
707                            if (name.equals(fieldsDisplayValues[offset])) {
708                                    index--;
709    
710                                    if (index < 0) {
711                                            break;
712                                    }
713                            }
714                    }
715    
716                    return offset;
717            }
718    
719            protected String getFieldNamespace(
720                    String fieldDisplayValue, DDMFieldsCounter ddmFieldsCounter,
721                    int offset) {
722    
723                    String[] fieldsDisplayValues = StringUtil.split(fieldDisplayValue);
724    
725                    String fieldsDisplayValue = fieldsDisplayValues[offset];
726    
727                    return StringUtil.extractLast(
728                            fieldsDisplayValue, DDMImpl.INSTANCE_SEPARATOR);
729            }
730    
731            protected Map<String, Map<String, Object>> getFieldsContext(
732                    PageContext pageContext, String portletNamespace, String namespace) {
733    
734                    String fieldsContextKey =
735                            portletNamespace + namespace + "fieldsContext";
736    
737                    Map<String, Map<String, Object>> fieldsContext =
738                            (Map<String, Map<String, Object>>)pageContext.getAttribute(
739                                    fieldsContextKey);
740    
741                    if (fieldsContext == null) {
742                            fieldsContext = new HashMap<String, Map<String, Object>>();
743    
744                            pageContext.setAttribute(fieldsContextKey, fieldsContext);
745                    }
746    
747                    return fieldsContext;
748            }
749    
750            protected DDMFieldsCounter getFieldsCounter(
751                    PageContext pageContext, Fields fields, String portletNamespace,
752                    String namespace) {
753    
754                    String fieldsCounterKey = portletNamespace + namespace + "fieldsCount";
755    
756                    DDMFieldsCounter ddmFieldsCounter =
757                            (DDMFieldsCounter)pageContext.getAttribute(fieldsCounterKey);
758    
759                    if (ddmFieldsCounter == null) {
760                            ddmFieldsCounter = new DDMFieldsCounter();
761    
762                            pageContext.setAttribute(fieldsCounterKey, ddmFieldsCounter);
763                    }
764    
765                    return ddmFieldsCounter;
766            }
767    
768            protected String getFieldsDisplayValue(
769                    PageContext pageContext, Fields fields, String namespace) {
770    
771                    String defaultFieldsDisplayValue = null;
772    
773                    if (fields != null) {
774                            Field fieldsDisplayField = fields.get(DDMImpl.FIELDS_DISPLAY_NAME);
775    
776                            if (fieldsDisplayField != null) {
777                                    defaultFieldsDisplayValue =
778                                            (String)fieldsDisplayField.getValue();
779                            }
780                    }
781    
782                    return ParamUtil.getString(
783                            (HttpServletRequest)pageContext.getRequest(),
784                            namespace + DDMImpl.FIELDS_DISPLAY_NAME, defaultFieldsDisplayValue);
785            }
786    
787            protected String[] getFieldsDisplayValues(String fieldDisplayValue) {
788                    List<String> fieldsDisplayValues = new ArrayList<String>();
789    
790                    for (String value : StringUtil.split(fieldDisplayValue)) {
791                            String fieldName = StringUtil.extractFirst(
792                                    value, DDMImpl.INSTANCE_SEPARATOR);
793    
794                            fieldsDisplayValues.add(fieldName);
795                    }
796    
797                    return fieldsDisplayValues.toArray(
798                            new String[fieldsDisplayValues.size()]);
799            }
800    
801            protected Map<String, Object> getFreeMarkerContext(
802                    PageContext pageContext, String portletNamespace, String namespace,
803                    Element dynamicElementElement, Locale locale) {
804    
805                    Map<String, Object> freeMarkerContext = new HashMap<String, Object>();
806    
807                    Map<String, Object> fieldContext = getFieldContext(
808                            pageContext, portletNamespace, namespace, dynamicElementElement,
809                            locale);
810    
811                    Map<String, Object> parentFieldContext = new HashMap<String, Object>();
812    
813                    Element parentElement = dynamicElementElement.getParent();
814    
815                    if (parentElement != null) {
816                            parentFieldContext = getFieldContext(
817                                    pageContext, portletNamespace, namespace, parentElement,
818                                    locale);
819                    }
820    
821                    freeMarkerContext.put("fieldStructure", fieldContext);
822                    freeMarkerContext.put("namespace", namespace);
823                    freeMarkerContext.put("parentFieldStructure", parentFieldContext);
824                    freeMarkerContext.put("portletNamespace", portletNamespace);
825                    freeMarkerContext.put(
826                            "requestedLanguageDir", LanguageUtil.get(locale, "lang.dir"));
827                    freeMarkerContext.put("requestedLocale", locale);
828    
829                    return freeMarkerContext;
830            }
831    
832            protected URL getResource(String name) {
833                    Class<?> clazz = getClass();
834    
835                    ClassLoader classLoader = clazz.getClassLoader();
836    
837                    return classLoader.getResource(name);
838            }
839    
840            protected JSONArray getStructureFieldReadOnlyAttributes(
841                    DDMStructure structure, String fieldName) {
842    
843                    JSONArray jsonArray = JSONFactoryUtil.createJSONArray();
844    
845                    try {
846                            if (DDMStorageLinkLocalServiceUtil.getStructureStorageLinksCount(
847                                            structure.getStructureId()) > 0) {
848    
849                                    jsonArray.put("name");
850                            }
851                    }
852                    catch (Exception e) {
853                    }
854    
855                    return jsonArray;
856            }
857    
858            protected String processFTL(
859                            PageContext pageContext, Element element, String mode,
860                            boolean readOnly, Map<String, Object> freeMarkerContext)
861                    throws Exception {
862    
863                    String fieldNamespace = element.attributeValue(
864                            "fieldNamespace", _DEFAULT_NAMESPACE);
865    
866                    TemplateResource templateResource = _defaultTemplateResource;
867    
868                    Map<String, Object> fieldStructure =
869                            (Map<String, Object>)freeMarkerContext.get("fieldStructure");
870    
871                    boolean fieldReadOnly = GetterUtil.getBoolean(
872                            fieldStructure.get("readOnly"));
873    
874                    if ((fieldReadOnly && Validator.isNotNull(mode) &&
875                             StringUtil.equalsIgnoreCase(
876                                    mode, DDMTemplateConstants.TEMPLATE_MODE_EDIT)) ||
877                            readOnly) {
878    
879                            fieldNamespace = _DEFAULT_READ_ONLY_NAMESPACE;
880    
881                            templateResource = _defaultReadOnlyTemplateResource;
882                    }
883    
884                    String type = element.attributeValue("type");
885    
886                    String templateName = StringUtil.replaceFirst(
887                            type, fieldNamespace.concat(StringPool.DASH), StringPool.BLANK);
888    
889                    StringBundler resourcePath = new StringBundler(5);
890    
891                    resourcePath.append(_TPL_PATH);
892                    resourcePath.append(StringUtil.toLowerCase(fieldNamespace));
893                    resourcePath.append(CharPool.SLASH);
894                    resourcePath.append(templateName);
895                    resourcePath.append(_TPL_EXT);
896    
897                    String resource = resourcePath.toString();
898    
899                    URL url = getResource(resource);
900    
901                    if (url != null) {
902                            templateResource = new URLTemplateResource(resource, url);
903                    }
904    
905                    if (templateResource == null) {
906                            throw new Exception("Unable to load template resource " + resource);
907                    }
908    
909                    Template template = TemplateManagerUtil.getTemplate(
910                            TemplateConstants.LANG_TYPE_FTL, templateResource, false);
911    
912                    for (Map.Entry<String, Object> entry : freeMarkerContext.entrySet()) {
913                            template.put(entry.getKey(), entry.getValue());
914                    }
915    
916                    return processFTL(pageContext, template);
917            }
918    
919            /**
920             * @see com.liferay.taglib.util.ThemeUtil#includeFTL
921             */
922            protected String processFTL(PageContext pageContext, Template template)
923                    throws Exception {
924    
925                    HttpServletRequest request =
926                            (HttpServletRequest)pageContext.getRequest();
927    
928                    // FreeMarker variables
929    
930                    template.prepare(request);
931    
932                    // Tag libraries
933    
934                    HttpServletResponse response =
935                            (HttpServletResponse)pageContext.getResponse();
936    
937                    Writer writer = new UnsyncStringWriter();
938    
939                    // Portal JSP tag library factory
940    
941                    TemplateHashModel portalTaglib =
942                            FreeMarkerTaglibFactoryUtil.createTaglibFactory(
943                                    pageContext.getServletContext());
944    
945                    template.put("PortalJspTagLibs", portalTaglib);
946    
947                    // FreeMarker JSP tag library support
948    
949                    final Servlet servlet = (Servlet)pageContext.getPage();
950    
951                    GenericServlet genericServlet = null;
952    
953                    if (servlet instanceof GenericServlet) {
954                            genericServlet = (GenericServlet)servlet;
955                    }
956                    else {
957                            genericServlet = new GenericServlet() {
958    
959                                    @Override
960                                    public void service(
961                                                    ServletRequest servletRequest,
962                                                    ServletResponse servletResponse)
963                                            throws IOException, ServletException {
964    
965                                            servlet.service(servletRequest, servletResponse);
966                                    }
967    
968                            };
969    
970                            genericServlet.init(pageContext.getServletConfig());
971                    }
972    
973                    ServletContextHashModel servletContextHashModel =
974                            new ServletContextHashModel(
975                                    genericServlet, ObjectWrapper.DEFAULT_WRAPPER);
976    
977                    template.put("Application", servletContextHashModel);
978    
979                    HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
980                            request, response, ObjectWrapper.DEFAULT_WRAPPER);
981    
982                    template.put("Request", httpRequestHashModel);
983    
984                    // Merge templates
985    
986                    template.processTemplate(writer);
987    
988                    return writer.toString();
989            }
990    
991            protected void putMetadataValue(
992                    JSONObject jsonObject, String attributeName, String attributeValue,
993                    String type) {
994    
995                    if (type.equals(DDMImpl.TYPE_RADIO) ||
996                            type.equals(DDMImpl.TYPE_SELECT)) {
997    
998                            if (attributeName.equals("predefinedValue")) {
999                                    try {
1000                                            jsonObject.put(
1001                                                    attributeName,
1002                                                    JSONFactoryUtil.createJSONArray(attributeValue));
1003                                    }
1004                                    catch (Exception e) {
1005                                    }
1006    
1007                                    return;
1008                            }
1009                    }
1010    
1011                    jsonObject.put(attributeName, attributeValue);
1012            }
1013    
1014            private static final String _DEFAULT_NAMESPACE = "alloy";
1015    
1016            private static final String _DEFAULT_READ_ONLY_NAMESPACE = "readonly";
1017    
1018            private static final String _TPL_EXT = ".ftl";
1019    
1020            private static final String _TPL_PATH =
1021                    "com/liferay/portlet/dynamicdatamapping/dependencies/";
1022    
1023            private TemplateResource _defaultReadOnlyTemplateResource;
1024            private TemplateResource _defaultTemplateResource;
1025    
1026    }