| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.bval.jsr303.groups; |
| |
| |
| import javax.validation.GroupDefinitionException; |
| import javax.validation.GroupSequence; |
| import javax.validation.ValidationException; |
| import javax.validation.groups.Default; |
| import java.util.*; |
| |
| /** |
| * Description: compute group order, based on the RI behavior as to guarantee |
| * compatibility with interpretations of the spec.<br/> |
| * Implementation is thread-safe. |
| */ |
| public class GroupsComputer { |
| /** The default group array used in case any of the validate methods is called without a group. */ |
| private static final Groups DEFAULT_GROUPS; |
| |
| static { |
| DEFAULT_GROUPS = new GroupsComputer().computeGroups(Arrays.asList(getDefaultGroupArray())); |
| } |
| |
| /** |
| * Get the default group array. |
| * @return <code>{@link Default}.class</code> only |
| */ |
| public static Class<?>[] getDefaultGroupArray() { |
| return new Class<?>[] { Default.class }; |
| } |
| |
| /** caching resolved groups in a thread-safe map. */ |
| private final Map<Class<?>, List<Group>> resolvedSequences = Collections |
| .synchronizedMap(new HashMap<Class<?>, List<Group>>()); |
| |
| /** |
| * Compute groups from an array of group classes. |
| * @param groups |
| * @return {@link Groups} |
| */ |
| public Groups computeGroups(Class<?>[] groups) { |
| if (groups == null) { |
| throw new IllegalArgumentException("null passed as group"); |
| } |
| |
| // if no groups is specified use the default |
| if (groups.length == 0) { |
| return DEFAULT_GROUPS; |
| } |
| |
| return computeGroups(Arrays.asList(groups)); |
| } |
| |
| /** |
| * Main compute implementation. |
| * @param groups |
| * @return {@link Groups} |
| */ |
| protected Groups computeGroups(Collection<Class<?>> groups) { |
| if (groups == null || groups.size() == 0) { |
| throw new IllegalArgumentException("At least one group has to be specified."); |
| } |
| |
| for (Class<?> clazz : groups) { |
| if (!clazz.isInterface()) { |
| throw new ValidationException( |
| "A group has to be an interface. " + clazz.getName() + " is not."); |
| } |
| } |
| |
| Groups chain = new Groups(); |
| for (Class<?> clazz : groups) { |
| GroupSequence anno = clazz.getAnnotation(GroupSequence.class); |
| if (anno == null) { |
| Group group = new Group(clazz); |
| chain.insertGroup(group); |
| insertInheritedGroups(clazz, chain); |
| } else { |
| insertSequence(clazz, anno, chain); |
| } |
| } |
| |
| return chain; |
| } |
| |
| private void insertInheritedGroups(Class<?> clazz, Groups chain) { |
| for (Class<?> extendedInterface : clazz.getInterfaces()) { |
| Group group = new Group(extendedInterface); |
| chain.insertGroup(group); |
| insertInheritedGroups(extendedInterface, chain); |
| } |
| } |
| |
| private void insertSequence(Class<?> clazz, GroupSequence anno, Groups chain) { |
| List<Group> sequence; |
| if (resolvedSequences.containsKey(clazz)) { |
| sequence = resolvedSequences.get(clazz); |
| } else { |
| sequence = resolveSequence(clazz, anno, new HashSet<Class<?>>()); |
| } |
| chain.insertSequence(sequence); |
| } |
| |
| private List<Group> resolveSequence(Class<?> group, GroupSequence sequenceAnnotation, |
| Set<Class<?>> processedSequences) { |
| if (processedSequences.contains(group)) { |
| throw new GroupDefinitionException("Cyclic dependency in groups definition"); |
| } else { |
| processedSequences.add(group); |
| } |
| List<Group> resolvedGroupSequence = new LinkedList<Group>(); |
| Class<?>[] sequenceArray = sequenceAnnotation.value(); |
| for (Class<?> clazz : sequenceArray) { |
| GroupSequence anno = clazz.getAnnotation(GroupSequence.class); |
| if (anno == null) { |
| resolvedGroupSequence.add(new Group(clazz)); // group part of sequence |
| } else { |
| List<Group> tmpSequence = |
| resolveSequence(clazz, anno, processedSequences); // recursion! |
| resolvedGroupSequence.addAll(tmpSequence); |
| } |
| } |
| resolvedSequences.put(group, resolvedGroupSequence); |
| return resolvedGroupSequence; |
| } |
| } |