1
7 package org.hibernate.validator.internal.engine.groups;
8
9 import java.lang.invoke.MethodHandles;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.concurrent.ConcurrentHashMap;
15 import java.util.concurrent.ConcurrentMap;
16
17 import javax.validation.GroupSequence;
18 import javax.validation.groups.Default;
19
20 import org.hibernate.validator.internal.util.logging.Log;
21 import org.hibernate.validator.internal.util.logging.LoggerFactory;
22
23
29 public class ValidationOrderGenerator {
30
31 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
32
33 private final ConcurrentMap<Class<?>, Sequence> resolvedSequences = new ConcurrentHashMap<Class<?>, Sequence>();
34
35
45 public ValidationOrder getValidationOrder(Class<?> group, boolean expand) {
46 if ( Default.class.equals( group ) ) {
47 return ValidationOrder.DEFAULT_GROUP;
48 }
49
50 if ( expand ) {
51 return getValidationOrder( Collections.<Class<?>>singletonList( group ) );
52 }
53 else {
54 DefaultValidationOrder validationOrder = new DefaultValidationOrder();
55 validationOrder.insertGroup( new Group( group ) );
56 return validationOrder;
57 }
58 }
59
60
67 public ValidationOrder getValidationOrder(Collection<Class<?>> groups) {
68 if ( groups == null || groups.size() == 0 ) {
69 throw LOG.getAtLeastOneGroupHasToBeSpecifiedException();
70 }
71
72
73
74 if ( groups.size() == 1 && groups.contains( Default.class ) ) {
75 return ValidationOrder.DEFAULT_GROUP;
76 }
77
78 for ( Class<?> clazz : groups ) {
79 if ( !clazz.isInterface() ) {
80 throw LOG.getGroupHasToBeAnInterfaceException( clazz );
81 }
82 }
83
84 DefaultValidationOrder validationOrder = new DefaultValidationOrder();
85 for ( Class<?> clazz : groups ) {
86 if ( Default.class.equals( clazz ) ) {
87 validationOrder.insertGroup( Group.DEFAULT_GROUP );
88 }
89 else if ( isGroupSequence( clazz ) ) {
90 insertSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), true, validationOrder );
91 }
92 else {
93 Group group = new Group( clazz );
94 validationOrder.insertGroup( group );
95 insertInheritedGroups( clazz, validationOrder );
96 }
97 }
98
99 return validationOrder;
100 }
101
102 public ValidationOrder getDefaultValidationOrder(Class<?> clazz, List<Class<?>> defaultGroupSequence) {
103 DefaultValidationOrder validationOrder = new DefaultValidationOrder();
104 insertSequence( clazz, defaultGroupSequence.toArray( new Class<?>[defaultGroupSequence.size()] ), false, validationOrder );
105 return validationOrder;
106 }
107
108 private boolean isGroupSequence(Class<?> clazz) {
109 return clazz.getAnnotation( GroupSequence.class ) != null;
110 }
111
112
118 private void insertInheritedGroups(Class<?> clazz, DefaultValidationOrder chain) {
119 for ( Class<?> inheritedGroup : clazz.getInterfaces() ) {
120 Group group = new Group( inheritedGroup );
121 chain.insertGroup( group );
122 insertInheritedGroups( inheritedGroup, chain );
123 }
124 }
125
126 private void insertSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, boolean cache, DefaultValidationOrder validationOrder) {
127 Sequence sequence = cache ? resolvedSequences.get( sequenceClass ) : null;
128 if ( sequence == null ) {
129 sequence = resolveSequence( sequenceClass, sequenceElements, new ArrayList<Class<?>>() );
130
131 sequence.expandInheritedGroups();
132
133
134 if ( cache ) {
135 final Sequence cachedResolvedSequence = resolvedSequences.putIfAbsent( sequenceClass, sequence );
136 if ( cachedResolvedSequence != null ) {
137 sequence = cachedResolvedSequence;
138 }
139 }
140 }
141 validationOrder.insertSequence( sequence );
142 }
143
144 private Sequence resolveSequence(Class<?> sequenceClass, Class<?>[] sequenceElements, List<Class<?>> processedSequences) {
145 if ( processedSequences.contains( sequenceClass ) ) {
146 throw LOG.getCyclicDependencyInGroupsDefinitionException();
147 }
148 else {
149 processedSequences.add( sequenceClass );
150 }
151 List<Group> resolvedSequenceGroups = new ArrayList<Group>();
152 for ( Class<?> clazz : sequenceElements ) {
153 if ( isGroupSequence( clazz ) ) {
154 Sequence tmpSequence = resolveSequence( clazz, clazz.getAnnotation( GroupSequence.class ).value(), processedSequences );
155 addGroups( resolvedSequenceGroups, tmpSequence.getComposingGroups() );
156 }
157 else {
158 List<Group> list = new ArrayList<Group>();
159 list.add( new Group( clazz ) );
160 addGroups( resolvedSequenceGroups, list );
161 }
162 }
163 return new Sequence( sequenceClass, resolvedSequenceGroups );
164 }
165
166 private void addGroups(List<Group> resolvedGroupSequence, List<Group> groups) {
167 for ( Group tmpGroup : groups ) {
168 if ( resolvedGroupSequence.contains( tmpGroup ) && resolvedGroupSequence.indexOf( tmpGroup ) < resolvedGroupSequence
169 .size() - 1 ) {
170 throw LOG.getUnableToExpandGroupSequenceException();
171 }
172 resolvedGroupSequence.add( tmpGroup );
173 }
174 }
175
176 @Override
177 public String toString() {
178 final StringBuilder sb = new StringBuilder();
179 sb.append( "ValidationOrderGenerator" );
180 sb.append( "{resolvedSequences=" ).append( resolvedSequences );
181 sb.append( '}' );
182 return sb.toString();
183 }
184 }
185