1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.io;
33
34 import com.google.common.annotations.Beta;
35 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
36 import java.io.IOException;
37 import java.lang.reflect.ParameterizedType;
38 import java.lang.reflect.Type;
39 import java.util.Arrays;
40 import java.util.Iterator;
41 import java.util.ServiceLoader;
42 import java.util.function.Function;
43 import java.util.function.Predicate;
44 import javax.annotation.CheckReturnValue;
45 import javax.annotation.ParametersAreNonnullByDefault;
46 import javax.annotation.concurrent.ThreadSafe;
47 import org.spf4j.base.CoreTextMediaType;
48 import org.spf4j.reflect.CachingTypeMapWrapper;
49 import org.spf4j.reflect.GraphTypeMap;
50
51
52
53
54
55 @Beta
56 @ThreadSafe
57 @ParametersAreNonnullByDefault
58 public final class ConfigurableAppenderSupplier implements ObjectAppenderSupplier {
59
60
61 private final CachingTypeMapWrapper<ObjectAppender>[] appenderMap;
62
63 public ConfigurableAppenderSupplier() {
64 this(true, (t) -> false);
65 }
66
67 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED")
68 public ConfigurableAppenderSupplier(final boolean registerFromServiceLoader, final Predicate<Class<?>> except,
69 final ObjectAppender<?>... appenders) {
70 appenderMap = new CachingTypeMapWrapper[CoreTextMediaType.values().length];
71 for (int i = 0; i < appenderMap.length; i++) {
72 appenderMap[i] = new CachingTypeMapWrapper<>(new GraphTypeMap());
73 }
74 if (registerFromServiceLoader) {
75 @SuppressWarnings("unchecked")
76 ServiceLoader<ObjectAppender<?>> load = (ServiceLoader) ServiceLoader.load(ObjectAppender.class,
77 ObjectAppender.class.getClassLoader());
78 Iterator<ObjectAppender<?>> iterator = load.iterator();
79 while (iterator.hasNext()) {
80 ObjectAppender<?> appender = iterator.next();
81 Class<?> appenderType = getAppenderType(appender);
82 if (!except.test(appenderType)) {
83 if (!ConfigurableAppenderSupplier.this.tryRegister((Class) appenderType, (ObjectAppender) appender)) {
84 throw new IllegalArgumentException("Attempting to register duplicate appender(" + appender + ") for "
85 + appenderType);
86 }
87 }
88 }
89 }
90 for (ObjectAppender<?> appender : appenders) {
91 if (!ConfigurableAppenderSupplier.this.tryRegister(getAppenderType(appender), (ObjectAppender) appender)) {
92 throw new IllegalArgumentException("Cannot register appender " + appender);
93 }
94 }
95 appenderMap[ObjectAppender.TOSTRING_APPENDER.getAppendedType().ordinal()]
96 .putIfNotPresent(Object.class, ObjectAppender.TOSTRING_APPENDER);
97 }
98
99 public static Class<?> getAppenderType(final ObjectAppender<?> appender) {
100
101 Class<?> aClass = appender.getClass();
102 Class<?> appenderType = null;
103 do {
104 Type[] genericInterfaces = aClass.getGenericInterfaces();
105 for (Type type : genericInterfaces) {
106 if (type instanceof ParameterizedType) {
107 ParameterizedType pType = (ParameterizedType) type;
108 if (pType.getRawType() == ObjectAppender.class) {
109 Type actualTypeArgument = pType.getActualTypeArguments()[0];
110 if (actualTypeArgument instanceof ParameterizedType) {
111 appenderType = (Class) ((ParameterizedType) actualTypeArgument).getRawType();
112 } else {
113 appenderType = (Class) actualTypeArgument;
114 }
115 break;
116 }
117 }
118 }
119 aClass = aClass.getSuperclass();
120 } while (appenderType == null && aClass != null);
121 if (appenderType == null) {
122 throw new IllegalArgumentException("Improperly declared Appender " + appender);
123 }
124 return appenderType;
125 }
126
127 @SuppressWarnings("unchecked")
128 public <T> int register(final Class<T> type, final ObjectAppender<? super T>... appenders) {
129 int i = 0;
130 for (ObjectAppender<? super T> appender : appenders) {
131 if (ConfigurableAppenderSupplier.this.tryRegister(type, appender)) {
132 i++;
133 }
134 }
135 return i;
136 }
137
138 public <T> void replace(final CoreTextMediaType mt, final Class<T> type,
139 final Function<ObjectAppender<? super T>, ObjectAppender<? super T>> replace) {
140 appenderMap[mt.ordinal()].replace(type, (Function) replace);
141 }
142
143 public <T> void register(final Class<T> type, final ObjectAppender<? super T> appender) {
144 if (!tryRegister(type, appender)) {
145 throw new IllegalArgumentException("Cannnot Register " + appender + " for " + type);
146 }
147 }
148
149 @CheckReturnValue
150 public <T> boolean tryRegister(final Class<T> type, final ObjectAppender<? super T> appender) {
151 return appenderMap[appender.getAppendedType().ordinal()].putIfNotPresent(type, appender);
152 }
153
154 public <T> void register(final Class<T> type, final CoreTextMediaType contentType,
155 final ObjectAppender<? super T> appender) {
156 if (!tryRegister(type, contentType, appender)) {
157 throw new IllegalArgumentException("Cannnot Register " + appender + " for " + type);
158 }
159 }
160
161 @CheckReturnValue
162 public <T> boolean tryRegister(final Class<T> type, final CoreTextMediaType contentType,
163 final ObjectAppender<? super T> appender) {
164 return appenderMap[contentType.ordinal()].putIfNotPresent(type,
165 new ObjectAppenderContentTypeAdapter(appender, contentType));
166 }
167
168
169 public boolean unregister(final Class<?> type) {
170 boolean result = false;
171 for (int i = 0, l = CoreTextMediaType.values().length; i < l; i++) {
172 boolean removed = appenderMap[i].remove(type);
173 if (removed) {
174 result = true;
175 }
176 }
177 return result;
178 }
179
180 @Override
181 public ObjectAppender get(final CoreTextMediaType mt, final Type type) {
182 return appenderMap[mt.ordinal()].get(type);
183 }
184
185 @Override
186 public String toString() {
187 return "ConfigurableAppenderSupplier{" + "lookup=" + Arrays.toString(appenderMap) + '}';
188 }
189
190 private static final class ObjectAppenderContentTypeAdapter<T> implements ObjectAppender<T> {
191
192 private final ObjectAppender<? super T> appender;
193 private final CoreTextMediaType contentType;
194
195 ObjectAppenderContentTypeAdapter(final ObjectAppender<? super T> appender, final CoreTextMediaType contentType) {
196 this.appender = appender;
197 this.contentType = contentType;
198 }
199
200 @Override
201 public void append(final T object, final Appendable appendTo) throws IOException {
202 appender.append(object, appendTo);
203 }
204
205 @Override
206 public void append(final T object, final Appendable appendTo, final ObjectAppenderSupplier appenderSupplier)
207 throws IOException {
208 appender.append(object, appendTo, appenderSupplier);
209 }
210
211 @Override
212 public CoreTextMediaType getAppendedType() {
213 return contentType;
214 }
215 }
216
217 }