All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.exadel.aem.toolkit.plugin.utils.NamingHelper Maven / Gradle / Ivy

Go to download

Maven plugin for storing AEM (Granite UI) markup created with Exadel Toolbox Authoring Kit

There is a newer version: 2.5.3
Show newest version
/*
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.exadel.aem.toolkit.plugin.utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Element;
import com.google.common.base.CaseFormat;

import com.exadel.aem.toolkit.api.handlers.Target;
import com.exadel.aem.toolkit.core.CoreConstants;
import com.exadel.aem.toolkit.plugin.maven.PluginRuntime;

/**
 * Helper class for creating standard-compliant names for XML entities designed to work together
 * with {@link NamingUtil}
 */
class NamingHelper {
    private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
    private static final Pattern NODE_NAME_INDEX_PATTERN = Pattern.compile("\\d*$");
    private static final Pattern NAMESPACE_PATTERN = Pattern.compile("^\\w+:");

    private static final Pattern INVALID_FIELD_NAME_PATTERN = Pattern.compile("^\\W+|[^\\w-/]$|[^\\w-/:]+");
    private static final Pattern INVALID_FIELD_NAME_POSTFIX_PATTERN = Pattern.compile("^[^\\w-/]+|[^\\w-/]$|[^\\w-/:]+");
    private static final Pattern INVALID_NODE_NAME_NS_PATTERN = Pattern.compile("^\\W*:|\\W+:$|[^\\w:]+");
    private static final Pattern INVALID_PLAIN_NAME_PATTERN = Pattern.compile("\\W+");

    private static final Pattern PARENT_PATH_PREFIX_PATTERN = Pattern.compile("^(?:\\.\\./)+");

    private boolean lowercaseFirst;
    private boolean preserveParentPath;
    private boolean allowSoloParentPath;
    private boolean removeInvalidNamespace;
    private Pattern clearingPattern;

    /**
     * Default (instantiation-restricting) constructor
     */
    private NamingHelper() {
    }

    /* -----------------
       Interface methods
       ----------------- */

    /**
     * Checks whether the given string argument is compliant with XML entity naming rules and either returns it as is or
     * transforms to become compliant. Provided default is used to substitute a non-alphanumeric string or prepend a
     * string that does not start with a letter
     * @param source       String value to test and optionally transform
     * @param defaultValue String value to be used as a default or a valid prefix
     * @return String value
     */
    String getValidName(String source, String defaultValue) {
        if (StringUtils.isBlank(source)) {
            return StringUtils.defaultIfBlank(defaultValue, CoreConstants.NN_ITEM);
        }

        String result = source.trim();
        Matcher parentPathPrefixMatcher = preserveParentPath
                ? PARENT_PATH_PREFIX_PATTERN.matcher(result)
                : null;
        String parentPathPrefix = parentPathPrefixMatcher != null && parentPathPrefixMatcher.find()
                ? parentPathPrefixMatcher.group()
                : StringUtils.EMPTY;

        Matcher whitespacePatternMatcher = WHITESPACE_PATTERN.matcher(result);
        boolean convertToCamelCase = whitespacePatternMatcher.find();

        if (convertToCamelCase) {
            result = WHITESPACE_PATTERN.matcher(result).replaceAll(CoreConstants.SEPARATOR_UNDERSCORE);
        }

        result = clearingPattern.matcher(result).replaceAll(StringUtils.EMPTY);

        if (convertToCamelCase) {
            result = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, result);
        }

        boolean needsDefaultValue = result.isEmpty() || !Character.isAlphabetic(result.codePointAt(0));
        boolean canDoWithoutDefaultValue = allowSoloParentPath && !parentPathPrefix.isEmpty();
        if (needsDefaultValue && !canDoWithoutDefaultValue) {
            result = StringUtils.defaultString(defaultValue) +  result;
        }

        if (removeInvalidNamespace) {
            result = removeInvalidNamespaces(result);
        }

        if (lowercaseFirst && !result.chars().allMatch(Character::isUpperCase)) {
            result = StringUtils.uncapitalize(result);
        }

        if (StringUtils.isNotEmpty(parentPathPrefix)) {
            return parentPathPrefix + result;
        }
        return result;
    }

    /**
     * Creates an XML compliant node name that is unique among children of a specified entity by optionally adding
     * an incremental number index to the name
     * @param source       String value to test and optionally transform
     * @param defaultValue String value to be used as a default or a valid prefix
     * @param context      {@code Target} instance to search for existing child nodes
     * @return String value
     */
    String getUniqueName(String source, String defaultValue, Target context) {
        String result = getValidName(source, defaultValue);
        if (context == null) {
            return result;
        }
        int index = 1;
        while (context.exists(result)) {
            result = NODE_NAME_INDEX_PATTERN.matcher(result).replaceFirst(String.valueOf(index++));
        }
        return result;
    }

    /**
     * Creates an XML compliant node name that is unique among children of a specified entity by optionally adding
     * an incremental number index to the name
     * @param source       String value to test and optionally transform
     * @param defaultValue String value to be used as a default or a valid prefix
     * @param context      {@code Element} instance to search for existing child nodes
     * @return String value
     */
    String getUniqueName(String source, String defaultValue, Element context) {
        String result = getValidName(source, defaultValue);
        if (context == null) {
            return result;
        }
        int index = 1;
        while (PluginRuntime.context().getXmlUtility().getChildElement(context, result) != null) {
            result = NODE_NAME_INDEX_PATTERN.matcher(result).replaceFirst(String.valueOf(index++));
        }
        return result;
    }

    /**
     * Called by {@link NamingHelper#getValidName(String, String)} to remove namespace sign ("{@code :}") from a name
     * source when a namespace prefix does not correspond to any registered XML namespace
     * @param value Name source
     * @return Same value if no invalid namespace detected, or a transformed string
     */
    private static String removeInvalidNamespaces(String value) {
        Matcher namespaceMatcher = NAMESPACE_PATTERN.matcher(value);
        String namespaceCapture = namespaceMatcher.find()
            ? namespaceMatcher.group().substring(0, namespaceMatcher.end() - 1)
            : null;
        if (namespaceCapture != null && !XmlFactory.XML_NAMESPACES.containsKey(namespaceCapture)) {
            return value.replace(namespaceMatcher.group(), namespaceCapture);
        }
        return value;
    }

    /* ---------------
       Factory methods
       --------------- */

    /**
     * Creates and initializes an instance of {@link NamingHelper} to deal with regular field names and name prefixes
     * @return {@code NamingHelper} object
     */
    static NamingHelper forFieldName() {
        NamingHelper helper = new NamingHelper();
        helper.preserveParentPath = true;
        helper.clearingPattern = INVALID_FIELD_NAME_PATTERN;
        return helper;
    }

    /**
     * Creates and initializes an instance of {@link NamingHelper} to deal with field name postfix parts
     * @return {@code NamingHelper} object
     */
    static NamingHelper forFieldNamePrefix() {
        NamingHelper helper = new NamingHelper();
        helper.preserveParentPath = true;
        helper.allowSoloParentPath = true;
        helper.clearingPattern = INVALID_FIELD_NAME_PATTERN;
        return helper;
    }

    /**
     * Creates and initializes an instance of {@link NamingHelper} to deal with field name postfix parts
     * @return {@code NamingHelper} object
     */
    static NamingHelper forFieldNamePostfix() {
        NamingHelper helper = new NamingHelper();
        helper.clearingPattern = INVALID_FIELD_NAME_POSTFIX_PATTERN;
        return helper;
    }

    /**
     * Creates and initializes an instance of {@link NamingHelper} to deal with fully qualified (namespaced) node
     * names and attribute names
     * @return {@code NamingHelper} object
     */
    static NamingHelper forNodeName() {
        NamingHelper helper = new NamingHelper();
        helper.lowercaseFirst = true;
        helper.clearingPattern = INVALID_NODE_NAME_NS_PATTERN;
        helper.removeInvalidNamespace = true;
        return helper;
    }

    /**
     * Creates and initializes an instance of {@link NamingHelper} to deal with plan names that contain only alphanumeric
     * characters
     * @return {@code NamingHelper} object
     */
    static NamingHelper forPlainName() {
        NamingHelper helper = new NamingHelper();
        helper.clearingPattern = INVALID_PLAIN_NAME_PATTERN;
        return helper;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy