1 /*
2 * Copyright 2011 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.modelmapper.config;
17
18 import org.modelmapper.Condition;
19 import org.modelmapper.PropertyMap;
20 import org.modelmapper.Provider;
21 import org.modelmapper.spi.*;
22 import org.modelmapper.spi.ConditionalConverter.MatchResult;
23
24 import java.util.List;
25
26 /**
27 * Configures conventions used during the matching process.
28 *
29 * @author Jonathan Halterman
30 */
31 public interface Configuration {
32 /**
33 * The level at and below which properties can be accessed.
34 */
35 public enum AccessLevel {
36 /** Only public properties are accessible. */
37 PUBLIC,
38 /** All public and protected properties are accessible. */
39 PROTECTED,
40 /** All public, protected and package private properties are accessible. */
41 PACKAGE_PRIVATE,
42 /** All properties are accessible. */
43 PRIVATE;
44 }
45
46 /**
47 * Registers the {@code valueReader} to use when mapping from instances of types {@code T}.
48 *
49 * <p>
50 * This method is part of the ModelMapper SPI.
51 *
52 * @param <T> source type
53 * @param valueReader to register
54 * @throws IllegalArgumentException if {@code valueReader} is null or if type argument {@code T}
55 * is not declared for the {@code valueReader}
56 */
57 <T> Configuration addValueReader(ValueReader<T> valueReader);
58
59 /**
60 * Registers the {@code valueWriter} to use when mapping property to instances of types {@code T}.
61 *
62 * <p>
63 * This method is part of the ModelMapper SPI.
64 *
65 * @param <T> source type
66 * @param valueWriter to register
67 * @throws IllegalArgumentException if {@code valueWriter} is null or if type argument {@code T}
68 * is not declared for the {@code valueWriter}
69 */
70 <T> Configuration addValueWriter(ValueWriter<T> valueWriter);
71
72 /**
73 * Returns a copy of the Configuration.
74 */
75 Configuration copy();
76
77 /**
78 * Gets the ordered list of internal conditional converters that are used to perform type
79 * conversion. This list is mutable and may be modified to control which converters are used to
80 * perform type conversion along with the order in which converters are selected.
81 *
82 * <p>
83 * This method is part of the ModelMapper SPI.
84 */
85 List<ConditionalConverter<?, ?>> getConverters();
86
87 /**
88 * Returns the destination name tokenizer.
89 *
90 * @see #setDestinationNameTokenizer(NameTokenizer)
91 */
92 NameTokenizer getDestinationNameTokenizer();
93
94 /**
95 * Returns the destination name transformer.
96 *
97 * @see #setDestinationNameTransformer(NameTransformer)
98 */
99 NameTransformer getDestinationNameTransformer();
100
101 /**
102 * Returns the destination naming convention.
103 *
104 * @see #setDestinationNamingConvention(NamingConvention)
105 */
106 NamingConvention getDestinationNamingConvention();
107
108 /**
109 * Returns the field access level.
110 *
111 * @see #setFieldAccessLevel(AccessLevel)
112 */
113 AccessLevel getFieldAccessLevel();
114
115 /**
116 * Gets the matching strategy.
117 *
118 * @see #setMatchingStrategy(MatchingStrategy)
119 */
120 MatchingStrategy getMatchingStrategy();
121
122 /**
123 * Returns the method access level.
124 *
125 * @see #setMethodAccessLevel(AccessLevel)
126 */
127 AccessLevel getMethodAccessLevel();
128
129 /**
130 * Returns the Condition that must apply for a property in order for mapping to take place, else
131 * {@code null} if no condition has been configured.
132 *
133 * @see #setPropertyCondition(Condition)
134 */
135 Condition<?, ?> getPropertyCondition();
136
137 /**
138 * Returns the Provider used for provisioning destination object instances, else {@code null} if
139 * no Provider has been configured.
140 *
141 * @see #setProvider(Provider)
142 */
143 Provider<?> getProvider();
144
145 /**
146 * Returns the source name tokenizer.
147 *
148 * @see #setSourceNameTokenizer(NameTokenizer)
149 */
150 NameTokenizer getSourceNameTokenizer();
151
152 /**
153 * Returns the source name transformer.
154 *
155 * @see #setSourceNameTransformer(NameTransformer)
156 */
157 NameTransformer getSourceNameTransformer();
158
159 /**
160 * Gets the source naming convention.
161 *
162 * @see #setSourceNamingConvention(NamingConvention)
163 */
164 NamingConvention getSourceNamingConvention();
165
166 /**
167 * Gets a thread-safe, mutable, ordered list of internal and user-defined ValueReaders that are
168 * used to read source object values during mapping. This list is may be modified to control which
169 * ValueReaders are used to along with the order in which ValueReaders are selected for a source
170 * type.
171 *
172 * <p>
173 * The returned List throws an IllegalArgumentException when attempting to add or set a
174 * ValueReader for which the type argument {@code T} has not been defined.
175 *
176 * <p>
177 * This method is part of the ModelMapper SPI.
178 */
179 List<ValueReader<?>> getValueReaders();
180
181 /**
182 * Gets a thread-safe, mutable, ordered list of internal and user-defined ValueWriters that are
183 * used to write destination object values during mapping. This list is may be modified to control which
184 * ValueWriters are used to along with the order in which ValueWriters are selected for a destination
185 * type.
186 *
187 * <p>
188 * The returned List throws an IllegalArgumentException when attempting to add or set a
189 * ValueWriter for which the type argument {@code T} has not been defined.
190 *
191 * <p>
192 * This method is part of the ModelMapper SPI.
193 */
194 List<ValueWriter<?>> getValueWriters();
195
196 /**
197 * Returns {@code true} if ambiguous properties are ignored or {@code false} if they will result
198 * in an exception.
199 *
200 * @see #setAmbiguityIgnored(boolean)
201 */
202 boolean isAmbiguityIgnored();
203
204 /**
205 * Returns whether field matching is enabled.
206 *
207 * @see #setFieldMatchingEnabled(boolean)
208 */
209 boolean isFieldMatchingEnabled();
210
211 /**
212 * Returns {@code true} if {@link ConditionalConverter}s must define a {@link MatchResult#FULL
213 * full} match in order to be applied. Otherwise conditional converters may also be applied for a
214 * {@link MatchResult#PARTIAL partial} match.
215 * <p>
216 * Default is {@code false}.
217 *
218 * @see #setFullTypeMatchingRequired(boolean)
219 */
220 boolean isFullTypeMatchingRequired();
221
222 /**
223 * Returns whether implicit mapping should be enabled. When {@code true} (default), ModelMapper
224 * will implicitly map source to destination properties based on configured conventions. When
225 * {@code false}, only explicit mappings defined in {@link PropertyMap property maps} will be
226 * used.
227 *
228 * @see #setImplicitMappingEnabled(boolean)
229 */
230 boolean isImplicitMappingEnabled();
231
232 /**
233 * Returns whether nested properties were preferred when ModelMapper were building the type map
234 * with implicit mapping. When {@code true} (default), ModelMapper will prefer looking for nested
235 * properties for a mapping definition.
236 *
237 * This option should be disabled when you are trying to map a model contains circular reference.
238 *
239 * <pre>
240 * modelMapper.createTypeMap(SourceTree.class, DestinationTree.class,
241 * modelMapper.getConfiguration().copy().setPreferNestedProperties(false));
242 * </pre>
243 *
244 * @see #setPreferNestedProperties(boolean)
245 */
246 boolean isPreferNestedProperties();
247
248 /**
249 * Returns whether a property mapping will be skipped if the property value is {@code null}.
250 * When {@code true}, ModelMapper will always not set {@code null} to destination property.
251 *
252 * @see #setSkipNullEnabled(boolean)
253 */
254 boolean isSkipNullEnabled();
255
256 /**
257 * Returns whether OSGi Class Loader Bridging is required.
258 *
259 * @see #setUseOSGiClassLoaderBridging(boolean)
260 */
261 boolean isUseOSGiClassLoaderBridging();
262
263 /**
264 * Returns whether the deep copy feature is enabled.
265 *
266 * @see #setDeepCopyEnabled(boolean)
267 * @see org.modelmapper.internal.converter.AssignableConverter
268 */
269 boolean isDeepCopyEnabled();
270
271 /**
272 * Returns whether collections should be 'merged' when mapped.
273 * When {@code true}, mapping a source collection of size {@code m}
274 * to a destination collection of size {@code n} with {@code m < n} results
275 * in a collection with the first {@code m} elements mapped from the source and elements from
276 * {@code m+1} to {@code n} being preserved from the destination collection.
277 * When {@code false} the elements of the destination collection are not preserved if they are not present
278 * in the source collection.
279 *
280 * @see #setCollectionsMergeEnabled(boolean)
281 * @see org.modelmapper.internal.converter.MergingCollectionConverter
282 * @see org.modelmapper.internal.converter.NonMergingCollectionConverter
283 */
284 boolean isCollectionsMergeEnabled();
285
286 /**
287 * Sets whether destination properties that match more than one source property should be ignored.
288 * When true, ambiguous destination properties are skipped during the matching process. When
289 * false, a ConfigurationException is thrown when ambiguous properties are encountered.
290 *
291 * @param ignore whether ambiguity is to be ignored
292 * @see #isAmbiguityIgnored()
293 */
294 Configuration setAmbiguityIgnored(boolean ignore);
295
296 /**
297 * Sets the tokenizer to be applied to destination property and class names during the matching
298 * process.
299 *
300 * @throws IllegalArgumentException if {@code nameTokenizer} is null
301 */
302 Configuration setDestinationNameTokenizer(NameTokenizer nameTokenizer);
303
304 /**
305 * Sets the name transformer used to transform destination property and class names during the
306 * matching process.
307 *
308 * @throws IllegalArgumentException if {@code nameTransformer} is null
309 */
310 Configuration setDestinationNameTransformer(NameTransformer nameTransformer);
311
312 /**
313 * Sets the convention used to identify destination property names during the matching process.
314 *
315 * @throws IllegalArgumentException if {@code namingConvention} is null
316 */
317 Configuration setDestinationNamingConvention(NamingConvention namingConvention);
318
319 /**
320 * Indicates that fields should be eligible for matching at the given {@code accessLevel}.
321 *
322 * <p>
323 * <b>Note</b>: Field access is only used when {@link #setFieldMatchingEnabled(boolean) field
324 * matching} is enabled.
325 *
326 * @throws IllegalArgumentException if {@code accessLevel} is null
327 * @see #setFieldMatchingEnabled(boolean)
328 */
329 Configuration setFieldAccessLevel(AccessLevel accessLevel);
330
331 /**
332 * Sets whether field matching should be enabled. When true, mapping may take place between
333 * accessible fields. Default is {@code false}.
334 *
335 * @param enabled whether field matching is enabled
336 * @see #isFieldMatchingEnabled()
337 * @see #setFieldAccessLevel(AccessLevel)
338 */
339 Configuration setFieldMatchingEnabled(boolean enabled);
340
341 /**
342 * Set whether {@link ConditionalConverter}s must define a {@link MatchResult#FULL full} match in
343 * order to be applied. If {@code false}, conditional converters may also be applied for a
344 * {@link MatchResult#PARTIAL partial} match.
345 *
346 * @param required whether full type matching is required for conditional converters.
347 * @see #isFullTypeMatchingRequired()
348 */
349 Configuration setFullTypeMatchingRequired(boolean required);
350
351 /**
352 * Sets whether implicit mapping should be enabled. When {@code true} (default), ModelMapper will
353 * implicitly map source to destination properties based on configured conventions. When
354 * {@code false}, only explicit mappings defined in {@link PropertyMap property maps} will be
355 * used.
356 *
357 * @param enabled whether implicit matching is enabled
358 * @see #isImplicitMappingEnabled()
359 */
360 Configuration setImplicitMappingEnabled(boolean enabled);
361
362 /**
363 * Sets whether nested properties were preferred when ModelMapper were building the type map with
364 * implicit mapping. When {@code true} (default), ModelMapper will prefer looking for nested
365 * properties for a mapping definition.
366 *
367 * This option should be disabled when you are trying to map a model contains circular reference.
368 *
369 * <pre>
370 * modelMapper.createTypeMap(SourceTree.class, DestinationTree.class,
371 * modelMapper.getConfiguration().copy().setPreferNestedProperties(false));
372 * </pre>
373 *
374 * @param enabled whether prefer nested properties
375 * @see #isPreferNestedProperties()
376 */
377 Configuration setPreferNestedProperties(boolean enabled);
378
379 /**
380 * Sets whether a property should be skipped or not when the property value is {@code null}.
381 *
382 * @param enabled whether skip null is enabled
383 * @see #isSkipNullEnabled()
384 */
385 Configuration setSkipNullEnabled(boolean enabled);
386
387 /**
388 * Sets whether deep copy should be enabled. When {@code false} (default), ModelMapper will
389 * copy the reference to the destination object of a property if they have same type. When {@code true},
390 * ModelMapper will deep copy the property to destination object.
391 *
392 * @param enabled enabled whether deep copy is enabled
393 * @see #isDeepCopyEnabled()
394 * @see org.modelmapper.internal.converter.AssignableConverter
395 */
396 Configuration setDeepCopyEnabled(boolean enabled);
397
398 /**
399 * Sets whether the 'merging' of collections should be enabled. When {@code true} (default), ModelMapper will
400 * map the elements of the source collection to the destination collection and keep any elements of the destination collection
401 * when the source collection is smaller than the destination collection.
402 * When {@code false} the destination collection only consists of the elements of the source collection after mapping.
403 *
404 * @param enabled
405 * @see #isCollectionsMergeEnabled()
406 * @see org.modelmapper.internal.converter.MergingCollectionConverter
407 * @see org.modelmapper.internal.converter.NonMergingCollectionConverter
408 */
409 Configuration setCollectionsMergeEnabled(boolean enabled);
410
411 /**
412 * Sets whether to use an OSGi Class Loader Bridge as described in the following article:
413 * https://www.infoq.com/articles/code-generation-with-osgi
414 * <p>
415 * This eliminates the need for forcing ModelMapper users to add custom OSGi imports to cglib's internals.
416 * <p>
417 * In addition to that, the Class Loader Bridge will attempt to solve the issue described in the following StackOverflow topic:
418 * https://stackoverflow.com/questions/47854086/cglib-creating-class-proxy-in-osgi-results-in-noclassdeffounderror
419 * <p>
420 * Ideally, this is expected to eliminate class loading issues in OSGi environments that were caused by missing custom imports.
421 * <p>
422 * <b>Note</b>:
423 * If Class Loader Bridging is selected, then the source and destination class types must not be package-private.
424 * That is because in Java, two classes are only consider equal to the JRE if they were loaded with the same class loader even if they otherwise represent an identical class.
425 * The same is true for Java packages. If a class is loaded with a different class loader, it cannot get private-package access to any classes (or class members) that belong to another class loader.
426 *
427 * @param useOSGiClassLoaderBridging whether to use OSGi Class Loader Bridge. Default is false
428 * @see #isUseOSGiClassLoaderBridging()
429 */
430 Configuration setUseOSGiClassLoaderBridging(boolean useOSGiClassLoaderBridging);
431
432 /**
433 * Sets the strategy used to match source properties to destination properties.
434 *
435 * @throws IllegalArgumentException if {@code matchingStrategy} is null
436 */
437 Configuration setMatchingStrategy(MatchingStrategy matchingStrategy);
438
439 /**
440 * Indicates that methods should be eligible for matching at the given {@code accessLevel}.
441 *
442 * @throws IllegalArgumentException if {@code accessLevel} is null
443 * @see AccessLevel
444 */
445 Configuration setMethodAccessLevel(AccessLevel accessLevel);
446
447 /**
448 * Sets the {@code condition} that must apply for a property in order for mapping to take place.
449 * This is overridden by any property conditions defined in a TypeMap or PropertyMap.
450 *
451 * @throws IllegalArgumentException if {@code condition} is null
452 */
453 Configuration setPropertyCondition(Condition<?, ?> condition);
454
455 /**
456 * Sets the {@code provider} to use for providing destination object instances.
457 *
458 * @param provider to register
459 * @throws IllegalArgumentException if {@code provider} is null
460 */
461 Configuration setProvider(Provider<?> provider);
462
463 /**
464 * Sets the tokenizer to be applied to source property and class names during the matching
465 * process.
466 *
467 * @throws IllegalArgumentException if {@code nameTokenizer} is null
468 */
469 Configuration setSourceNameTokenizer(NameTokenizer nameTokenizer);
470
471 /**
472 * Sets the name transformer used to transform source property and class names during the matching
473 * process.
474 *
475 * @throws IllegalArgumentException if {@code nameTransformer} is null
476 */
477 Configuration setSourceNameTransformer(NameTransformer nameTransformer);
478
479 /**
480 * Sets the convention used to identify source property names during the matching process.
481 *
482 * @throws IllegalArgumentException if {@code namingConvention} is null
483 */
484 Configuration setSourceNamingConvention(NamingConvention namingConvention);
485 }
486