View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht.reflect.internal;
21  
22  import org.grouplens.grapht.*;
23  import org.grouplens.grapht.reflect.*;
24  import org.grouplens.grapht.util.ClassProxy;
25  import org.grouplens.grapht.util.Preconditions;
26  import org.grouplens.grapht.util.Types;
27  
28  import javax.inject.Provider;
29  import javax.inject.Singleton;
30  import java.io.InvalidObjectException;
31  import java.io.ObjectInputStream;
32  import java.io.ObjectStreamException;
33  import java.io.Serializable;
34  import java.lang.reflect.Modifier;
35  import java.lang.reflect.Type;
36  import java.util.List;
37  import java.util.Map;
38  
39  /**
40   * ProviderClassSatisfaction is a satisfaction implementation that satisfies a
41   * type given a {@link Provider} class capable of providing that type.
42   * 
43   * @author <a href="http://grouplens.org">GroupLens Research</a>
44   */
45  public class ProviderClassSatisfaction implements Satisfaction, Serializable {
46      private static final long serialVersionUID = -1L;
47      private final transient Class<? extends Provider<?>> providerType;
48  
49      /**
50       * Create a ProviderClassSatisfaction that wraps a given provider type.
51       * 
52       * @param providerType The provider class type
53       * @throws NullPointerException if providerType is null
54       * @throws IllegalArgumentException if the class is not a Provider, or is not instantiable
55       */
56      public ProviderClassSatisfaction(Class<? extends Provider<?>> providerType) {
57          Preconditions.notNull("provider type", providerType);
58          Preconditions.isAssignable(Provider.class, providerType);
59          int mods = providerType.getModifiers();
60          if (Modifier.isAbstract(mods) || Modifier.isInterface(mods)) {
61              throw new IllegalArgumentException("Provider satisfaction " + providerType + " is abstract");
62          }
63          if (!Types.isInstantiable(providerType)) {
64              throw new IllegalArgumentException("Provider satisfaction " + providerType + " is not instantiable");
65          }
66          
67          this.providerType = providerType;
68      }
69      
70      @Override
71      public CachePolicy getDefaultCachePolicy() {
72          return (getErasedType().getAnnotation(Singleton.class) != null ? CachePolicy.MEMOIZE : CachePolicy.NO_PREFERENCE);
73      }
74      
75      /**
76       * @return The Provider class that provides instances satisfying this
77       *         satisfaction
78       */
79      public Class<? extends Provider<?>> getProviderType() {
80          return providerType;
81      }
82      
83      @Override
84      public List<Desire> getDependencies() {
85          return ReflectionDesire.getDesires(providerType);
86      }
87  
88      @Override
89      public Type getType() {
90          return Types.getProvidedType(providerType);
91      }
92  
93      @Override
94      public Class<?> getErasedType() {
95          return Types.erase(getType());
96      }
97  
98      @Override
99      public boolean hasInstance() {
100         return false;
101     }
102 
103     @Override
104     public <T> T visit(SatisfactionVisitor<T> visitor) {
105         return visitor.visitProviderClass(providerType);
106     }
107 
108     @Override
109     @SuppressWarnings({ "unchecked", "rawtypes" })
110     public Instantiator makeInstantiator(Map<Desire,Instantiator> dependencies,
111                                          LifecycleManager lm) {
112         // we have to use the raw type because we don't have enough information,
113         // but we can assume correctly that it will build a provider
114         ClassInstantiator providerBuilder = new ClassInstantiator(providerType, getDependencies(),
115                                                                   dependencies, lm);
116         return Instantiators.ofProviderInstantiator(providerBuilder);
117     }
118     
119     @Override
120     public boolean equals(Object o) {
121         if (!(o instanceof ProviderClassSatisfaction)) {
122             return false;
123         }
124         return ((ProviderClassSatisfaction) o).providerType.equals(providerType);
125     }
126     
127     @Override
128     public int hashCode() {
129         return providerType.hashCode();
130     }
131     
132     @Override
133     public String toString() {
134         return "Provider(" + providerType.getName() + ")";
135     }
136 
137     private Object writeReplace() {
138         return new SerialProxy(providerType);
139     }
140 
141     private void readObject(ObjectInputStream stream) throws ObjectStreamException {
142         throw new InvalidObjectException("must use serialization proxy");
143     }
144 
145     private static class SerialProxy implements Serializable {
146         private static final long serialVersionUID = 1L;
147 
148         private final ClassProxy providerType;
149 
150         public SerialProxy(Class<?> cls) {
151             providerType = ClassProxy.of(cls);
152         }
153 
154         @SuppressWarnings("unchecked")
155         private Object readResolve() throws ObjectStreamException {
156             try {
157                 return new ProviderClassSatisfaction((Class<? extends Provider<?>>) providerType.resolve().asSubclass(Provider.class));
158             } catch (ClassNotFoundException e) {
159                 InvalidObjectException ex = new InvalidObjectException("cannot resolve " + providerType);
160                 ex.initCause(e);
161                 throw ex;
162             } catch (ClassCastException e) {
163                 InvalidObjectException ex = new InvalidObjectException("class " + providerType + " is not a provider");
164                 ex.initCause(e);
165                 throw ex;
166             }
167         }
168     }
169 }