org.opensingular.form.SFormUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.opensingular.form.SFormUtil.java

Source

/*
 * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com
 *
 * 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 org.opensingular.form;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.opensingular.form.internal.PathReader;
import org.opensingular.form.type.core.SPackageBootstrap;
import org.opensingular.form.type.country.brazil.SPackageCountryBrazil;
import org.opensingular.form.type.util.SPackageUtil;
import org.opensingular.lib.commons.internal.function.SupplierUtil;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.lang.model.SourceVersion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.joining;

public final class SFormUtil {

    private static final Pattern idPattern = Pattern.compile("[_a-zA-Z][_a-zA-Z0-9]*");

    private SFormUtil() {
    }

    public static boolean isNotValidSimpleName(@Nonnull String name) {
        Objects.requireNonNull(name);
        return !idPattern.matcher(name).matches();
    }

    @Nonnull
    static String validateSimpleName(@Nonnull String name) {
        if (isNotValidSimpleName(name)) {
            throw new SingularFormException('\'' + name + "' no  um nome vlido para tipo ou atributo");
        }
        return name;
    }

    @Nonnull
    static String validatePackageName(@Nonnull String name) {
        Objects.requireNonNull(name);
        if (!SourceVersion.isName(name)) {
            throw new SingularFormException('\'' + name + "' no  um nome vlido para um pacote");
        }
        return name;
    }

    @Nonnull
    public static String resolveName(@Nullable String simpleName, @Nonnull SType<?> type) {
        return simpleName == null ? type.getNameSimple() : simpleName;
    }

    static SType<?> resolveFieldType(SType<?> type, PathReader pathReader) {
        SType<?> currentType = type;
        PathReader currentPathReader = pathReader;
        while (!currentPathReader.isEmpty()) {
            currentType = resolveFieldTypeInternal(currentType, currentPathReader);
            currentPathReader = currentPathReader.next();
        }
        return currentType;
    }

    private static SType<?> resolveFieldTypeInternal(@Nonnull SType<?> type, PathReader pathReader) {
        if (type.isComposite()) {
            if (pathReader.isIndex()) {
                throw new SingularFormException(
                        pathReader.getErrorMsg(type, "?ndice de lista no se aplica a um tipo composto"), type);
            }
            String token = pathReader.getToken();
            SType<?> campo = ((STypeComposite<?>) type).getField(token);
            if (campo == null) {
                throw new SingularFormException(
                        pathReader.getErrorMsg(type, "No existe o campo '" + token + '\''), type);
            }
            return campo;
        } else if (type.isList()) {
            if (pathReader.isIndex()) {
                return ((STypeList<?, ?>) type).getElementsType();
            }
            throw new SingularFormException(pathReader.getErrorMsg(type, "No se aplica a um tipo lista"), type);
        } else if (type instanceof STypeSimple) {
            throw new SingularFormException(
                    pathReader.getErrorMsg(type, "No se aplica um path a um tipo simples"), type);
        } else {
            throw new SingularFormException(
                    pathReader.getErrorMsg(type, "No implementado para " + type.getClass()), type);
        }
    }

    /**
     * Retorna o nome do filho atual indo em direo ao raiz mas parando segundo
     * a condico de parada informada.
     */
    public static String generatePath(SInstance instance, Predicate<SInstance> stopCondition) {
        SInstance current = instance;
        List<SInstance> sequencia = null;
        while (!stopCondition.test(current)) {
            if (sequencia == null) {
                sequencia = new ArrayList<>();
            }
            sequencia.add(current);
            current = current.getParent();
        }
        if (sequencia == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = sequencia.size() - 1; i != -1; i--) {
            current = sequencia.get(i);
            if (current.getParent() instanceof SIList) {
                int pos = ((SIList<?>) current.getParent()).indexOf(current);
                if (pos == -1) {
                    throw new SingularFormException(
                            current.getName() + " no  mais filho de " + current.getParent().getName());
                }
                sb.append('[').append(pos).append(']');
            } else {
                if (current.getParent() != null && sb.length() != 0) {
                    sb.append('.');
                }
                sb.append(current.getName());
            }
        }
        return sb.toString();
    }

    public static String generateUserFriendlyName(String simpleName) {
        final Pattern lowerUpper = Pattern.compile("(.*?[a-z])([A-Z].*?)");
        final Pattern prefixoSigla = Pattern.compile("([A-Z]+)([A-Z][a-z])");
        final ImmutableSet<String> upperCaseSpecialCases = ImmutableSet.of("id", "url");

        return StringUtils.capitalize(Arrays.asList(simpleName).stream()
                .map(s -> lowerUpper.matcher(s).replaceAll("$1-$2"))
                .map(s -> prefixoSigla.matcher(s).replaceAll("$1-$2"))
                .flatMap(s -> Arrays.asList(s.split("[-_]+")).stream())
                .map(s -> (StringUtils.isAllUpperCase(s) ? s : StringUtils.uncapitalize(s)))
                .map(s -> upperCaseSpecialCases.contains(s) ? StringUtils.capitalize(s) : s).collect(joining(" ")));
    }

    public static String generateUserFriendlyPath(SInstance instance) {
        return generateUserFriendlyPath(instance, null);
    }

    public static String generateUserFriendlyPath(SInstance instance, SInstance parentContext) {
        LinkedList<String> labels = new LinkedList<>();
        SInstance child = null;
        for (SInstance node = instance; node != null
                && !node.equals(parentContext); child = node, node = node.getParent()) {

            final String labelNode = node.asAtr().getLabel();

            if (node instanceof SIList<?>) {
                SIList<?> lista = (SIList<?>) node;
                String labelLista = lista.asAtr().getLabel();
                int index = lista.indexOf(child) + 1;
                labels.add(labelLista + ((index > 0) ? " [" + (index) + ']' : ""));
            } else {
                if (StringUtils.isNotBlank(labelNode)) {
                    labels.add(labelNode);
                }
            }
        }
        Collections.reverse(labels);

        if (!labels.isEmpty()) {
            return StringUtils.join(labels, " > ");
        }
        return null;
    }

    /**
     * Retorna o nome completo do tipo sem precisar carregar a definio
     * mediante a leitura das anotaes {@link SInfoType} e {@link SInfoPackage}.
     */
    @Nonnull
    public static String getTypeName(@Nonnull Class<? extends SType<?>> typeClass) {
        Class<? extends SPackage> packageClass = getTypePackage(typeClass);
        return getInfoPackageName(packageClass) + '.' + getTypeSimpleName(typeClass);
    }

    public static String getTypeSimpleName(Class<? extends SType<?>> typeClass) {
        SInfoType infoType = getInfoType(typeClass);
        String typeName = infoType.name();
        if (StringUtils.isBlank(typeName)) {
            typeName = typeClass.getSimpleName();
        }
        return typeName;
    }

    public static Optional<String> getTypeLabel(Class<? extends SType> typeClass) {
        SInfoType infoType = getInfoType((Class<? extends SType<?>>) typeClass);
        if (StringUtils.isBlank(infoType.label())) {
            return Optional.empty();
        }
        return Optional.of(infoType.label());
    }

    @Nonnull
    static SInfoType getInfoType(Class<? extends SType<?>> typeClass) {
        SInfoType mFormTipo = typeClass.getAnnotation(SInfoType.class);
        if (mFormTipo == null) {
            throw new SingularFormException("O tipo '" + typeClass.getName() + " no possui a anotao @"
                    + SInfoType.class.getSimpleName() + " em sua definio.");
        }
        return mFormTipo;
    }

    @Nonnull
    public static Class<? extends SPackage> getTypePackage(Class<? extends SType<?>> typeClass) {
        Class<? extends SPackage> sPackage = getInfoType(typeClass).spackage();
        if (sPackage == null) {
            throw new SingularFormException("O tipo '" + typeClass.getName()
                    + "' no define o atributo 'pacote' na anotao @" + SInfoType.class.getSimpleName());
        }
        return sPackage;
    }

    @Nullable
    static SInfoPackage getInfoPackage(@Nonnull Class<? extends SPackage> packageClass) {
        return packageClass.getAnnotation(SInfoPackage.class);
    }

    @Nonnull
    static String getInfoPackageName(@Nonnull Class<? extends SPackage> packageClass) {
        SInfoPackage info = getInfoPackage(packageClass);
        return info != null && !StringUtils.isBlank(info.name()) ? info.name() : packageClass.getName();
    }

    @Nonnull
    static String getScopeNameOrException(@Nonnull Class<? extends SScope> scopeClass) {
        if (SPackage.class.isAssignableFrom(scopeClass)) {
            return getInfoPackageName((Class<SPackage>) scopeClass);
        } else if (SType.class.isAssignableFrom(scopeClass)) {
            return getTypeName((Class<SType<?>>) scopeClass);
        } else {
            throw new SingularFormException("Unsupported class: " + scopeClass.getName());
        }
    }

    @Nonnull
    static Class<? extends SPackage> getPackageClassOrException(@Nonnull Class<? extends SScope> scopeClass) {
        if (SPackage.class.isAssignableFrom(scopeClass)) {
            return (Class<SPackage>) scopeClass;
        } else if (SType.class.isAssignableFrom(scopeClass)) {
            return getTypePackage((Class<SType<?>>) scopeClass);
        } else {
            throw new SingularFormException("Unsupported class: " + scopeClass.getName());
        }
    }

    private static Supplier<Map<String, Class<? extends SPackage>>> singularPackages;

    private synchronized static Map<String, Class<? extends SPackage>> getSingularPackages() {
        if (singularPackages == null) {
            singularPackages = SupplierUtil.cached(() -> {
                Builder<String, Class<? extends SPackage>> builder = ImmutableMap.builder();
                addPackage(builder, SPackageUtil.class);
                addPackage(builder, SPackageBootstrap.class);
                addPackage(builder, SPackageCountryBrazil.class);
                return builder.build();
            });
        }
        return singularPackages.get();
    }

    private static void addPackage(Builder<String, Class<? extends SPackage>> builder,
            Class<? extends SPackage> packageClass) {
        builder.put(getInfoPackageName(packageClass), packageClass);
    }

    /**
     * Tentar descobrir um pacote padres do singular ao qual provavelmente o
     * tipo informado pertence.
     *
     * @return null se o tipo no for de um pacote do singular ou seno for
     * encontrado um tipo compatvel.
     */
    static Class<? extends SPackage> getSingularPackageForType(String pathFullName) {
        if (!pathFullName.startsWith(SDictionary.SINGULAR_PACKAGES_PREFIX)) {
            return null;
        }
        Map<String, Class<? extends SPackage>> packages = getSingularPackages();
        String selected = null;
        for (String candidate : packages.keySet()) {
            if (pathFullName.startsWith(candidate) && pathFullName.charAt(candidate.length()) == '.'
                    && (selected == null || selected.length() < candidate.length())) {
                selected = candidate;
            }
        }
        return selected == null ? null : packages.get(selected);
    }

    /**
     * Indica se o tipo  um definio do prprio singular (true) ou se  uma definio de terceiros ou do usurio.
     */
    public static boolean isSingularBuiltInType(SType<?> type) {
        return type.getPackage().getName().startsWith(SDictionary.SINGULAR_PACKAGES_PREFIX);
    }
}