org.jahia.ajax.gwt.helper.ContentDefinitionHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.ajax.gwt.helper.ContentDefinitionHelper.java

Source

/**
 * ==========================================================================================
 * =                   JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION                       =
 * ==========================================================================================
 *
 *                                 http://www.jahia.com
 *
 *     Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved.
 *
 *     THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES:
 *     1/GPL OR 2/JSEL
 *
 *     1/ GPL
 *     ==================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program 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 General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 *
 *     2/ JSEL - Commercial and Supported Versions of the program
 *     ===================================================================================
 *
 *     IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS:
 *
 *     Alternatively, commercial and supported versions of the program - also known as
 *     Enterprise Distributions - must be used in accordance with the terms and conditions
 *     contained in a separate written agreement between you and Jahia Solutions Group SA.
 *
 *     If you are unsure which license is appropriate for your use,
 *     please contact the sales department at sales@jahia.com.
 */
package org.jahia.ajax.gwt.helper;

import com.extjs.gxt.ui.client.data.ModelData;
import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.value.*;
import org.jahia.ajax.gwt.client.data.GWTChoiceListInitializer;
import org.jahia.ajax.gwt.client.data.GWTJahiaValueDisplayBean;
import org.jahia.ajax.gwt.client.data.definition.*;
import org.jahia.ajax.gwt.client.service.GWTJahiaServiceException;
import org.jahia.data.templates.JahiaTemplatesPackage;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.content.*;
import org.jahia.services.content.decorator.JCRSiteNode;
import org.jahia.services.content.nodetypes.*;
import org.jahia.services.content.nodetypes.initializers.ChoiceListInitializer;
import org.jahia.services.content.nodetypes.initializers.ChoiceListInitializerService;
import org.jahia.services.content.nodetypes.initializers.ChoiceListValue;
import org.jahia.utils.i18n.Messages;
import org.slf4j.Logger;

import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeTypeIterator;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Helper class for accessing node types and definitions.
 *
 * @author Thomas Draier
 *         Date: Sep 12, 2008 - 11:48:20 AM
 */
public class ContentDefinitionHelper {
    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(ContentDefinitionHelper.class);

    private NavigationHelper navigation;
    private ChoiceListInitializerService choiceListInitializerService;

    public void setNavigation(NavigationHelper navigation) {
        this.navigation = navigation;
    }

    public void setChoiceListInitializerService(ChoiceListInitializerService choiceListInitializerService) {
        this.choiceListInitializerService = choiceListInitializerService;
    }

    private static final List<String> excludedItems = Arrays.asList("j:locktoken", "jcr:lockOwner",
            "jcr:lockIsDeep", "j:nodename", "j:fullpath", "j:applyAcl", "jcr:uuid", "j:fieldsinuse");

    private static final List<String> excludedTypes = Arrays.asList("nt:base", "mix:versionable", "jnt:workflow");

    public GWTJahiaNodeType getNodeType(String name, Locale uiLocale) {
        if (StringUtils.isEmpty(name)) {
            return null;
        }
        ExtendedNodeType nodeType = null;
        try {
            nodeType = NodeTypeRegistry.getInstance().getNodeType(name);
        } catch (NoSuchNodeTypeException e) {
            return null;
        }
        GWTJahiaNodeType gwt = getGWTJahiaNodeType(nodeType, uiLocale);
        return gwt;
    }

    public List<GWTJahiaNodeType> getGWTNodeTypes(List<ExtendedNodeType> availableMixins, Locale uiLocale) {
        List<GWTJahiaNodeType> gwtMixin = new ArrayList<GWTJahiaNodeType>();
        for (ExtendedNodeType extendedNodeType : availableMixins) {
            gwtMixin.add(getGWTJahiaNodeType(extendedNodeType, uiLocale));
        }
        return gwtMixin;
    }

    public GWTJahiaNodeType getGWTJahiaNodeType(ExtendedNodeType nodeType, Locale uiLocale) {
        GWTJahiaNodeType gwt = new GWTJahiaNodeType();
        gwt.setName(nodeType.getName());
        gwt.setMixin(nodeType.isMixin());
        gwt.setAbstract(nodeType.isAbstract());
        gwt.setDescription(nodeType.getDescription(uiLocale));
        String label = nodeType.getLabel(uiLocale);
        gwt.setLabel(label);

        List<ExtendedItemDefinition> defs = nodeType.getItems();

        List<GWTJahiaItemDefinition> items = new ArrayList<GWTJahiaItemDefinition>();
        List<GWTJahiaItemDefinition> inheritedItems = new ArrayList<GWTJahiaItemDefinition>();

        for (ExtendedItemDefinition def : defs) {
            ExtendedItemDefinition overrideDef = def;
            if (!excludedTypes.contains(def.getDeclaringNodeType().getName())
                    && !excludedItems.contains(def.getName())) {
                GWTJahiaItemDefinition item;
                if (def.isNode()) {
                    GWTJahiaNodeDefinition node = new GWTJahiaNodeDefinition();
                    overrideDef = nodeType.getChildNodeDefinitionsAsMap().get(def.getName());
                    if (overrideDef == null) {
                        overrideDef = def;
                    }
                    ExtendedNodeDefinition end = (ExtendedNodeDefinition) overrideDef;
                    item = node;
                    node.setRequiredPrimaryTypes(end.getRequiredPrimaryTypeNames());
                    node.setDefaultPrimaryType(end.getDefaultPrimaryTypeName());
                    node.setAllowsSameNameSiblings(end.allowsSameNameSiblings());
                    node.setWorkflow(end.getWorkflow());
                } else {
                    GWTJahiaPropertyDefinition prop = new GWTJahiaPropertyDefinition();
                    overrideDef = nodeType.getPropertyDefinitionsAsMap().get(def.getName());
                    if (overrideDef == null) {
                        overrideDef = def;
                    }
                    ExtendedPropertyDefinition epd = (ExtendedPropertyDefinition) overrideDef;
                    prop.setInternationalized(epd.isInternationalized());
                    prop.setRequiredType(epd.getRequiredType());
                    prop.setMultiple(epd.isMultiple());
                    String[] constr = epd.getValueConstraints();
                    if (constr != null && constr.length > 0) {
                        prop.setConstrained(true);
                        prop.setValueConstraints(Arrays.asList(constr));
                        switch (prop.getRequiredType()) {
                        case GWTJahiaNodePropertyType.LONG:
                        case GWTJahiaNodePropertyType.DOUBLE:
                        case GWTJahiaNodePropertyType.DECIMAL:
                        case GWTJahiaNodePropertyType.BINARY:
                            resolveNumericConstraint(prop);
                            break;

                        case GWTJahiaNodePropertyType.DATE:
                            resolveDateConstraint(prop);
                            break;

                        case GWTJahiaNodePropertyType.STRING:
                            if (prop.getValueConstraints().size() == 1
                                    && GWTJahiaNodeSelectorType.CHOICELIST != def.getSelector()) {
                                prop.setConstraintErrorMessage(
                                        epd.getMessage("constraint.error.message", uiLocale));
                            }
                            break;
                        }
                    }
                    List<GWTJahiaNodePropertyValue> gwtValues = new ArrayList<GWTJahiaNodePropertyValue>();
                    for (Value value : epd.getDefaultValues()) {
                        try {
                            GWTJahiaNodePropertyValue convertedValue = convertValue(value, epd);
                            if (convertedValue != null) {
                                gwtValues.add(convertedValue);
                            }
                        } catch (RepositoryException e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                    prop.setDefaultValues(gwtValues);
                    item = prop;
                }
                item.setAutoCreated(overrideDef.isAutoCreated());
                item.setLabel(def.getLabel(uiLocale, nodeType));
                item.setMandatory(overrideDef.isMandatory());
                item.setHidden(overrideDef.isHidden());
                item.setName(overrideDef.getName());
                item.setProtected(overrideDef.isProtected());
                item.setDeclaringNodeType(def.getDeclaringNodeType().getName());
                item.setOverrideDeclaringNodeType(overrideDef.getDeclaringNodeType().getName());
                if ("jcr:description".equals(def.getName())) {
                    item.setDeclaringNodeTypeLabel(def.getLabel(uiLocale));
                } else {
                    item.setDeclaringNodeTypeLabel(def.getDeclaringNodeType().getLabel(uiLocale));
                }
                item.setSelector(overrideDef.getSelector());
                item.setSelectorOptions(new HashMap<String, String>(overrideDef.getSelectorOptions()));
                item.setDataType(overrideDef.getItemType());
                item.setTooltip(def.getTooltip(uiLocale, nodeType));
                if (def.getDeclaringNodeType().getName().equals(nodeType.getName())) {
                    items.add(item);
                } else {
                    inheritedItems.add(item);
                }
            }
        }
        gwt.setItems(items);
        gwt.setInheritedItems(inheritedItems);
        List<String> supertypesNames = new ArrayList<String>();
        ExtendedNodeType[] nodeTypes = nodeType.getSupertypes();
        for (ExtendedNodeType type : nodeTypes) {
            supertypesNames.add(type.getName());
        }
        gwt.setSuperTypes(supertypesNames);
        try {
            gwt.setIcon(JCRContentUtils.getIconWithContext(nodeType));
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }

        return gwt;
    }

    public static void resolveNumericConstraint(GWTJahiaPropertyDefinition prop) {
        for (String valueConstraint : prop.getValueConstraints()) {
            final boolean lowerInclusive;
            Double lowerLimit = null;
            final boolean upperInclusive;
            Double upperLimit = null;
            Pattern pattern = Pattern
                    .compile("([\\(\\[]) *(\\-?\\d+\\.?\\d*)? *, *(\\-?\\d+\\.?\\d*)? *([\\)\\]])");
            Matcher matcher = pattern.matcher(valueConstraint);
            if (matcher.matches()) {
                try {
                    // group 1 is lower inclusive/exclusive
                    String s = matcher.group(1);
                    lowerInclusive = s.equals("[");
                    // group 2 is lower limit
                    s = matcher.group(2);
                    if (s == null || s.length() == 0) {
                        lowerLimit = null;
                    } else {
                        lowerLimit = Double.valueOf(matcher.group(2));
                    }
                    // group 3 is upper limit
                    s = matcher.group(3);
                    if (s == null || s.length() == 0) {
                        upperLimit = null;
                    } else {
                        upperLimit = Double.valueOf(matcher.group(3));
                    }
                    // group 4 is lower inclusive/exclusive
                    s = matcher.group(4);
                    upperInclusive = s.equals("]");
                    if (lowerLimit == null && upperLimit == null) {
                        String msg = "'" + valueConstraint + "' is not a valid value constraint"
                                + " format for numeric types: neither lower- nor upper-limit specified";
                        logger.debug(msg);
                        continue;
                    }
                    if (lowerLimit != null && upperLimit != null) {
                        if (lowerLimit > upperLimit) {
                            String msg = "'" + valueConstraint
                                    + "' is not a valid value constraint format for numeric types: lower-limit exceeds upper-limit";
                            logger.debug(msg);
                            continue;
                        }
                    }
                } catch (NumberFormatException nfe) {
                    String msg = "'" + valueConstraint
                            + "' is not a valid value constraint format for numeric types";
                    logger.debug(msg);
                    continue;
                }
                if (upperLimit != null) {
                    prop.setMaxValue(Double.toString(upperInclusive ? upperLimit : --upperLimit));
                }
                if (lowerLimit != null) {
                    prop.setMinValue(Double.toString(lowerInclusive ? lowerLimit : ++lowerLimit));
                }
            }
        }
    }

    public static void resolveDateConstraint(GWTJahiaPropertyDefinition prop) {
        for (String valueConstraint : prop.getValueConstraints()) {
            final boolean lowerInclusive;
            Calendar lowerLimit;
            final boolean upperInclusive;
            Calendar upperLimit;

            Pattern pattern = Pattern
                    .compile("([\\(\\[]) *([0-9TZ\\.\\+-:]*)? *, *([0-9TZ\\.\\+-:]*)? *([\\)\\]])");
            Matcher matcher = pattern.matcher(valueConstraint);
            if (matcher.matches()) {
                try {
                    // group 1 is lower inclusive/exclusive
                    String s = matcher.group(1);
                    lowerInclusive = s.equals("[");
                    // group 2 is lower limit
                    s = matcher.group(2);
                    if (s == null || s.length() == 0) {
                        lowerLimit = null;
                    } else {
                        lowerLimit = DateValue.valueOf(matcher.group(2)).getDate();
                    }
                    // group 3 is upper limit
                    s = matcher.group(3);
                    if (s == null || s.length() == 0) {
                        upperLimit = null;
                    } else {
                        upperLimit = DateValue.valueOf(matcher.group(3)).getDate();
                    }
                    // group 4 is upper inclusive/exclusive
                    s = matcher.group(4);
                    upperInclusive = s.equals("]");

                    if (lowerLimit == null && upperLimit == null) {
                        String msg = "'" + valueConstraint
                                + "' is not a valid value constraint format for dates: neither min- nor max-date specified";
                        logger.debug(msg);
                        continue;
                    }
                    if (lowerLimit != null && upperLimit != null) {
                        if (lowerLimit.after(upperLimit)) {
                            String msg = "'" + valueConstraint
                                    + "' is not a valid value constraint format for dates: min-date > max-date";
                            logger.debug(msg);
                            continue;
                        }
                    }
                    if (upperLimit != null) {
                        prop.setMaxValue(String.valueOf(
                                upperInclusive ? upperLimit.getTimeInMillis() : upperLimit.getTimeInMillis() - 1));
                    }
                    if (lowerLimit != null) {
                        prop.setMinValue(String.valueOf(
                                lowerInclusive ? lowerLimit.getTimeInMillis() : lowerLimit.getTimeInMillis() + 1));
                    }
                } catch (ValueFormatException vfe) {
                    String msg = "'" + valueConstraint + "' is not a valid value constraint format for dates";
                    logger.debug(msg);
                    continue;
                } catch (RepositoryException re) {
                    String msg = "'" + valueConstraint + "' is not a valid value constraint format for dates";
                    logger.debug(msg);
                    continue;
                }
            }
        }
    }

    public List<GWTJahiaNodeType> getNodeTypes(List<String> names, Locale uiLocale) {
        try {
            List<GWTJahiaNodeType> list = new ArrayList<GWTJahiaNodeType>();
            for (String name : names) {
                if (!StringUtils.isEmpty(name)) {
                    GWTJahiaNodeType nodeType = getNodeType(name, uiLocale);
                    if (nodeType != null) {
                        list.add(nodeType);
                    }
                }
            }
            return list;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    public List<GWTJahiaNodeType> getSubNodeTypes(List<String> names, Locale uiLocale) {
        try {
            List<GWTJahiaNodeType> list = new ArrayList<GWTJahiaNodeType>();
            for (String name : names) {
                ExtendedNodeType t = NodeTypeRegistry.getInstance().getNodeType(name);
                NodeTypeIterator nti = t.getSubtypes();
                while (nti.hasNext()) {
                    ExtendedNodeType type = (ExtendedNodeType) nti.next();
                    GWTJahiaNodeType nodeType = getNodeType(type.getName(), uiLocale);
                    list.add(nodeType);
                }
            }
            return list;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return null;
    }

    /**
     * Returns a list of node types with name and label populated that are the
     * sub-types of the specified base type or that are allowed to be created in
     * the specified parent node (if the baseType parameter is null).
     *
     * @param baseTypes            the node type name to find sub-types
     * @param ctx                  current processing context instance
     * @param uiLocale
     * @param displayStudioElement
     * @return a list of node types with name and label populated that are the
     * sub-types of the specified base type or that are allowed to be
     * created in the specified parent node (if the baseType parameter
     * is null)
     */
    public Map<GWTJahiaNodeType, List<GWTJahiaNodeType>> getContentTypes(List<String> baseTypes,
            Map<String, Object> ctx, Locale uiLocale, boolean includeSubTypes, boolean displayStudioElement) {
        Map<GWTJahiaNodeType, List<GWTJahiaNodeType>> map = new HashMap<GWTJahiaNodeType, List<GWTJahiaNodeType>>();
        NodeTypeRegistry registry = NodeTypeRegistry.getInstance();
        try {
            ExtendedNodeType content = registry.getNodeType("jmix:droppableContent");
            NodeTypeIterator typeIterator = content.getDeclaredSubtypes();
            Map<String, ExtendedNodeType> contentTypes = new HashMap<String, ExtendedNodeType>();
            while (typeIterator.hasNext()) {
                ExtendedNodeType type = (ExtendedNodeType) typeIterator.next();
                contentTypes.put(type.getName(), type);
            }

            List<String> types;
            if (baseTypes == null) {
                types = new ArrayList<String>();
            } else {
                types = baseTypes;
            }

            Set<ExtendedNodeType> nodeTypes = new HashSet<ExtendedNodeType>();
            if (baseTypes != null) {
                for (String type : types) {
                    recurseAdd(registry.getNodeType(type), types, contentTypes, nodeTypes, includeSubTypes);
                }
            } else {
                recurseAdd(content, types, contentTypes, nodeTypes, includeSubTypes);
            }

            typeIterator = content.getDeclaredSubtypes();
            while (typeIterator.hasNext()) {
                ExtendedNodeType mainType = (ExtendedNodeType) typeIterator.next();
                List<GWTJahiaNodeType> l = new ArrayList<GWTJahiaNodeType>();

                NodeTypeIterator subtypes = mainType.getDeclaredSubtypes();
                while (subtypes.hasNext()) {
                    ExtendedNodeType nodeType = (ExtendedNodeType) subtypes.next();
                    if (nodeTypes.contains(nodeType)) {
                        final GWTJahiaNodeType nt = getGWTJahiaNodeType(nodeType, uiLocale);
                        if (!displayStudioElement && !Arrays.asList(nodeType.getDeclaredSupertypeNames())
                                .contains("jmix:studioOnly")) {
                            l.add(nt);
                        } else if (displayStudioElement) {
                            l.add(nt);
                        }
                        nodeTypes.remove(nodeType);
                    }
                }
                if (!l.isEmpty()) {
                    map.put(getGWTJahiaNodeType(mainType, uiLocale), l);
                }
            }
            if (!nodeTypes.isEmpty()) {
                List<GWTJahiaNodeType> l = new ArrayList<GWTJahiaNodeType>();
                for (ExtendedNodeType nodeType : nodeTypes) {
                    if (!displayStudioElement
                            && !Arrays.asList(nodeType.getDeclaredSupertypeNames()).contains("jmix:studioOnly")) {
                        final GWTJahiaNodeType nt = getGWTJahiaNodeType(nodeType, uiLocale);
                        l.add(nt);
                    }
                }
                map.put(null, l);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }

        return map;
    }

    private void recurseAdd(ExtendedNodeType req, List<String> baseTypes,
            Map<String, ExtendedNodeType> contentTypes, Collection<ExtendedNodeType> result,
            boolean includeSubTypes) {
        boolean excludeNonDroppable = false;
        if (req.getName().equals("jmix:droppableContent") || contentTypes.keySet().contains(req.getName())) {
            excludeNonDroppable = true;
        }

        add(req, baseTypes, contentTypes, result, excludeNonDroppable);
        if (includeSubTypes) {
            NodeTypeIterator subtypes = req.getSubtypes();
            while (subtypes.hasNext()) {
                ExtendedNodeType subtype = (ExtendedNodeType) subtypes.next();
                add(subtype, baseTypes, contentTypes, result, excludeNonDroppable);
            }
        }
    }

    private void add(ExtendedNodeType type, List<String> baseTypes, Map<String, ExtendedNodeType> contentTypes,
            Collection<ExtendedNodeType> result, boolean excludeNonDroppable) {
        if (!excludedTypes.contains(type.getName()) && !type.isMixin() && !type.isAbstract()
                && (!excludeNonDroppable || CollectionUtils
                        .containsAny(Arrays.asList(type.getDeclaredSupertypeNames()), contentTypes.keySet()))) {
            if (!baseTypes.isEmpty()) {
                for (String t : baseTypes) {
                    if (type.isNodeType(t)) {
                        result.add(type);
                        return;
                    }
                }
            } else {
                result.add(type);
            }
        }
    }

    public List<ExtendedNodeType> getAvailableMixin(String type, JCRSiteNode site) throws NoSuchNodeTypeException {
        ArrayList<ExtendedNodeType> res = new ArrayList<ExtendedNodeType>();
        Set<String> foundTypes = new HashSet<String>();

        Set<String> installedModules = site != null && site.getPath().startsWith("/sites/")
                ? site.getInstalledModulesWithAllDependencies()
                : null;

        Map<ExtendedNodeType, Set<ExtendedNodeType>> m = NodeTypeRegistry.getInstance().getMixinExtensions();

        ExtendedNodeType realType = NodeTypeRegistry.getInstance().getNodeType(type);
        for (ExtendedNodeType nodeType : m.keySet()) {
            if (realType.isNodeType(nodeType.getName())) {
                for (ExtendedNodeType extension : m.get(nodeType)) {
                    //                        ctx.put("contextType", realType);
                    if (installedModules == null || extension.getTemplatePackage() == null
                            || extension.getTemplatePackage().getModuleType().equalsIgnoreCase("system")
                            || installedModules.contains(extension.getTemplatePackage().getId())) {
                        res.add(extension);
                        foundTypes.add(extension.getName());
                    }
                }
            }
        }

        return res;
    }

    public GWTJahiaNodePropertyValue convertValue(Value val, ExtendedPropertyDefinition def)
            throws RepositoryException {
        String theValue;
        int type;

        switch (def.getRequiredType()) {
        case PropertyType.BINARY:
            type = GWTJahiaNodePropertyType.BINARY;
            theValue = val.getString();
            break;
        case PropertyType.BOOLEAN:
            type = GWTJahiaNodePropertyType.BOOLEAN;
            theValue = String.valueOf(val.getBoolean());
            break;
        case PropertyType.DATE:
            type = GWTJahiaNodePropertyType.DATE;
            Calendar date = val.getDate();
            if (date == null) {
                theValue = null;
            } else {
                theValue = String.valueOf(date.getTimeInMillis());
            }
            break;
        case PropertyType.DOUBLE:
            type = GWTJahiaNodePropertyType.DOUBLE;
            theValue = String.valueOf(val.getDouble());
            break;
        case PropertyType.LONG:
            type = GWTJahiaNodePropertyType.LONG;
            theValue = String.valueOf(val.getLong());
            break;
        case PropertyType.NAME:
            type = GWTJahiaNodePropertyType.NAME;
            theValue = val.getString();
            break;
        case PropertyType.PATH:
            type = GWTJahiaNodePropertyType.PATH;
            theValue = val.getString();
            break;
        case PropertyType.WEAKREFERENCE:
            GWTJahiaNodePropertyValue convertedValue = null;
            JCRNodeWrapper node = ((JCRValueWrapper) val).getNode();
            // check if the referenced node exists
            if (node != null) {
                convertedValue = new GWTJahiaNodePropertyValue(navigation.getGWTJahiaNode(node),
                        GWTJahiaNodePropertyType.WEAKREFERENCE);
            }
            return convertedValue;
        case PropertyType.REFERENCE:
            return new GWTJahiaNodePropertyValue(navigation.getGWTJahiaNode(((JCRValueWrapper) val).getNode()),
                    GWTJahiaNodePropertyType.REFERENCE);
        case PropertyType.STRING:
            type = GWTJahiaNodePropertyType.STRING;
            theValue = val.getString();
            if (def.getSelector() == GWTJahiaNodeSelectorType.PICKER) {
                JCRValueWrapper value = val instanceof JCRValueWrapper ? (JCRValueWrapper) val
                        : new JCRValueWrapperImpl(val, def,
                                JCRSessionFactory.getInstance().getCurrentUserSession());
                if (value.getNode() != null) {
                    return new GWTJahiaNodePropertyValue(theValue,
                            navigation.getGWTJahiaNode((JCRNodeWrapper) value.getNode()), type);
                } else {
                    return null;
                }
            }
            break;
        case PropertyType.UNDEFINED:
            type = GWTJahiaNodePropertyType.UNDEFINED;
            theValue = val.getString();
            break;
        default:
            type = GWTJahiaNodePropertyType.UNDEFINED;
            theValue = val.getString();
            break;
        }

        return new GWTJahiaNodePropertyValue(theValue, type);
    }

    public Value convertValue(GWTJahiaNodePropertyValue val) throws RepositoryException {
        Value value;
        switch (val.getType()) {
        case GWTJahiaNodePropertyType.BINARY:
            value = new BinaryValue(val.getBinary());
            break;
        case GWTJahiaNodePropertyType.BOOLEAN:
            value = new BooleanValue(val.getBoolean());
            break;
        case GWTJahiaNodePropertyType.DATE:
            Calendar cal = Calendar.getInstance();
            cal.setTime(val.getDate());
            value = new DateValue(cal);
            break;
        case GWTJahiaNodePropertyType.DOUBLE:
            value = new DoubleValue(val.getDouble());
            break;
        case GWTJahiaNodePropertyType.LONG:
            value = new LongValue(val.getLong());
            break;
        case GWTJahiaNodePropertyType.NAME:
            value = NameValue.valueOf(val.getString());
            break;
        case GWTJahiaNodePropertyType.PATH:
            value = PathValue.valueOf(val.getString());
            break;
        case GWTJahiaNodePropertyType.REFERENCE:
            value = ReferenceValue.valueOf(val.getString());
            break;
        case GWTJahiaNodePropertyType.WEAKREFERENCE:
            value = WeakReferenceValue.valueOf(val.getString());
            break;
        case GWTJahiaNodePropertyType.STRING:
            value = new StringValue(val.getString());
            break;
        case GWTJahiaNodePropertyType.UNDEFINED:
            value = new StringValue(val.getString());
            break;
        default:
            value = new StringValue(val.getString());
            break;
        }

        return value;
    }

    public Map<String, GWTChoiceListInitializer> getAllChoiceListInitializersValues(List<ExtendedNodeType> items,
            ExtendedNodeType contextType, JCRNodeWrapper contextNode, JCRNodeWrapper contextParent, Locale uiLocale)
            throws RepositoryException {
        Map<String, GWTChoiceListInitializer> results = new HashMap<String, GWTChoiceListInitializer>();

        Map<String, Object> context = new HashMap<String, Object>();
        context.put("contextType", contextType);
        context.put("contextNode", contextNode);
        context.put("contextParent", contextParent);

        for (Map.Entry<String, ExtendedPropertyDefinition> item : getChoiceListItems(items).entrySet()) {
            GWTChoiceListInitializer initializer = getChoiceListInitializerValues(item.getValue(), context,
                    uiLocale);
            if (initializer != null) {
                results.put(item.getKey(), initializer);
            }
        }
        return results;
    }

    public GWTChoiceListInitializer getInitializerValues(ExtendedPropertyDefinition epd,
            ExtendedNodeType contextType, JCRNodeWrapper contextNode, JCRNodeWrapper contextParent,
            Map<String, List<GWTJahiaNodePropertyValue>> dependentValues, Locale uiLocale)
            throws RepositoryException {
        Map<String, Object> context = new HashMap<String, Object>();
        context.put("contextType", contextType);
        context.put("contextNode", contextNode);
        context.put("contextParent", contextParent);
        for (Map.Entry<String, List<GWTJahiaNodePropertyValue>> entry : dependentValues.entrySet()) {
            context.put(entry.getKey(), CollectionUtils.collect(entry.getValue(), new Transformer() {
                public Object transform(Object input) {
                    return input.toString();
                }
            }));
        }

        return getChoiceListInitializerValues(epd, context, uiLocale);
    }

    private GWTChoiceListInitializer getChoiceListInitializerValues(ExtendedPropertyDefinition epd,
            Map<String, Object> context, Locale uiLocale) {
        GWTChoiceListInitializer initializer = null;
        if (!epd.isHidden()) {
            Map<String, String> map = epd.getSelectorOptions();
            if (map.size() > 0) {
                final List<GWTJahiaValueDisplayBean> displayBeans = new ArrayList<GWTJahiaValueDisplayBean>(32);
                final Map<String, ChoiceListInitializer> initializers = choiceListInitializerService
                        .getInitializers();
                List<String> dependentProperties = null;
                if (map.containsKey("dependentProperties")) {
                    dependentProperties = Lists
                            .newArrayList(StringUtils.split(map.get("dependentProperties"), ','));
                    context.put("dependentProperties", dependentProperties);
                }
                List<ChoiceListValue> listValues = null;
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    if (initializers.containsKey(entry.getKey())) {
                        listValues = initializers.get(entry.getKey()).getChoiceListValues(epd, entry.getValue(),
                                listValues, uiLocale, context);
                    }
                }
                if (listValues != null) {
                    for (ChoiceListValue choiceListValue : listValues) {
                        try {
                            final GWTJahiaValueDisplayBean displayBean = new GWTJahiaValueDisplayBean(
                                    choiceListValue.getValue().getString(), choiceListValue.getDisplayName());
                            final Map<String, Object> props = choiceListValue.getProperties();
                            if (props != null) {
                                for (Map.Entry<String, Object> objectEntry : props.entrySet()) {
                                    if (objectEntry.getKey() == null || objectEntry.getValue() == null) {
                                        logger.error("Null value : " + objectEntry.getKey() + " / "
                                                + objectEntry.getValue());
                                    } else {
                                        displayBean.set(objectEntry.getKey(), objectEntry.getValue());
                                    }
                                }
                            }
                            displayBeans.add(displayBean);
                        } catch (RepositoryException e) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
                initializer = new GWTChoiceListInitializer(displayBeans, dependentProperties);
            }
        }
        return initializer;
    }

    private Map<String, ExtendedPropertyDefinition> getChoiceListItems(List<ExtendedNodeType> allTypes) {
        Map<String, ExtendedPropertyDefinition> items = new HashMap<String, ExtendedPropertyDefinition>();
        for (ExtendedNodeType nodeType : allTypes) {
            Collection<ExtendedPropertyDefinition> c = nodeType.getPropertyDefinitionsAsMap().values();
            for (ExtendedPropertyDefinition definition : c) {
                if (definition.getSelector() == SelectorType.CHOICELIST
                        && !definition.getSelectorOptions().isEmpty()) {
                    items.put(definition.getDeclaringNodeType().getName() + "." + definition.getName(), definition);
                }
            }
        }
        return items;
    }

    /**
     * Get both dynamic and static default values for the specified types and locales
     *
     * @param items   a list of nodeTypes
     * @param locales a list of locales
     * @return the default values per type, per locale
     * @throws RepositoryException
     */
    public Map<String, Map<String, List<GWTJahiaNodePropertyValue>>> getAllDefaultValues(
            List<ExtendedNodeType> items, List<Locale> locales) throws RepositoryException {
        Set<Map.Entry<String, ExtendedPropertyDefinition>> entries = getInitializedItems(items).entrySet();
        Map<String, Map<String, List<GWTJahiaNodePropertyValue>>> results = new HashMap<String, Map<String, List<GWTJahiaNodePropertyValue>>>();
        for (Locale locale : locales) {
            Map<String, List<GWTJahiaNodePropertyValue>> defaultValues = new HashMap<String, List<GWTJahiaNodePropertyValue>>();
            for (Map.Entry<String, ExtendedPropertyDefinition> entry : entries) {
                List<GWTJahiaNodePropertyValue> propertyDefaultValues = new ArrayList<GWTJahiaNodePropertyValue>();
                for (Value value : entry.getValue().getDefaultValues(locale)) {
                    propertyDefaultValues.add(convertValue(value, entry.getValue()));
                }
                defaultValues.put(entry.getKey(), propertyDefaultValues);
            }
            results.put(locale.toString(), defaultValues);
        }
        return results;
    }

    private Map<String, ExtendedPropertyDefinition> getInitializedItems(List<ExtendedNodeType> allTypes) {
        Map<String, ExtendedPropertyDefinition> items = new HashMap<String, ExtendedPropertyDefinition>();
        for (ExtendedNodeType nodeType : allTypes) {
            for (ExtendedPropertyDefinition definition : nodeType.getPropertyDefinitionsAsMap().values()) {
                if (definition.getDefaultValues().length > 0) {
                    items.put(definition.getDeclaringNodeType().getName() + "." + definition.getName(), definition);
                }
            }
        }
        return items;
    }

    /**
     * Returns a tree of node types and sub-types with name and label populated that are the
     * sub-types of the specified node types.
     *
     * @param nodeTypes
     * @param excludedNodeTypes
     * @param includeSubTypes
     * @param site
     * @param uiLocale
     * @param session
     * @return a tree of node types
     * @throws GWTJahiaServiceException
     */
    public List<GWTJahiaNodeType> getContentTypesAsTree(final List<String> nodeTypes,
            final List<String> excludedNodeTypes, final boolean includeSubTypes, final JCRSiteNode site,
            final Locale uiLocale, final JCRSessionWrapper session) throws GWTJahiaServiceException {
        try {
            List<JahiaTemplatesPackage> packages = new ArrayList<JahiaTemplatesPackage>();

            if (site.isNodeType("jnt:module")) {
                packages.add(site.getTemplatePackage());
            } else {
                for (String s : site.getInstalledModules()) {
                    JahiaTemplatesPackage aPackage = ServicesRegistry.getInstance().getJahiaTemplateManagerService()
                            .getTemplatePackageById(s);
                    packages.add(aPackage);
                }
            }

            for (int i = 0; i < packages.size(); i++) {
                JahiaTemplatesPackage aPackage = packages.get(i);
                if (aPackage != null) {
                    for (JahiaTemplatesPackage dep : aPackage.getDependencies()) {
                        if (!packages.contains(dep)) {
                            packages.add(dep);
                        }
                    }
                }
            }

            List<ExtendedNodeType> types = new ArrayList<ExtendedNodeType>();
            for (JahiaTemplatesPackage pkg : packages) {
                if (pkg != null) {
                    for (NodeTypeIterator nti = NodeTypeRegistry.getInstance().getNodeTypes(pkg.getId()); nti
                            .hasNext();) {
                        ExtendedNodeType extendedNodeType = (ExtendedNodeType) nti.nextNodeType();
                        if (isValidNodeType(extendedNodeType, nodeTypes, excludedNodeTypes, includeSubTypes,
                                site)) {
                            types.add(extendedNodeType);
                        }
                    }
                    if (pkg.isDefault()) {
                        for (NodeTypeIterator nti = NodeTypeRegistry.getInstance().getNodeTypes("system-jahia"); nti
                                .hasNext();) {
                            ExtendedNodeType extendedNodeType = (ExtendedNodeType) nti.nextNodeType();
                            if (isValidNodeType(extendedNodeType, nodeTypes, excludedNodeTypes, includeSubTypes,
                                    site)) {
                                types.add(extendedNodeType);
                            }
                        }
                    }
                }
            }

            Map<ExtendedNodeType, List<ExtendedNodeType>> r = new HashMap<ExtendedNodeType, List<ExtendedNodeType>>();
            for (ExtendedNodeType nt : types) {
                if (!nt.isMixin() && !nt.isAbstract()) {
                    ExtendedNodeType parent = findFolder(nt);
                    if (!r.containsKey(parent)) {
                        r.put(parent, new ArrayList<ExtendedNodeType>());
                    }
                    r.get(parent).add(nt);
                }
            }

            List<GWTJahiaNodeType> roots = new ArrayList<GWTJahiaNodeType>();
            for (Map.Entry<ExtendedNodeType, List<ExtendedNodeType>> entry : r.entrySet()) {
                GWTJahiaNodeType nt = getGWTJahiaNodeType(entry.getKey() != null ? entry.getKey()
                        : NodeTypeRegistry.getInstance().getNodeType("nt:base"), uiLocale);
                roots.add(nt);
                for (ExtendedNodeType type : entry.getValue()) {
                    nt.add(getGWTJahiaNodeType(type, uiLocale));
                }
            }

            if (roots.size() == 1 && (roots.get(0).isMixin() || roots.get(0).getName().equals("nt:base"))) {
                List<ModelData> l = roots.get(0).getChildren();
                roots.clear();
                for (ModelData o : l) {
                    roots.add((GWTJahiaNodeType) o);
                }
            }

            return roots;
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
            throw new GWTJahiaServiceException(Messages.getInternalWithArguments("label.gwt.error.cannot.translate",
                    uiLocale, e.getLocalizedMessage()));
        }
    }

    private ExtendedNodeType findFolder(ExtendedNodeType nt) throws RepositoryException {
        if (!"jmix:droppableContent".equals(nt.getName()) && nt.isNodeType("jmix:droppableContent")) {
            if (logger.isDebugEnabled()) {
                logger.debug("Detected component type {}", nt.getName());
            }

            ExtendedNodeType[] supertypes = nt.getSupertypes();
            for (int i = supertypes.length - 1; i >= 0; i--) {
                ExtendedNodeType st = supertypes[i];
                if (st.isMixin() && !st.getName().equals("jmix:droppableContent")
                        && st.isNodeType("jmix:droppableContent")) {
                    return st;
                }
            }
        }
        return null;
    }

    private boolean isValidNodeType(ExtendedNodeType ent, List<String> nodeTypes, List<String> excludedNodeTypes,
            boolean includeSubTypes, JCRNodeWrapper node) throws RepositoryException {
        if (ent == null) {
            return false;
        }

        if (includeSubTypes) {
            if (isNodeType(nodeTypes, ent) && checkPermissionForType(ent, node)) {
                return excludedNodeTypes == null || !isNodeType(excludedNodeTypes, ent);
            }
        } else {
            for (String nodeType : nodeTypes) {
                if (ent.getName().equals(nodeType) && checkPermissionForType(ent, node)) {
                    return excludedNodeTypes == null || !isNodeType(excludedNodeTypes, ent);
                }
            }
        }
        return false;
    }

    public boolean checkPermissionForType(String typename, JCRNodeWrapper node) throws RepositoryException {
        ExtendedNodeType type = NodeTypeRegistry.getInstance().getNodeType(typename);
        return checkPermissionForType(type, node);
    }

    private boolean checkPermissionForType(ExtendedNodeType type, JCRNodeWrapper node)
            throws NoSuchNodeTypeException {
        NodeTypeRegistry.getInstance().getNodeType("jmix:accessControllableContent").getDeclaredSubtypes();

        List<ExtendedNodeType> superTypes = Arrays.asList(type.getSupertypes());
        NodeTypeIterator it = NodeTypeRegistry.getInstance().getNodeType("jmix:accessControllableContent")
                .getDeclaredSubtypes();

        boolean allowed = true;
        while (it.hasNext()) {
            ExtendedNodeType next = (ExtendedNodeType) it.next();
            if (superTypes.contains(next)) {
                allowed = node.hasPermission("component-" + next.getName().replace(":", "_"));
                // Keep only last (nearest) accessControllableContent mixin if type inherits from multiple ones, so continue looping
            }
        }
        return allowed;
    }

    private boolean isNodeType(List<String> nodeTypes, ExtendedNodeType type) {
        if (nodeTypes != null) {
            for (String nodeType : nodeTypes) {
                if (type.isNodeType(nodeType)) {
                    return true;
                }
            }
            return false;
        }

        return true;
    }

}