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.internal.converter;
17
18 import org.modelmapper.spi.ConditionalConverter;
19 import org.modelmapper.spi.ConditionalConverter.MatchResult;
20
21 import java.util.List;
22 import java.util.concurrent.CopyOnWriteArrayList;
23
24 /**
25  * @author Jonathan Halterman
26  */

27 public final class ConverterStore {
28   private static final ConditionalConverter<?, ?>[] DEFAULT_CONVERTERS = new ConditionalConverter<?, ?>[] {
29           new ArrayConverter(), new MergingCollectionConverter(), new MapConverter(),
30           new FromOptionalConverter(),new OptionalConverter(), new ToOptionalConverter(),
31           new AssignableConverter(), new StringConverter(), new EnumConverter(), new NumberConverter(),
32           new BooleanConverter(), new CharacterConverter(), new DateConverter(),
33           new CalendarConverter(), new UuidConverter(),
34   };
35
36   private final List<ConditionalConverter<?, ?>> converters;
37
38   public ConverterStore() {
39     this(new CopyOnWriteArrayList<>(DEFAULT_CONVERTERS));
40   }
41
42   ConverterStore(List<ConditionalConverter<?, ?>> converters) {
43     this.converters = converters;
44   }
45
46   /**
47    * Returns the first converter that supports converting from {@code sourceType} to
48    * {@code destinationType}. It will select converter that was full match first.
49    * Then it will select {@code MatchResult.PARTIAL} if there is no full match converter
50    * exists.
51    */

52   @SuppressWarnings("unchecked")
53   public <S, D> ConditionalConverter<S, D> getFirstSupported(Class<?> sourceType,
54       Class<?> destinationType) {
55     ConditionalConverter<S, D> firstPartialMatchConverter = null;
56
57     for (ConditionalConverter<?, ?> converter : converters) {
58       MatchResult matchResult = converter.match(sourceType, destinationType);
59       if (matchResult == MatchResult.FULL)
60         return (ConditionalConverter<S, D>) converter;
61       if (firstPartialMatchConverter == null
62           && matchResult == MatchResult.PARTIAL)
63         firstPartialMatchConverter = (ConditionalConverter<S, D>) converter;
64     }
65     return firstPartialMatchConverter;
66   }
67
68   public List<ConditionalConverter<?, ?>> getConverters() {
69     return converters;
70   }
71
72   public ConverterStore removeConverter(Class<? extends ConditionalConverter<?, ?>> converterClass) {
73     ConditionalConverter<?, ?> matchConverter = getConverterByType(converterClass);
74     if (matchConverter != null)
75       converters.remove(matchConverter);
76     return this;
77   }
78
79   public boolean hasConverter(Class<? extends ConditionalConverter<?, ?>> converterClass) {
80     return getConverterByType(converterClass) != null;
81   }
82
83   public ConverterStore addConverter(ConditionalConverter<?, ?> converter) {
84     converters.add(converter);
85     return this;
86   }
87
88   /**
89    * Replaces the converter of specified class with the given instance.
90    * <p>
91    * If the converter to replace is not found than no replace is performed,
92    * the converters are left untouched.
93    *
94    * @param converterClass to replace
95    * @param converter instance with which to replace it
96    * @return converter store itself
97    */

98   public ConverterStore replaceConverter(Class<? extends ConditionalConverter<?, ?>> converterClass, ConditionalConverter<?, ?> converter) {
99       ConditionalConverter<?, ?> matchConverter = getConverterByType(converterClass);
100       if (matchConverter != null) {
101         int idx = converters.indexOf(matchConverter);
102         if (idx != -1) {
103           // because of concurrency do not use List#set(int, T obj) to avoid to replace a wrong converter
104           converters.remove(matchConverter);
105           converters.add(idx, converter);
106         }
107       }
108       return this;
109   }
110
111   private ConditionalConverter<?, ?> getConverterByType(Class<? extends ConditionalConverter<?, ?>> converterClass) {
112     for (ConditionalConverter<?, ?> converter : converters) {
113       if (converter.getClass().equals(converterClass))
114         return converter;
115     }
116     return null;
117   }
118
119 }
120