com.liferay.dynamic.data.mapping.internal.util.DDMImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.dynamic.data.mapping.internal.util.DDMImpl.java

Source

/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.dynamic.data.mapping.internal.util;

import com.liferay.document.library.kernel.service.DLAppLocalService;
import com.liferay.document.library.kernel.util.DLUtil;
import com.liferay.dynamic.data.mapping.exception.StructureDefinitionException;
import com.liferay.dynamic.data.mapping.io.DDMFormJSONDeserializer;
import com.liferay.dynamic.data.mapping.io.DDMFormJSONSerializer;
import com.liferay.dynamic.data.mapping.io.DDMFormValuesJSONDeserializer;
import com.liferay.dynamic.data.mapping.io.DDMFormValuesJSONSerializer;
import com.liferay.dynamic.data.mapping.model.DDMForm;
import com.liferay.dynamic.data.mapping.model.DDMFormField;
import com.liferay.dynamic.data.mapping.model.DDMFormFieldOptions;
import com.liferay.dynamic.data.mapping.model.DDMFormLayout;
import com.liferay.dynamic.data.mapping.model.DDMFormLayoutColumn;
import com.liferay.dynamic.data.mapping.model.DDMFormLayoutPage;
import com.liferay.dynamic.data.mapping.model.DDMFormLayoutRow;
import com.liferay.dynamic.data.mapping.model.DDMStructure;
import com.liferay.dynamic.data.mapping.model.DDMStructureVersion;
import com.liferay.dynamic.data.mapping.model.DDMTemplate;
import com.liferay.dynamic.data.mapping.model.LocalizedValue;
import com.liferay.dynamic.data.mapping.service.DDMStructureLocalServiceUtil;
import com.liferay.dynamic.data.mapping.service.DDMTemplateLocalServiceUtil;
import com.liferay.dynamic.data.mapping.storage.DDMFormValues;
import com.liferay.dynamic.data.mapping.storage.Field;
import com.liferay.dynamic.data.mapping.storage.FieldConstants;
import com.liferay.dynamic.data.mapping.storage.Fields;
import com.liferay.dynamic.data.mapping.util.DDM;
import com.liferay.dynamic.data.mapping.util.DDMFormValuesToFieldsConverter;
import com.liferay.dynamic.data.mapping.util.FieldsToDDMFormValuesConverter;
import com.liferay.dynamic.data.mapping.util.comparator.StructureIdComparator;
import com.liferay.dynamic.data.mapping.util.comparator.StructureModifiedDateComparator;
import com.liferay.dynamic.data.mapping.util.comparator.TemplateIdComparator;
import com.liferay.dynamic.data.mapping.util.comparator.TemplateModifiedDateComparator;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.json.JSONArray;
import com.liferay.portal.kernel.json.JSONFactoryUtil;
import com.liferay.portal.kernel.json.JSONObject;
import com.liferay.portal.kernel.language.LanguageUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.Image;
import com.liferay.portal.kernel.model.Layout;
import com.liferay.portal.kernel.repository.model.FileEntry;
import com.liferay.portal.kernel.security.pacl.DoPrivileged;
import com.liferay.portal.kernel.service.ImageLocalService;
import com.liferay.portal.kernel.service.LayoutLocalService;
import com.liferay.portal.kernel.service.ServiceContext;
import com.liferay.portal.kernel.theme.ThemeDisplay;
import com.liferay.portal.kernel.upload.UploadRequest;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
import com.liferay.portal.kernel.util.DateUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.Http;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.LocaleUtil;
import com.liferay.portal.kernel.util.OrderByComparator;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.Portal;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.TimeZoneUtil;
import com.liferay.portal.kernel.util.UnicodeFormatter;
import com.liferay.portal.kernel.util.Validator;

import java.io.File;
import java.io.Serializable;

import java.text.DateFormat;
import java.text.ParseException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.portlet.PortletRequest;

import javax.servlet.http.HttpServletRequest;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
 * @author Eduardo Lundgren
 * @author Brian Wing Shun Chan
 * @author Eduardo Garcia
 * @author Marcellus Tavares
 */
@Component(immediate = true)
@DoPrivileged
public class DDMImpl implements DDM {

    public static final String FIELDS_DISPLAY_NAME = "_fieldsDisplay";

    public static final String INSTANCE_SEPARATOR = "_INSTANCE_";

    public static final String TYPE_CHECKBOX = "checkbox";

    public static final String TYPE_DDM_DATE = "ddm-date";

    public static final String TYPE_DDM_DOCUMENTLIBRARY = "ddm-documentlibrary";

    public static final String TYPE_DDM_IMAGE = "ddm-image";

    public static final String TYPE_DDM_LINK_TO_PAGE = "ddm-link-to-page";

    public static final String TYPE_DDM_TEXT_HTML = "ddm-text-html";

    public static final String TYPE_RADIO = "radio";

    public static final String TYPE_SELECT = "select";

    @Override
    public DDMForm getDDMForm(long classNameId, long classPK) throws PortalException {

        if ((classNameId <= 0) || (classPK <= 0)) {
            return null;
        }

        long ddmStructureClassNameId = _portal.getClassNameId(DDMStructure.class);
        long ddmTemplateClassNameId = _portal.getClassNameId(DDMTemplate.class);

        if (classNameId == ddmStructureClassNameId) {
            DDMStructure structure = DDMStructureLocalServiceUtil.getStructure(classPK);

            DDMForm ddmForm = structure.getFullHierarchyDDMForm();

            return ddmForm;
        } else if (classNameId == ddmTemplateClassNameId) {
            DDMTemplate template = DDMTemplateLocalServiceUtil.getTemplate(classPK);

            return _ddmFormJSONDeserializer.deserialize(template.getScript());
        }

        return null;
    }

    @Override
    public DDMForm getDDMForm(PortletRequest portletRequest) throws PortalException {

        try {
            String definition = ParamUtil.getString(portletRequest, "definition");

            return _ddmFormJSONDeserializer.deserialize(definition);
        } catch (PortalException pe) {
            throw new StructureDefinitionException(pe);
        }
    }

    @Override
    public DDMForm getDDMForm(String serializedJSONDDMForm) throws PortalException {

        return _ddmFormJSONDeserializer.deserialize(serializedJSONDDMForm);
    }

    @Override
    public JSONArray getDDMFormFieldsJSONArray(DDMStructure ddmStructure, String script) {

        DDMForm ddmForm = null;

        if (ddmStructure != null) {
            ddmForm = ddmStructure.getFullHierarchyDDMForm();
        }

        return getDDMFormFieldsJSONArray(ddmForm, script);
    }

    @Override
    public JSONArray getDDMFormFieldsJSONArray(DDMStructureVersion ddmStructureVersion, String script) {

        DDMForm ddmForm = null;

        if (ddmStructureVersion != null) {
            ddmForm = ddmStructureVersion.getDDMForm();
        }

        return getDDMFormFieldsJSONArray(ddmForm, script);
    }

    @Override
    public String getDDMFormJSONString(DDMForm ddmForm) {
        return _ddmFormJSONSerializer.serialize(ddmForm);
    }

    @Override
    public DDMFormValues getDDMFormValues(DDMForm ddmForm, String serializedJSONDDMFormValues)
            throws PortalException {

        return _ddmFormValuesJSONDeserializer.deserialize(ddmForm, serializedJSONDDMFormValues);
    }

    @Override
    public DDMFormValues getDDMFormValues(long ddmStructureId, String fieldNamespace, ServiceContext serviceContext)
            throws PortalException {

        DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(ddmStructureId);

        Fields fields = getFields(ddmStructure.getStructureId(), fieldNamespace, serviceContext);

        return _fieldsToDDMFormValuesConverter.convert(ddmStructure, fields);
    }

    @Override
    public String getDDMFormValuesJSONString(DDMFormValues ddmFormValues) {
        return _ddmFormValuesJSONSerializer.serialize(ddmFormValues);
    }

    @Override
    public DDMFormLayout getDefaultDDMFormLayout(DDMForm ddmForm) {
        DDMFormLayout ddmFormLayout = new DDMFormLayout();

        Locale defaultLocale = ddmForm.getDefaultLocale();

        ddmFormLayout.setDefaultLocale(defaultLocale);

        ddmFormLayout.setPaginationMode(DDMFormLayout.SINGLE_PAGE_MODE);

        DDMFormLayoutPage ddmFormLayoutPage = new DDMFormLayoutPage();

        LocalizedValue title = getDefaultDDMFormPageTitle(defaultLocale);

        ddmFormLayoutPage.setTitle(title);

        for (DDMFormField ddmFormField : ddmForm.getDDMFormFields()) {
            ddmFormLayoutPage.addDDMFormLayoutRow(getDefaultDDMFormLayoutRow(ddmFormField));
        }

        ddmFormLayout.addDDMFormLayoutPage(ddmFormLayoutPage);

        return ddmFormLayout;
    }

    @Override
    public Serializable getDisplayFieldValue(ThemeDisplay themeDisplay, Serializable fieldValue, String type)
            throws Exception {

        if (type.equals(DDMImpl.TYPE_DDM_DATE)) {
            fieldValue = DateUtil.formatDate("yyyy-MM-dd", fieldValue.toString(), themeDisplay.getLocale());
        } else if (type.equals(DDMImpl.TYPE_CHECKBOX)) {
            Boolean valueBoolean = (Boolean) fieldValue;

            if (valueBoolean) {
                fieldValue = LanguageUtil.get(themeDisplay.getLocale(), "yes");
            } else {
                fieldValue = LanguageUtil.get(themeDisplay.getLocale(), "no");
            }
        } else if (type.equals(DDMImpl.TYPE_DDM_DOCUMENTLIBRARY)) {
            if (Validator.isNull(fieldValue)) {
                return StringPool.BLANK;
            }

            String valueString = String.valueOf(fieldValue);

            JSONObject jsonObject = JSONFactoryUtil.createJSONObject(valueString);

            String uuid = jsonObject.getString("uuid");
            long groupId = jsonObject.getLong("groupId");

            FileEntry fileEntry = _dlAppLocalService.getFileEntryByUuidAndGroupId(uuid, groupId);

            fieldValue = DLUtil.getPreviewURL(fileEntry, fileEntry.getFileVersion(), null, StringPool.BLANK, false,
                    true);
        } else if (type.equals(DDMImpl.TYPE_DDM_LINK_TO_PAGE)) {
            if (Validator.isNull(fieldValue)) {
                return StringPool.BLANK;
            }

            String valueString = String.valueOf(fieldValue);

            JSONObject jsonObject = JSONFactoryUtil.createJSONObject(valueString);

            long groupId = jsonObject.getLong("groupId");
            boolean privateLayout = jsonObject.getBoolean("privateLayout");
            long layoutId = jsonObject.getLong("layoutId");

            Layout layout = _layoutLocalService.getLayout(groupId, privateLayout, layoutId);

            fieldValue = _portal.getLayoutFriendlyURL(layout, themeDisplay);
        } else if (type.equals(DDMImpl.TYPE_SELECT)) {
            String valueString = String.valueOf(fieldValue);

            JSONArray jsonArray = JSONFactoryUtil.createJSONArray(valueString);

            String[] stringArray = ArrayUtil.toStringArray(jsonArray);

            fieldValue = stringArray[0];
        }

        return fieldValue;
    }

    @Override
    public Fields getFields(long ddmStructureId, DDMFormValues ddmFormValues) throws PortalException {

        DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(ddmStructureId);

        return _ddmFormValuesToFieldsConverter.convert(ddmStructure, ddmFormValues);
    }

    @Override
    public Fields getFields(long ddmStructureId, long ddmTemplateId, ServiceContext serviceContext)
            throws PortalException {

        return getFields(ddmStructureId, ddmTemplateId, StringPool.BLANK, serviceContext);
    }

    @Override
    public Fields getFields(long ddmStructureId, long ddmTemplateId, String fieldNamespace,
            ServiceContext serviceContext) throws PortalException {

        DDMStructure ddmStructure = getDDMStructure(ddmStructureId, ddmTemplateId);

        Set<String> fieldNames = ddmStructure.getFieldNames();

        boolean translating = true;

        String defaultLanguageId = (String) serviceContext.getAttribute("defaultLanguageId");
        String toLanguageId = (String) serviceContext.getAttribute("toLanguageId");

        if (Validator.isNull(toLanguageId) || Objects.equals(defaultLanguageId, toLanguageId)) {

            translating = false;
        }

        Fields fields = new Fields();

        for (String fieldName : fieldNames) {
            boolean localizable = GetterUtil.getBoolean(ddmStructure.getFieldProperty(fieldName, "localizable"),
                    true);

            if (!localizable && translating && !fieldName.startsWith(StringPool.UNDERLINE)) {

                continue;
            }

            List<Serializable> fieldValues = getFieldValues(ddmStructure, fieldName, fieldNamespace,
                    serviceContext);

            if ((fieldValues == null) || fieldValues.isEmpty()) {
                continue;
            }

            Field field = createField(ddmStructure, fieldName, fieldValues, serviceContext);

            fields.put(field);
        }

        return fields;
    }

    @Override
    public Fields getFields(long ddmStructureId, ServiceContext serviceContext) throws PortalException {

        String serializedDDMFormValues = GetterUtil.getString(serviceContext.getAttribute("ddmFormValues"));

        if (Validator.isNotNull(serializedDDMFormValues)) {
            return getFields(ddmStructureId, serializedDDMFormValues);
        }

        return getFields(ddmStructureId, 0, serviceContext);
    }

    @Override
    public Fields getFields(long ddmStructureId, String fieldNamespace, ServiceContext serviceContext)
            throws PortalException {

        String serializedDDMFormValues = GetterUtil
                .getString(serviceContext.getAttribute(fieldNamespace + "ddmFormValues"));

        if (Validator.isNotNull(serializedDDMFormValues)) {
            return getFields(ddmStructureId, serializedDDMFormValues);
        }

        return getFields(ddmStructureId, 0, fieldNamespace, serviceContext);
    }

    @Override
    public Serializable getIndexedFieldValue(Serializable fieldValue, String type) throws Exception {

        if (fieldValue instanceof Date) {
            Date valueDate = (Date) fieldValue;

            DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat("yyyyMMddHHmmss");

            fieldValue = dateFormat.format(valueDate);
        } else if (type.equals(DDMImpl.TYPE_SELECT)) {
            String valueString = (String) fieldValue;

            JSONArray jsonArray = JSONFactoryUtil.createJSONArray(valueString);

            String[] stringArray = ArrayUtil.toStringArray(jsonArray);

            fieldValue = stringArray[0];
        }

        return fieldValue;
    }

    @Override
    public OrderByComparator<DDMStructure> getStructureOrderByComparator(String orderByCol, String orderByType) {

        boolean orderByAsc = false;

        if (orderByType.equals("asc")) {
            orderByAsc = true;
        }

        OrderByComparator<DDMStructure> orderByComparator = null;

        if (orderByCol.equals("id")) {
            orderByComparator = new StructureIdComparator(orderByAsc);
        } else if (orderByCol.equals("modified-date")) {
            orderByComparator = new StructureModifiedDateComparator(orderByAsc);
        }

        return orderByComparator;
    }

    @Override
    public OrderByComparator<DDMTemplate> getTemplateOrderByComparator(String orderByCol, String orderByType) {

        boolean orderByAsc = false;

        if (orderByType.equals("asc")) {
            orderByAsc = true;
        }

        OrderByComparator<DDMTemplate> orderByComparator = null;

        if (orderByCol.equals("id")) {
            orderByComparator = new TemplateIdComparator(orderByAsc);
        } else if (orderByCol.equals("modified-date")) {
            orderByComparator = new TemplateModifiedDateComparator(orderByAsc);
        }

        return orderByComparator;
    }

    @Override
    public Fields mergeFields(Fields newFields, Fields existingFields) {
        String[] newFieldsDisplayValues = splitFieldsDisplayValue(newFields.get(DDMImpl.FIELDS_DISPLAY_NAME));

        String[] existingFieldsDisplayValues = splitFieldsDisplayValue(
                existingFields.get(DDMImpl.FIELDS_DISPLAY_NAME));

        Iterator<Field> itr = newFields.iterator(true);

        while (itr.hasNext()) {
            Field newField = itr.next();

            Field existingField = existingFields.get(newField.getName());

            if (existingField == null) {
                existingFields.put(newField);

                continue;
            }

            if (newField.isPrivate()) {
                String[] existingFieldValues = splitFieldsDisplayValue(existingField);

                String[] newFieldValues = splitFieldsDisplayValue(newField);

                if (newFieldValues.length > existingFieldValues.length) {
                    existingFields.put(newField);
                }

                continue;
            }

            existingField.setDefaultLocale(newField.getDefaultLocale());

            Map<Locale, List<Serializable>> mergedFieldValuesMap = getMergedFieldValuesMap(newField,
                    newFieldsDisplayValues, existingField, existingFieldsDisplayValues);

            existingField.setValuesMap(mergedFieldValuesMap);
        }

        return existingFields;
    }

    @Override
    public DDMForm updateDDMFormDefaultLocale(DDMForm ddmForm, Locale newDefaultLocale) {

        DDMForm ddmFormCopy = new DDMForm(ddmForm);

        Locale defautLocale = ddmForm.getDefaultLocale();

        if (defautLocale.equals(newDefaultLocale)) {
            return ddmFormCopy;
        }

        ddmFormCopy.addAvailableLocale(newDefaultLocale);
        ddmFormCopy.setDefaultLocale(newDefaultLocale);

        updateDDMFormFieldsDefaultLocale(ddmFormCopy.getDDMFormFields(), newDefaultLocale);

        return ddmFormCopy;
    }

    protected void addDDMFormFieldLocalizedProperties(JSONObject jsonObject, DDMFormField ddmFormField,
            Locale locale, Locale defaultLocale) {

        addDDMFormFieldLocalizedProperty(jsonObject, "label", ddmFormField.getLabel(), locale, defaultLocale,
                ddmFormField.getType());
        addDDMFormFieldLocalizedProperty(jsonObject, "predefinedValue", ddmFormField.getPredefinedValue(), locale,
                defaultLocale, ddmFormField.getType());
        addDDMFormFieldLocalizedProperty(jsonObject, "tip", ddmFormField.getTip(), locale, defaultLocale,
                ddmFormField.getType());
    }

    protected void addDDMFormFieldLocalizedProperty(JSONObject jsonObject, String propertyName,
            LocalizedValue localizedValue, Locale locale, Locale defaultLocale, String type) {

        String propertyValue = localizedValue.getString(locale);

        if (Validator.isNull(propertyValue)) {
            propertyValue = localizedValue.getString(defaultLocale);
        }

        if (type.equals(DDMImpl.TYPE_SELECT)) {
            if (propertyName.equals("predefinedValue")) {
                try {
                    jsonObject.put(propertyName, JSONFactoryUtil.createJSONArray(propertyValue));
                } catch (Exception e) {
                }

                return;
            }
        }

        jsonObject.put(propertyName, propertyValue);
    }

    protected void addDDMFormFieldOptions(JSONObject jsonObject, DDMFormField ddmFormField,
            Set<Locale> availableLocales, Locale defaultLocale) {

        String type = ddmFormField.getType();

        if (!(type.equals(DDMImpl.TYPE_RADIO) || type.equals(DDMImpl.TYPE_SELECT))) {

            return;
        }

        String fieldName = ddmFormField.getName();

        JSONArray jsonArray = JSONFactoryUtil.createJSONArray();

        DDMFormFieldOptions ddmFormFieldOptions = ddmFormField.getDDMFormFieldOptions();

        for (String optionValue : ddmFormFieldOptions.getOptionsValues()) {
            JSONObject optionJSONObject = JSONFactoryUtil.createJSONObject();

            String name = fieldName.concat(StringUtil.randomString());

            optionJSONObject.put("id", name);
            optionJSONObject.put("name", name);

            optionJSONObject.put("type", "option");
            optionJSONObject.put("value", optionValue);

            addDDMFormFieldLocalizedProperty(optionJSONObject, "label",
                    ddmFormFieldOptions.getOptionLabels(optionValue), defaultLocale, defaultLocale, "option");

            JSONObject localizationMapJSONObject = JSONFactoryUtil.createJSONObject();

            for (Locale availableLocale : availableLocales) {
                JSONObject localeMap = JSONFactoryUtil.createJSONObject();

                addDDMFormFieldLocalizedProperty(localeMap, "label",
                        ddmFormFieldOptions.getOptionLabels(optionValue), availableLocale, defaultLocale, "option");

                localizationMapJSONObject.put(LocaleUtil.toLanguageId(availableLocale), localeMap);
            }

            optionJSONObject.put("localizationMap", localizationMapJSONObject);

            jsonArray.put(optionJSONObject);
        }

        jsonObject.put("options", jsonArray);
    }

    protected int countFieldRepetition(String[] fieldsDisplayValues, String fieldName) {

        int count = 0;

        for (String fieldsDisplayValue : fieldsDisplayValues) {
            String prefix = StringUtil.extractFirst(fieldsDisplayValue, INSTANCE_SEPARATOR);

            if (prefix.equals(fieldName)) {
                count++;
            }
        }

        return count;
    }

    protected Field createField(DDMStructure ddmStructure, String fieldName, List<Serializable> fieldValues,
            ServiceContext serviceContext) {

        Field field = new Field();

        field.setDDMStructureId(ddmStructure.getStructureId());

        String languageId = GetterUtil.getString(serviceContext.getAttribute("languageId"),
                serviceContext.getLanguageId());

        Locale locale = LocaleUtil.fromLanguageId(languageId);

        String defaultLanguageId = GetterUtil.getString(serviceContext.getAttribute("defaultLanguageId"));

        Locale defaultLocale = LocaleUtil.fromLanguageId(defaultLanguageId);

        if (fieldName.startsWith(StringPool.UNDERLINE)) {
            locale = LocaleUtil.getSiteDefault();

            defaultLocale = LocaleUtil.getSiteDefault();
        }

        field.setDefaultLocale(defaultLocale);

        field.setName(fieldName);
        field.setValues(locale, fieldValues);

        return field;
    }

    protected JSONArray getDDMFormFieldsJSONArray(DDMForm ddmForm, String script) {

        JSONArray ddmFormFieldsJSONArray = null;

        if (ddmForm != null) {
            ddmFormFieldsJSONArray = getDDMFormFieldsJSONArray(ddmForm.getDDMFormFields(),
                    ddmForm.getAvailableLocales(), ddmForm.getDefaultLocale());
        } else if (Validator.isNotNull(script)) {
            try {
                DDMForm scriptDDMForm = _ddmFormJSONDeserializer.deserialize(script);

                ddmFormFieldsJSONArray = getDDMFormFieldsJSONArray(scriptDDMForm.getDDMFormFields(),
                        scriptDDMForm.getAvailableLocales(), scriptDDMForm.getDefaultLocale());
            } catch (PortalException pe) {
                if (_log.isWarnEnabled()) {
                    _log.warn("Unable to deserialize script", pe);
                }
            }
        }

        return ddmFormFieldsJSONArray;
    }

    protected JSONArray getDDMFormFieldsJSONArray(List<DDMFormField> ddmFormFields, Set<Locale> availableLocales,
            Locale defaultLocale) {

        JSONArray ddmFormFieldsJSONArray = JSONFactoryUtil.createJSONArray();

        for (DDMFormField ddmFormField : ddmFormFields) {
            JSONObject jsonObject = JSONFactoryUtil.createJSONObject();

            jsonObject.put("dataType", ddmFormField.getDataType());
            jsonObject.put("id", ddmFormField.getName());
            jsonObject.put("indexType", ddmFormField.getIndexType());
            jsonObject.put("localizable", ddmFormField.isLocalizable());
            jsonObject.put("multiple", ddmFormField.isMultiple());
            jsonObject.put("name", ddmFormField.getName());
            jsonObject.put("repeatable", ddmFormField.isRepeatable());
            jsonObject.put("required", ddmFormField.isRequired());
            jsonObject.put("showLabel", ddmFormField.isShowLabel());
            jsonObject.put("type", ddmFormField.getType());

            addDDMFormFieldLocalizedProperties(jsonObject, ddmFormField, defaultLocale, defaultLocale);

            addDDMFormFieldOptions(jsonObject, ddmFormField, availableLocales, defaultLocale);

            JSONObject localizationMapJSONObject = JSONFactoryUtil.createJSONObject();

            for (Locale availableLocale : availableLocales) {
                JSONObject localeMap = JSONFactoryUtil.createJSONObject();

                addDDMFormFieldLocalizedProperties(localeMap, ddmFormField, availableLocale, defaultLocale);

                localizationMapJSONObject.put(LocaleUtil.toLanguageId(availableLocale), localeMap);
            }

            jsonObject.put("localizationMap", localizationMapJSONObject);

            jsonObject.put("fields", getDDMFormFieldsJSONArray(ddmFormField.getNestedDDMFormFields(),
                    availableLocales, defaultLocale));

            ddmFormFieldsJSONArray.put(jsonObject);
        }

        return ddmFormFieldsJSONArray;
    }

    protected DDMStructure getDDMStructure(long ddmStructureId, long ddmTemplateId) throws PortalException {

        DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(ddmStructureId);

        DDMTemplate ddmTemplate = DDMTemplateLocalServiceUtil.fetchDDMTemplate(ddmTemplateId);

        if (ddmTemplate != null) {

            // Clone ddmStructure to make sure changes are never persisted

            ddmStructure = (DDMStructure) ddmStructure.clone();

            ddmStructure.setDefinition(ddmTemplate.getScript());
        }

        return ddmStructure;
    }

    protected DDMFormLayoutRow getDefaultDDMFormLayoutRow(DDMFormField ddmFormField) {

        DDMFormLayoutRow ddmFormLayoutRow = new DDMFormLayoutRow();

        ddmFormLayoutRow
                .addDDMFormLayoutColumn(new DDMFormLayoutColumn(DDMFormLayoutColumn.FULL, ddmFormField.getName()));

        return ddmFormLayoutRow;
    }

    protected LocalizedValue getDefaultDDMFormPageTitle(Locale defaultLocale) {
        LocalizedValue title = new LocalizedValue(defaultLocale);

        title.addString(defaultLocale, StringPool.BLANK);

        return title;
    }

    protected int getExistingFieldValueIndex(String[] newFieldsDisplayValues, String[] existingFieldsDisplayValues,
            String fieldName, int index) {

        String instanceId = getFieldIntanceId(newFieldsDisplayValues, fieldName, index);

        return getFieldValueIndex(existingFieldsDisplayValues, fieldName, instanceId);
    }

    protected String getFieldIntanceId(String[] fieldsDisplayValues, String fieldName, int index) {

        String prefix = fieldName.concat(INSTANCE_SEPARATOR);

        for (String fieldsDisplayValue : fieldsDisplayValues) {
            if (fieldsDisplayValue.startsWith(prefix)) {
                index--;

                if (index < 0) {
                    return StringUtil.extractLast(fieldsDisplayValue, DDMImpl.INSTANCE_SEPARATOR);
                }
            }
        }

        return null;
    }

    protected List<String> getFieldNames(String fieldNamespace, String fieldName, ServiceContext serviceContext) {

        String[] fieldsDisplayValues = StringUtil
                .split((String) serviceContext.getAttribute(fieldNamespace + FIELDS_DISPLAY_NAME));

        List<String> privateFieldNames = ListUtil.fromArray(new String[] { FIELDS_DISPLAY_NAME });

        List<String> fieldNames = new ArrayList<>();

        if ((fieldsDisplayValues.length == 0) || privateFieldNames.contains(fieldName)) {

            fieldNames.add(fieldNamespace + fieldName);
        } else {
            for (String namespacedFieldName : fieldsDisplayValues) {
                String fieldNameValue = StringUtil.extractFirst(namespacedFieldName, INSTANCE_SEPARATOR);

                if (fieldNameValue.equals(fieldName)) {
                    fieldNames.add(fieldNamespace + namespacedFieldName);
                }
            }
        }

        return fieldNames;
    }

    protected Fields getFields(long ddmStructureId, String serializedDDMFormValues) throws PortalException {

        DDMStructure ddmStructure = DDMStructureLocalServiceUtil.getStructure(ddmStructureId);

        DDMFormValues ddmFormValues = getDDMFormValues(ddmStructure.getFullHierarchyDDMForm(),
                serializedDDMFormValues);

        return _ddmFormValuesToFieldsConverter.convert(ddmStructure, ddmFormValues);
    }

    protected int getFieldValueIndex(String[] fieldsDisplayValues, String fieldName, String instanceId) {

        if (Validator.isNull(instanceId)) {
            return -1;
        }

        int offset = 0;

        String prefix = fieldName.concat(INSTANCE_SEPARATOR);

        for (String fieldsDisplayValue : fieldsDisplayValues) {
            if (fieldsDisplayValue.startsWith(prefix)) {
                String fieldIstanceId = StringUtil.extractLast(fieldsDisplayValue, DDMImpl.INSTANCE_SEPARATOR);

                if (fieldIstanceId.equals(instanceId)) {
                    return offset;
                }

                offset++;
            }
        }

        return -1;
    }

    protected List<Serializable> getFieldValues(DDMStructure ddmStructure, String fieldName, String fieldNamespace,
            ServiceContext serviceContext) throws PortalException {

        DDMFormField ddmFormField = ddmStructure.getDDMFormField(fieldName);

        String fieldDataType = ddmFormField.getDataType();
        String fieldType = ddmFormField.getType();

        LocalizedValue predefinedValue = ddmFormField.getPredefinedValue();

        List<String> fieldNames = getFieldNames(fieldNamespace, fieldName, serviceContext);

        List<Serializable> fieldValues = new ArrayList<>(fieldNames.size());

        for (String fieldNameValue : fieldNames) {
            Serializable fieldValue = serviceContext.getAttribute(fieldNameValue);

            if (Validator.isNull(fieldValue)) {
                fieldValue = predefinedValue.getString(serviceContext.getLocale());
            }

            if (fieldType.equals(DDMImpl.TYPE_CHECKBOX) && Validator.isNull(fieldValue)) {

                fieldValue = "false";
            } else if (fieldDataType.equals(FieldConstants.DATE)) {
                Date fieldValueDate = null;

                if (Validator.isNull(fieldValue)) {
                    int fieldValueMonth = GetterUtil
                            .getInteger(serviceContext.getAttribute(fieldNameValue + "Month"));
                    int fieldValueDay = GetterUtil.getInteger(serviceContext.getAttribute(fieldNameValue + "Day"));
                    int fieldValueYear = GetterUtil
                            .getInteger(serviceContext.getAttribute(fieldNameValue + "Year"));

                    fieldValueDate = _portal.getDate(fieldValueMonth, fieldValueDay, fieldValueYear,
                            TimeZoneUtil.getTimeZone("UTC"), null);
                } else {
                    try {
                        fieldValueDate = DateUtil.parseDate(String.valueOf(fieldValue), serviceContext.getLocale());
                    } catch (ParseException pe) {
                        _log.error("Unable to parse date " + fieldValue);
                    }
                }

                if (fieldValueDate != null) {
                    fieldValue = String.valueOf(fieldValueDate.getTime());
                }
            } else if (fieldDataType.equals(FieldConstants.IMAGE) && Validator.isNull(fieldValue)) {

                HttpServletRequest request = serviceContext.getRequest();

                if (request instanceof UploadRequest) {
                    String imageFieldValue = getImageFieldValue((UploadRequest) request, fieldNameValue);

                    if (Validator.isNotNull(imageFieldValue)) {
                        fieldValue = imageFieldValue;
                    }
                }
            }

            if (Validator.isNull(fieldValue)) {
                return null;
            }

            if (DDMImpl.TYPE_SELECT.equals(fieldType)) {
                String predefinedValueString = predefinedValue.getString(serviceContext.getLocale());

                if (!fieldValue.equals(predefinedValueString) && (fieldValue instanceof String)) {

                    fieldValue = new String[] { String.valueOf(fieldValue) };

                    fieldValue = JSONFactoryUtil.serialize(fieldValue);
                }
            }

            Serializable fieldValueSerializable = FieldConstants.getSerializable(fieldDataType,
                    GetterUtil.getString(fieldValue));

            fieldValues.add(fieldValueSerializable);
        }

        return fieldValues;
    }

    protected List<Serializable> getFieldValues(Field field, Locale locale) {
        Map<Locale, List<Serializable>> fieldValuesMap = field.getValuesMap();

        return fieldValuesMap.get(locale);
    }

    protected byte[] getImageBytes(UploadRequest uploadRequest, String fieldNameValue) throws Exception {

        File file = uploadRequest.getFile(fieldNameValue + "File");

        byte[] bytes = FileUtil.getBytes(file);

        if (ArrayUtil.isNotEmpty(bytes)) {
            return bytes;
        }

        String url = uploadRequest.getParameter(fieldNameValue + "URL");

        long imageId = GetterUtil.getLong(_http.getParameter(url, "img_id", false));

        Image image = _imageLocalService.fetchImage(imageId);

        if (image == null) {
            return null;
        }

        return image.getTextObj();
    }

    protected String getImageFieldValue(UploadRequest uploadRequest, String fieldNameValue) {

        try {
            byte[] bytes = getImageBytes(uploadRequest, fieldNameValue);

            if (ArrayUtil.isNotEmpty(bytes)) {
                JSONObject jsonObject = JSONFactoryUtil.createJSONObject();

                jsonObject.put("alt", uploadRequest.getParameter(fieldNameValue + "Alt"));
                jsonObject.put("data", UnicodeFormatter.bytesToHex(bytes));

                return jsonObject.toString();
            }
        } catch (Exception e) {
        }

        return StringPool.BLANK;
    }

    protected Set<Locale> getMergedAvailableLocales(Set<Locale> newFieldAvailableLocales,
            Set<Locale> existingFieldAvailableLocales) {

        Set<Locale> mergedAvailableLocales = new HashSet<>();

        mergedAvailableLocales.addAll(newFieldAvailableLocales);
        mergedAvailableLocales.addAll(existingFieldAvailableLocales);

        return mergedAvailableLocales;
    }

    protected List<Serializable> getMergedFieldValues(String fieldName, List<Serializable> newFieldValues,
            String[] newFieldsDisplayValues, List<Serializable> existingFieldValues,
            String[] existingFieldsDisplayValues, List<Serializable> defaultFieldValues) {

        if (existingFieldValues == null) {
            return newFieldValues;
        }

        List<Serializable> mergedLocaleValues = new ArrayList<>();

        int repetition = countFieldRepetition(newFieldsDisplayValues, fieldName);

        for (int i = 0; i < repetition; i++) {
            int existingFieldValueIndex = getExistingFieldValueIndex(newFieldsDisplayValues,
                    existingFieldsDisplayValues, fieldName, i);

            if (existingFieldValueIndex == -1) {
                mergedLocaleValues.add(i, defaultFieldValues.get(i));
            } else {
                if (newFieldValues != null) {
                    mergedLocaleValues.add(i, newFieldValues.get(i));
                } else {
                    Serializable existingValue = existingFieldValues.get(existingFieldValueIndex);

                    mergedLocaleValues.add(i, existingValue);
                }
            }
        }

        return mergedLocaleValues;
    }

    protected Map<Locale, List<Serializable>> getMergedFieldValuesMap(Field newField,
            String[] newFieldsDisplayValues, Field existingField, String[] existingFieldsDisplayValues) {

        Set<Locale> availableLocales = getMergedAvailableLocales(newField.getAvailableLocales(),
                existingField.getAvailableLocales());

        for (Locale locale : availableLocales) {
            List<Serializable> newFieldValues = getFieldValues(newField, locale);

            List<Serializable> existingFieldValues = getFieldValues(existingField, locale);

            List<Serializable> defaultFieldValues = getFieldValues(newField, newField.getDefaultLocale());

            List<Serializable> mergedLocaleValues = getMergedFieldValues(newField.getName(), newFieldValues,
                    newFieldsDisplayValues, existingFieldValues, existingFieldsDisplayValues, defaultFieldValues);

            existingField.setValues(locale, mergedLocaleValues);
        }

        return existingField.getValuesMap();
    }

    @Reference(unbind = "-")
    protected void setDDMFormJSONDeserializer(DDMFormJSONDeserializer ddmFormJSONDeserializer) {

        _ddmFormJSONDeserializer = ddmFormJSONDeserializer;
    }

    @Reference(unbind = "-")
    protected void setDDMFormJSONSerializer(DDMFormJSONSerializer ddmFormJSONSerializer) {

        _ddmFormJSONSerializer = ddmFormJSONSerializer;
    }

    @Reference(unbind = "-")
    protected void setDDMFormValuesJSONDeserializer(DDMFormValuesJSONDeserializer ddmFormValuesJSONDeserializer) {

        _ddmFormValuesJSONDeserializer = ddmFormValuesJSONDeserializer;
    }

    @Reference(unbind = "-")
    protected void setDDMFormValuesJSONSerializer(DDMFormValuesJSONSerializer ddmFormValuesJSONSerializer) {

        _ddmFormValuesJSONSerializer = ddmFormValuesJSONSerializer;
    }

    @Reference(unbind = "-")
    protected void setDDMFormValuesToFieldsConverter(
            DDMFormValuesToFieldsConverter ddmFormValuesToFieldsConverter) {

        _ddmFormValuesToFieldsConverter = ddmFormValuesToFieldsConverter;
    }

    @Reference(unbind = "-")
    protected void setDLAppLocalService(DLAppLocalService dlAppLocalService) {
        _dlAppLocalService = dlAppLocalService;
    }

    @Reference(unbind = "-")
    protected void setFieldsToDDMFormValuesConverter(
            FieldsToDDMFormValuesConverter fieldsToDDMFormValuesConverter) {

        _fieldsToDDMFormValuesConverter = fieldsToDDMFormValuesConverter;
    }

    @Reference(unbind = "-")
    protected void setImageLocalService(ImageLocalService imageLocalService) {
        _imageLocalService = imageLocalService;
    }

    @Reference(unbind = "-")
    protected void setLayoutLocalService(LayoutLocalService layoutLocalService) {

        _layoutLocalService = layoutLocalService;
    }

    protected String[] splitFieldsDisplayValue(Field fieldsDisplayField) {
        String value = (String) fieldsDisplayField.getValue();

        return StringUtil.split(value);
    }

    protected void updateDDMFormFieldDefaultLocale(DDMFormField ddmFormField, Locale newDefaultLocale) {

        updateDDMFormFieldOptionsDefaultLocale(ddmFormField.getDDMFormFieldOptions(), newDefaultLocale);

        updateLocalizedValueDefaultLocale(ddmFormField.getLabel(), newDefaultLocale);
        updateLocalizedValueDefaultLocale(ddmFormField.getPredefinedValue(), newDefaultLocale);
        updateLocalizedValueDefaultLocale(ddmFormField.getStyle(), newDefaultLocale);
        updateLocalizedValueDefaultLocale(ddmFormField.getTip(), newDefaultLocale);
    }

    protected void updateDDMFormFieldOptionsDefaultLocale(DDMFormFieldOptions ddmFormFieldOptions,
            Locale newDefaultLocale) {

        Map<String, LocalizedValue> options = ddmFormFieldOptions.getOptions();

        for (LocalizedValue localizedValue : options.values()) {
            updateLocalizedValueDefaultLocale(localizedValue, newDefaultLocale);
        }

        ddmFormFieldOptions.setDefaultLocale(newDefaultLocale);
    }

    protected void updateDDMFormFieldsDefaultLocale(List<DDMFormField> ddmFormFields, Locale newDefaultLocale) {

        for (DDMFormField ddmFormField : ddmFormFields) {
            updateDDMFormFieldDefaultLocale(ddmFormField, newDefaultLocale);

            updateDDMFormFieldsDefaultLocale(ddmFormField.getNestedDDMFormFields(), newDefaultLocale);
        }
    }

    protected void updateLocalizedValueDefaultLocale(LocalizedValue localizedValue, Locale newDefaultLocale) {

        Set<Locale> availableLocales = localizedValue.getAvailableLocales();

        if (!availableLocales.contains(newDefaultLocale)) {
            String defaultValueString = localizedValue.getString(localizedValue.getDefaultLocale());

            localizedValue.addString(newDefaultLocale, defaultValueString);
        }

        localizedValue.setDefaultLocale(newDefaultLocale);
    }

    private static final Log _log = LogFactoryUtil.getLog(DDMImpl.class);

    private DDMFormJSONDeserializer _ddmFormJSONDeserializer;
    private DDMFormJSONSerializer _ddmFormJSONSerializer;
    private DDMFormValuesJSONDeserializer _ddmFormValuesJSONDeserializer;
    private DDMFormValuesJSONSerializer _ddmFormValuesJSONSerializer;
    private DDMFormValuesToFieldsConverter _ddmFormValuesToFieldsConverter;
    private DLAppLocalService _dlAppLocalService;
    private FieldsToDDMFormValuesConverter _fieldsToDDMFormValuesConverter;

    @Reference
    private Http _http;

    private ImageLocalService _imageLocalService;
    private LayoutLocalService _layoutLocalService;

    @Reference
    private Portal _portal;

}