/* * Copyright 2012 Google Inc. * * 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.google.gwt.validation.client.impl.metadata; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.validation.groups.Default; /** * Contains all the information known about the inheritance information for validation groups. */ public class ValidationGroupsMetadata { /** * Builder for {@link ValidationGroupsMetadata} */ public static class Builder { private final Map<Class<?>, Set<Class<?>>> inheritanceinheritanceMap; private final Map<Class<?>, List<Class<?>>> sequenceMap; private Builder() { inheritanceinheritanceMap = new HashMap<Class<?>, Set<Class<?>>>(); sequenceMap = new HashMap<Class<?>, List<Class<?>>>(); addGroup(Default.class); } /** * Adds a group to the inheritance map. May optionally include parents of the group. * @param group The validation group to add. * @param parents A list of validation groups which {@code group} extends. Can be empty if the * group contains no parents. */ public Builder addGroup(Class<?> group, Class<?>... parents) { inheritanceinheritanceMap.put(group, new HashSet<Class<?>>(Arrays.asList(parents))); return this; } /** * Adds a group sequence to the sequence map. * @param groupSequence The class representing the sequence (annotated with @GroupSequence) * @param sequenceGroups The groups in the sequence. */ public Builder addSequence(Class<?> groupSequence, Class<?>... sequenceGroups) { sequenceMap.put(groupSequence, Arrays.asList(sequenceGroups)); return this; } public ValidationGroupsMetadata build() { return new ValidationGroupsMetadata(inheritanceinheritanceMap, sequenceMap); } } /** * Creates a builder populated only with the {@link Default} group. */ public static Builder builder() { return new Builder(); } private final Map<Class<?>, Set<Class<?>>> inheritanceMapping; private final Map<Class<?>, List<Class<?>>> sequenceMapping; private ValidationGroupsMetadata(Map<Class<?>, Set<Class<?>>> inheritanceinheritanceMap, Map<Class<?>, List<Class<?>>> sequenceMap) { this.inheritanceMapping = Collections.unmodifiableMap(inheritanceinheritanceMap); this.sequenceMapping = Collections.unmodifiableMap(sequenceMap); } /** * Checks if a given group has been added to the inheritance map. */ public boolean containsGroup(Class<?> group) { return inheritanceMapping.containsKey(group); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ValidationGroupsMetadata)) { return false; } ValidationGroupsMetadata otherObj = (ValidationGroupsMetadata)other; return inheritanceMapping.equals(otherObj.inheritanceMapping) && sequenceMapping.equals(otherObj.sequenceMapping); } /** * Finds all of the validation groups extended by an intial set of groups. * @param baseGroups The initial set of groups to find parents of. These groups must have been * added to the inheritance map already. * @return A unified set of groups and their parents. * @throws IllegalArgumentException If an initial group has not been added to the map before * calling this method. */ public Set<Class<?>> findAllExtendedGroups(Collection<Class<?>> baseGroups) throws IllegalArgumentException { Set<Class<?>> found = new HashSet<Class<?>>(); Stack<Class<?>> remaining = new Stack<Class<?>>(); // initialize for (Class<?> group : baseGroups) { if (!inheritanceMapping.containsKey(group)) { throw new IllegalArgumentException("The collection of groups contains a group which" + " was not added to the map. Be sure to call addGroup() for all groups first."); } remaining.push(group); } // traverse Class<?> current; Set<Class<?>> superInterfaces; while (!remaining.isEmpty()) { current = remaining.pop(); found.add(current); superInterfaces = inheritanceMapping.get(current); for (Class<?> parent : superInterfaces) { if (!found.contains(parent)) { remaining.push(parent); } } } return found; } /** * Recursively gets all of the groups and sequence groups in the map (children and parents alike) * in one flat set. */ public Set<Class<?>> getAllGroupsAndSequences() { Set<Class<?>> allGroups = new HashSet<Class<?>>(); for (Map.Entry<Class<?>, Set<Class<?>>> entry : inheritanceMapping.entrySet()) { allGroups.add(entry.getKey()); allGroups.addAll(entry.getValue()); } allGroups.addAll(sequenceMapping.keySet()); return allGroups; } /** * Returns all the known group sequence classes. */ public Set<Class<?>> getGroupSequences() { return sequenceMapping.keySet(); } /** * If the group has been added to the map then its parent groups (of one level above) are * retrieved. Otherwise null is returned. * * @see #containsGroup(Class) * @see #findAllExtendedGroups(Collection) */ public Set<Class<?>> getParentsOfGroup(Class<?> group) { return inheritanceMapping.get(group); } /** * Returns all of the groups added to the map (but not their parents). */ public Set<Class<?>> getRootGroups() { return inheritanceMapping.keySet(); } /** * If the sequence class has been added to the map then the actual sequence list is retrieved. * Otherwise null is returned. */ public List<Class<?>> getSequenceList(Class<?> sequence) { return sequenceMapping.get(sequence); } @Override public int hashCode() { int result = inheritanceMapping.hashCode(); result = 31 * result + sequenceMapping.hashCode(); return result; } /** * Checks if a group extends other groups (has parents). */ public boolean hasParents(Class<?> group) { Set<Class<?>> possibleParents = getParentsOfGroup(group); return possibleParents != null && !possibleParents.isEmpty(); } public boolean isInheritanceMapEmpty() { return inheritanceMapping.isEmpty(); } /** * Checks if a given class is a group sequence map. */ public boolean isSeqeuence(Class<?> sequence) { return sequenceMapping.containsKey(sequence); } public boolean isSequenceMapEmpty() { return sequenceMapping.isEmpty(); } @Override public String toString() { return "ValidationGroupsMetaData{inheritanceMap=" + inheritanceMapping + ", " + "sequenceMap=" + sequenceMapping + "}"; } }