org.jahia.services.importexport.validation.ConstraintsValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.jahia.services.importexport.validation.ConstraintsValidator.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.services.importexport.validation;

import org.apache.commons.lang.StringUtils;
import org.jahia.api.Constants;
import org.jahia.services.content.JCRMultipleValueUtils;
import org.jahia.services.content.JCRValueFactoryImpl;
import org.jahia.services.content.nodetypes.ExtendedNodeType;
import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;
import org.jahia.utils.Patterns;
import org.xml.sax.Attributes;

import javax.jcr.PropertyType;
import javax.jcr.Value;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import java.util.*;

/**
 * Validator that checks if all mandatory property constraints are satisfied or not before the import
 * @author Serge Huber
 */
public class ConstraintsValidator implements ImportValidator {

    public final static String CONSTRAINT_SEPARATOR = "@@";

    private final static String COMMA_SPLIT_REGEX = "\\s*,\\s*";

    private Map<String, Set<String>> missingMandatoryProperties = new TreeMap<String, Set<String>>();
    private Map<String, Set<String>> missingMandatoryI18NProperties = new TreeMap<String, Set<String>>();
    private Map<String, String> otherConstraintViolations = new HashMap<>();
    private Map<String, String> parentType = new HashMap<>(); // Map<path, nodetype>
    private Map<String, Set<String>> parentMixins = new HashMap<>(); // Map<path, mixins>

    private String previousPath;

    @Override
    public ValidationResult getResult() {
        return new ConstraintsValidatorResult(missingMandatoryProperties, missingMandatoryI18NProperties,
                otherConstraintViolations);
    }

    @Override
    public void validate(String decodedLocalName, String decodedQName, String currentPath, Attributes atts) {

        // Clean up maps of processed path
        if (!StringUtils.startsWith(currentPath + "/", previousPath + "/")) {
            while (!StringUtils.startsWith(currentPath + "/", previousPath + "/")
                    && !StringUtils.isEmpty(previousPath)) {
                parentType.remove(previousPath);
                parentMixins.remove(previousPath);
                previousPath = StringUtils.substringBeforeLast(previousPath, "/");
            }
        }

        previousPath = currentPath;

        String primaryType = atts.getValue(Constants.JCR_PRIMARYTYPE);

        String mixinTypes = atts.getValue(Constants.JCR_MIXINTYPES);
        Set<String> mixins = new HashSet<>();
        if (mixinTypes != null) {
            mixins.addAll(Arrays.asList(mixinTypes.split(COMMA_SPLIT_REGEX)));
        }

        // hold the primary type and mixins when processing translation nodes
        boolean isI18n = Constants.JAHIANT_TRANSLATION.equals(primaryType);
        if (isI18n) {
            currentPath = StringUtils.substringBeforeLast(currentPath, "/");
            if (parentMixins.get(currentPath) != null) {
                mixins.addAll(parentMixins.get(currentPath));
            }
            primaryType = parentType.get(currentPath);
        } else {
            parentType.put(currentPath, primaryType);
            parentMixins.put(currentPath, mixins);
        }

        checkTypeConstraints(primaryType, currentPath, atts, isI18n);

        for (String mixin : mixins) {
            checkTypeConstraints(mixin, currentPath, atts, isI18n);
        }
    }

    private void checkTypeConstraints(String type, String currentPath, Attributes atts, boolean i18nEntry) {
        try {
            ExtendedNodeType typeToCheck = NodeTypeRegistry.getInstance().getNodeType(type);
            ExtendedPropertyDefinition[] extendedPropertyDefinitions = typeToCheck.getPropertyDefinitions();
            for (ExtendedPropertyDefinition extendedPropertyDefinition : extendedPropertyDefinitions) {
                String propertyName = extendedPropertyDefinition.getName();
                String value = atts.getValue(propertyName);

                // Check if the property is mandatory
                boolean isMandatory = extendedPropertyDefinition.isMandatory()
                        && !extendedPropertyDefinition.isAutoCreated() && !extendedPropertyDefinition.isProtected()
                        && !extendedPropertyDefinition.hasDynamicDefaultValues();

                // check if the property def and the kind of entry (i18n or not) match
                boolean doCheck = extendedPropertyDefinition.isInternationalized() == i18nEntry;

                if (isMandatory && doCheck && !Constants.JCR_DATA.equals(propertyName)) {
                    if (value == null) {
                        Set<String> missingProperties = missingMandatoryProperties.get(currentPath);
                        if (missingProperties == null) {
                            missingProperties = new TreeSet<>();
                            missingMandatoryProperties.put(currentPath, missingProperties);
                        }
                        missingProperties.add(propertyName);
                    }
                }
                // Check other contraint
                if (value != null && doCheck) {
                    // process the value constraints for all property type but references
                    String propertyPath = currentPath + "/" + propertyName;
                    boolean hasConstraints = extendedPropertyDefinition.getValueConstraints() != null
                            && extendedPropertyDefinition.getValueConstraints().length > 0;
                    boolean isNotReference = extendedPropertyDefinition
                            .getRequiredType() != PropertyType.WEAKREFERENCE
                            && extendedPropertyDefinition.getRequiredType() != PropertyType.REFERENCE;
                    if (hasConstraints && isNotReference) {
                        // for i18n nodes we check on the parent node but for the mixins, they are directly on the i18n node
                        // in all cases we check first on the primary nodetype that can miss the property, then on the mixin that can set it, in this case the
                        // entry is removed from otherConstraintViolations
                        if (!extendedPropertyDefinition.isMultiple()) {
                            if (!typeToCheck.canSetProperty(propertyName,
                                    JCRValueFactoryImpl.getInstance().createValue(value))) {
                                otherConstraintViolations.put(propertyPath,
                                        propertyName + CONSTRAINT_SEPARATOR + value);
                            }
                        } else {
                            String[] valuesAsString = "".equals(value) ? new String[0]
                                    : Patterns.SPACE.split(value);
                            Value[] values = new Value[valuesAsString.length];
                            int i = 0;
                            for (String s : valuesAsString) {
                                values[i++] = JCRValueFactoryImpl.getInstance()
                                        .createValue(JCRMultipleValueUtils.decode(s));
                            }
                            if (!typeToCheck.canSetProperty(propertyName, values)) {
                                otherConstraintViolations.put(propertyPath,
                                        propertyName + CONSTRAINT_SEPARATOR + Arrays.toString(valuesAsString));
                            }
                        }
                    }
                }
            }
        } catch (NoSuchNodeTypeException e) {
            // MissingNodetypesValidator will return an error in this case
        }
    }
}