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.context;
21  
22  import org.apache.commons.lang3.builder.EqualsBuilder;
23  import org.apache.commons.lang3.builder.HashCodeBuilder;
24  import org.apache.commons.lang3.tuple.Pair;
25  import org.grouplens.grapht.reflect.InjectionPoint;
26  import org.grouplens.grapht.reflect.QualifierMatcher;
27  import org.grouplens.grapht.reflect.Qualifiers;
28  import org.grouplens.grapht.reflect.Satisfaction;
29  import org.grouplens.grapht.util.ClassProxy;
30  import org.grouplens.grapht.util.Types;
31  
32  import javax.annotation.Nullable;
33  import javax.inject.Qualifier;
34  import java.io.InvalidObjectException;
35  import java.io.ObjectInputStream;
36  import java.io.ObjectStreamException;
37  import java.io.Serializable;
38  
39  /**
40   * ReflectionContextElementMatcher is a ContextElementMatcher that matches nodes if the node's
41   * type inherits from the matcher's type and if the node's {@link Qualifier}
42   * matches the configured {@link QualifierMatcher}.
43   * 
44   * @author <a href="http://grouplens.org">GroupLens Research</a>
45   */
46  class TypeElementMatcher implements ContextElementMatcher, Serializable {
47      private static final long serialVersionUID = -1L;
48  
49      @Nullable
50      private final transient Class<?> type;
51      private final transient QualifierMatcher qualifier;
52  
53      /**
54       * Create an unanchored ReflectionContextElementMatcher that matches the given type
55       * with the default qualifier matcher.
56       * 
57       * @param type The type to match
58       * @throws NullPointerException if type is null
59       */
60      public TypeElementMatcher(Class<?> type) {
61          this(type, Qualifiers.matchDefault());
62      }
63  
64      /**
65       * Create an unanchored ReflectionContextElementMatcher that matches the given type and the
66       * given {@link Qualifier}.
67       *
68       * @param type      The type to match
69       * @param qualifier The QualifierMatcher that determines how qualifiers are matched
70       * @throws NullPointerException if type or qualifier is null
71       */
72      public TypeElementMatcher(Class<?> type, QualifierMatcher qualifier) {
73          this.type = type;
74          this.qualifier = qualifier;
75      }
76  
77      /**
78       * @return The type matched by this matcher
79       */
80      public Class<?> getMatchedType() {
81          return type;
82      }
83      
84      /**
85       * @return The {@link QualifierMatcher} matched by this matcher
86       */
87      public QualifierMatcher getMatchedQualifier() {
88          return qualifier;
89      }
90      
91      @Override
92      public MatchElement apply(Pair<Satisfaction, InjectionPoint> n) {
93          // we must check for nulls in case it is a synthetic satisfaction
94          Satisfaction sat = n.getLeft();
95          boolean typeMatches;
96          if (type == null) {
97              typeMatches = sat == null
98                            || sat.getErasedType() == null
99                            || sat.getType().equals(Void.TYPE);
100         } else {
101             typeMatches = sat != null && sat.getErasedType() != null &&
102                           type.isAssignableFrom(sat.getErasedType());
103         }
104 
105         if (typeMatches && qualifier.matches(n.getRight().getQualifier())) {
106             return new MatchElem(sat == null ? null : sat.getErasedType(),
107                                  type, qualifier);
108         } else {
109             return null;
110         }
111     }
112 
113     @Override
114     public boolean equals(Object o) {
115         if (o == this) {
116             return true;
117         } else if (o instanceof TypeElementMatcher) {
118             TypeElementMatcher r = (TypeElementMatcher) o;
119             return new EqualsBuilder().append(type, r.type)
120                                       .append(qualifier, r.qualifier)
121                                       .isEquals();
122         } else {
123             return false;
124         }
125     }
126     
127     @Override
128     public int hashCode() {
129         return new HashCodeBuilder().append(type)
130                                     .append(qualifier)
131                                     .toHashCode();
132     }
133     
134     @Override
135     public String toString() {
136         String tname = type == null ? "null" : type.getSimpleName();
137         return "[" + qualifier + ":" + tname + "]";
138     }
139 
140     private static class MatchElem implements MatchElement {
141         private final Class<?> matchedType;
142         private final Class<?> patternType;
143         private final QualifierMatcher qualMatcher;
144 
145         private MatchElem(Class<?> mtype, Class<?> ptype, QualifierMatcher qmatch) {
146             matchedType = mtype;
147             patternType = ptype;
148             qualMatcher = qmatch;
149         }
150 
151         @Override
152         public ContextElements.MatchPriority getPriority() {
153             return ContextElements.MatchPriority.TYPE;
154         }
155 
156         @Override
157         public Integer getTypeDistance() {
158             return Types.getTypeDistance(matchedType, patternType);
159         }
160 
161         @Override
162         public boolean equals(Object o) {
163             if (this == o) {
164                 return true;
165             } else if (o instanceof MatchElem) {
166                 MatchElem other = (MatchElem) o;
167                 EqualsBuilder eqb = new EqualsBuilder();
168                 return eqb.append(matchedType, other.matchedType)
169                           .append(patternType, other.patternType)
170                           .append(qualMatcher, other.qualMatcher)
171                           .isEquals();
172             } else {
173                 return false;
174             }
175         }
176 
177         @Override
178         public int hashCode() {
179             HashCodeBuilder hcb = new HashCodeBuilder();
180             return hcb.append(matchedType)
181                       .append(patternType)
182                       .append(qualMatcher)
183                       .toHashCode();
184         }
185 
186         @Override
187         public String toString() {
188             return String.format("Match(%s,%s)", matchedType, patternType);
189         }
190     }
191 
192     private Object writeReplace() {
193         return new SerialProxy(type, qualifier);
194     }
195 
196     private void readObject(ObjectInputStream stream) throws ObjectStreamException {
197         throw new InvalidObjectException("must use serialization proxy");
198     }
199 
200     private static class SerialProxy implements Serializable {
201         private static final long serialVersionUID = 2L;
202 
203         private final ClassProxy type;
204         private final QualifierMatcher qualifier;
205 
206         public SerialProxy(Class<?> t, QualifierMatcher qual) {
207             type = ClassProxy.of(t);
208             qualifier = qual;
209         }
210 
211         @SuppressWarnings("unchecked")
212         private Object readResolve() throws ObjectStreamException {
213             try {
214                 return new TypeElementMatcher(type.resolve(),
215                                                            qualifier);
216             } catch (ClassNotFoundException e) {
217                 InvalidObjectException ex = new InvalidObjectException("cannot resolve " + type);
218                 ex.initCause(e);
219                 throw ex;
220             }
221         }
222     }
223 }