View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
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   * @author zoly
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 }