1
7 package org.hibernate.validator.internal.engine.valueextraction;
8
9 import java.lang.reflect.Type;
10 import java.lang.reflect.TypeVariable;
11 import java.security.AccessController;
12 import java.security.PrivilegedAction;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.LinkedHashMap;
17 import java.util.LinkedHashSet;
18 import java.util.Map;
19 import java.util.Set;
20 import java.util.stream.Collectors;
21
22 import javax.validation.ConstraintDeclarationException;
23 import javax.validation.ValidationException;
24 import javax.validation.valueextraction.ValueExtractor;
25
26 import org.hibernate.validator.internal.util.privilegedactions.LoadClass;
27 import org.hibernate.validator.internal.util.stereotypes.Immutable;
28
29
34 public class ValueExtractorManager {
35
36 @Immutable
37 public static final Set<ValueExtractorDescriptor> SPEC_DEFINED_EXTRACTORS;
38
39
43 private static final String HIBERNATE_VALIDATOR_FORCE_DISABLE_JAVAFX_INTEGRATION = "org.hibernate.validator.force-disable-javafx-integration";
44
45 static {
46 LinkedHashSet<ValueExtractorDescriptor> specDefinedExtractors = new LinkedHashSet<>();
47
48 if ( isJavaFxExtensionsEnabled() ) {
49 specDefinedExtractors.add( ObservableValueValueExtractor.DESCRIPTOR );
50 specDefinedExtractors.add( ListPropertyValueExtractor.DESCRIPTOR );
51 specDefinedExtractors.add( ReadOnlyListPropertyValueExtractor.DESCRIPTOR );
52 specDefinedExtractors.add( MapPropertyValueExtractor.DESCRIPTOR );
53 specDefinedExtractors.add( ReadOnlyMapPropertyValueExtractor.DESCRIPTOR );
54 specDefinedExtractors.add( MapPropertyKeyExtractor.DESCRIPTOR );
55 specDefinedExtractors.add( ReadOnlyMapPropertyKeyExtractor.DESCRIPTOR );
56 specDefinedExtractors.add( SetPropertyValueExtractor.DESCRIPTOR );
57 specDefinedExtractors.add( ReadOnlySetPropertyValueExtractor.DESCRIPTOR );
58 }
59
60 specDefinedExtractors.add( ByteArrayValueExtractor.DESCRIPTOR );
61 specDefinedExtractors.add( ShortArrayValueExtractor.DESCRIPTOR );
62 specDefinedExtractors.add( IntArrayValueExtractor.DESCRIPTOR );
63 specDefinedExtractors.add( LongArrayValueExtractor.DESCRIPTOR );
64 specDefinedExtractors.add( FloatArrayValueExtractor.DESCRIPTOR );
65 specDefinedExtractors.add( DoubleArrayValueExtractor.DESCRIPTOR );
66 specDefinedExtractors.add( CharArrayValueExtractor.DESCRIPTOR );
67 specDefinedExtractors.add( BooleanArrayValueExtractor.DESCRIPTOR );
68 specDefinedExtractors.add( ObjectArrayValueExtractor.DESCRIPTOR );
69
70 specDefinedExtractors.add( ListValueExtractor.DESCRIPTOR );
71
72 specDefinedExtractors.add( MapValueExtractor.DESCRIPTOR );
73 specDefinedExtractors.add( MapKeyExtractor.DESCRIPTOR );
74
75 specDefinedExtractors.add( IterableValueExtractor.DESCRIPTOR );
76
77 specDefinedExtractors.add( OptionalValueExtractor.DESCRIPTOR );
78 specDefinedExtractors.add( OptionalIntValueExtractor.DESCRIPTOR );
79 specDefinedExtractors.add( OptionalDoubleValueExtractor.DESCRIPTOR );
80 specDefinedExtractors.add( OptionalLongValueExtractor.DESCRIPTOR );
81
82 SPEC_DEFINED_EXTRACTORS = Collections.unmodifiableSet( specDefinedExtractors );
83 }
84
85 @Immutable
86 private final Map<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> registeredValueExtractors;
87
88 private final ValueExtractorResolver valueExtractorResolver;
89
90 public ValueExtractorManager(Set<ValueExtractor<?>> externalExtractors) {
91 LinkedHashMap<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> tmpValueExtractors = new LinkedHashMap<>();
92
93
94 for ( ValueExtractorDescriptor descriptor : SPEC_DEFINED_EXTRACTORS ) {
95 tmpValueExtractors.put( descriptor.getKey(), descriptor );
96 }
97
98
99 for ( ValueExtractor<?> valueExtractor : externalExtractors ) {
100 ValueExtractorDescriptor descriptor = new ValueExtractorDescriptor( valueExtractor );
101 tmpValueExtractors.put( descriptor.getKey(), descriptor );
102 }
103
104 registeredValueExtractors = Collections.unmodifiableMap( tmpValueExtractors );
105 valueExtractorResolver = new ValueExtractorResolver( new HashSet<>( registeredValueExtractors.values() ) );
106 }
107
108 public ValueExtractorManager(ValueExtractorManager template,
109 Map<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> externalValueExtractorDescriptors) {
110 LinkedHashMap<ValueExtractorDescriptor.Key, ValueExtractorDescriptor> tmpValueExtractors = new LinkedHashMap<>( template.registeredValueExtractors );
111 tmpValueExtractors.putAll( externalValueExtractorDescriptors );
112
113 registeredValueExtractors = Collections.unmodifiableMap( tmpValueExtractors );
114 valueExtractorResolver = new ValueExtractorResolver( new HashSet<>( registeredValueExtractors.values() ) );
115 }
116
117 public static Set<ValueExtractor<?>> getDefaultValueExtractors() {
118 return SPEC_DEFINED_EXTRACTORS.stream()
119 .map( d -> d.getValueExtractor() )
120 .collect( Collectors.collectingAndThen( Collectors.toSet(), Collections::unmodifiableSet ) );
121 }
122
123
134 public ValueExtractorDescriptor getMaximallySpecificAndRuntimeContainerElementCompliantValueExtractor(Type declaredType, TypeVariable<?> typeParameter,
135 Class<?> runtimeType, Collection<ValueExtractorDescriptor> valueExtractorCandidates) {
136
137 if ( valueExtractorCandidates.size() == 1 ) {
138 return valueExtractorCandidates.iterator().next();
139 }
140 else if ( !valueExtractorCandidates.isEmpty() ) {
141 return valueExtractorResolver.getMaximallySpecificAndRuntimeContainerElementCompliantValueExtractor(
142 declaredType,
143 typeParameter,
144 runtimeType,
145 valueExtractorCandidates
146 );
147 }
148 else {
149 return valueExtractorResolver.getMaximallySpecificAndRuntimeContainerElementCompliantValueExtractor(
150 declaredType,
151 typeParameter,
152 runtimeType,
153 registeredValueExtractors.values()
154 );
155 }
156 }
157
158 public ValueExtractorResolver getResolver() {
159 return valueExtractorResolver;
160 }
161
162 @Override
163 public int hashCode() {
164 final int prime = 31;
165 int result = 1;
166 result = prime * result + ( ( registeredValueExtractors == null ) ? 0 : registeredValueExtractors.hashCode() );
167 return result;
168 }
169
170 @Override
171 public boolean equals(Object obj) {
172 if ( this == obj ) {
173 return true;
174 }
175 if ( obj == null ) {
176 return false;
177 }
178 if ( getClass() != obj.getClass() ) {
179 return false;
180 }
181 ValueExtractorManager other = (ValueExtractorManager) obj;
182
183 return registeredValueExtractors.equals( other.registeredValueExtractors );
184 }
185
186 private static boolean isJavaFxExtensionsEnabled() {
187 if ( isJavaFxForcefullyDisabled() ) {
188 return false;
189 }
190 else {
191 return isJavaFxInClasspath();
192 }
193 }
194
195 private static boolean isJavaFxForcefullyDisabled() {
196 return run( new PrivilegedAction<Boolean>() {
197 @Override
198 public Boolean run() {
199 return Boolean.valueOf( Boolean.getBoolean( HIBERNATE_VALIDATOR_FORCE_DISABLE_JAVAFX_INTEGRATION ) );
200 }
201 } );
202 }
203
204 private static boolean isJavaFxInClasspath() {
205 return isClassPresent( "javafx.beans.value.ObservableValue", false );
206 }
207
208 private static boolean isClassPresent(String className, boolean fallbackOnTCCL) {
209 try {
210 run( LoadClass.action( className, ValueExtractorManager.class.getClassLoader(), fallbackOnTCCL ) );
211 return true;
212 }
213 catch (ValidationException e) {
214 return false;
215 }
216 }
217
218 public void clear() {
219 valueExtractorResolver.clear();
220 }
221
222
228 private static <T> T run(PrivilegedAction<T> action) {
229 return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
230 }
231 }
232