/** * Copyright (C) 2016 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.validation.client.dynamic; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import javax.validation.Constraint; import org.jboss.errai.common.client.logging.util.StringFormat; import com.google.gwt.regexp.shared.RegExp; /** * Used for looking up {@link GeneratedDynamicValidator GeneratedDynamicValidators}. * * @author Christian Sadilek <csadilek@redhat.com> * @author Max Barkley <mbarkley@redhat.com> */ public class DynamicValidatorKey { private static final RegExp objArrayRegExp = RegExp.compile("^\\[+L[^;]+;$"); private static final Map<String, String> typeAliases = new HashMap<>(); static { typeAliases.put(Collections.emptyList().getClass().getName(), List.class.getName()); typeAliases.put(Collections.singletonList(null).getClass().getName(), List.class.getName()); typeAliases.put(ArrayList.class.getName(), List.class.getName()); typeAliases.put(LinkedList.class.getName(), List.class.getName()); typeAliases.put(Collections.emptySet().getClass().getName(), Set.class.getName()); typeAliases.put(Collections.singleton(null).getClass().getName(), Set.class.getName()); typeAliases.put(HashSet.class.getName(), Set.class.getName()); typeAliases.put(LinkedHashSet.class.getName(), Set.class.getName()); typeAliases.put(TreeSet.class.getName(), Set.class.getName()); typeAliases.put(Collections.emptyMap().getClass().getName(), Map.class.getName()); typeAliases.put(Collections.singletonMap(null, null).getClass().getName(), Map.class.getName()); typeAliases.put(HashMap.class.getName(), Map.class.getName()); typeAliases.put(LinkedHashMap.class.getName(), Map.class.getName()); typeAliases.put(TreeMap.class.getName(), Map.class.getName()); typeAliases.put(List.class.getName(), Collection.class.getName()); typeAliases.put(Set.class.getName(), Collection.class.getName()); typeAliases.put(Integer.class.getName(), Number.class.getName()); typeAliases.put(Long.class.getName(), Number.class.getName()); typeAliases.put(Double.class.getName(), Number.class.getName()); typeAliases.put(Short.class.getName(), Number.class.getName()); typeAliases.put(Float.class.getName(), Number.class.getName()); typeAliases.put(BigDecimal.class.getName(), Number.class.getName()); typeAliases.put(BigInteger.class.getName(), Number.class.getName()); } private static Optional<String> getTypeAlias(final String valueType) { final Optional<String> mappedAlias; if (isNonPrimitiveArrayType(valueType)) { final int compTypeStart = valueType.indexOf('L') + 1; final int compTypeEndExclusive = valueType.length() - 1; final String componentType = valueType.substring(compTypeStart, compTypeEndExclusive); final String arrayPrefix = valueType.substring(0, compTypeStart); mappedAlias = Optional.ofNullable(getTypeAlias(componentType).map(type -> arrayPrefix + type + ";")) .filter(o -> o.isPresent()) .orElseGet(() -> Optional.ofNullable(objArrayTypeWithReducedDimension(arrayPrefix))); } else { mappedAlias = Optional .ofNullable(Optional.ofNullable(typeAliases.get(valueType))) .filter(mapAlias -> mapAlias.isPresent()) .orElseGet(() -> Optional.ofNullable(Object.class.getName())); } return mappedAlias.filter(alias -> !alias.equals(valueType)); } private static String objArrayTypeWithReducedDimension(final String arrayPrefix) { if ("[L".equals(arrayPrefix)) { return Object.class.getName(); } else { return arrayPrefix.substring(1) + Object.class.getName() + ";"; } } private static boolean isNonPrimitiveArrayType(final String type) { return objArrayRegExp.test(type); } private final String constraint; private final String valueType; /** * @param constraint * The fully-qualified class name of a {@link Constraint}. * @param valueType * The fully-qualified class name of a validatable type. */ public DynamicValidatorKey(final String constraint, final String valueType) { this.constraint = constraint; this.valueType = valueType; } /** * @return The fully-qualified class name of the {@link Constraint} for this key. */ public String getConstraint() { return constraint; } /** * @return The fully-qualified class name of the validatable type for this key. */ public String getValueType() { return valueType; } /** * @return True iff {@link #getAlias()} returns a non-empty {@link Optional}. */ public boolean hasAlias() { return getAlias().isPresent(); } /** * @return If present returns a {@link DynamicValidatorKey} for the same constraint as this one, but a different * {@link #getValueType() value type} that is a super-type of the one for this key. */ public Optional<DynamicValidatorKey> getAlias() { return getTypeAlias(valueType).map(alias -> new DynamicValidatorKey(constraint, alias)); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((constraint == null) ? 0 : constraint.hashCode()); result = prime * result + ((valueType == null) ? 0 : valueType.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final DynamicValidatorKey other = (DynamicValidatorKey) obj; if (constraint == null) { if (other.constraint != null) return false; } else if (!constraint.equals(other.constraint)) return false; if (valueType == null) { if (other.valueType != null) return false; } else if (!valueType.equals(other.valueType)) return false; return true; } @Override public String toString() { return StringFormat.format("DynamicValidatorKey(constraint=%s,valueType=%s)", constraint, valueType); } }