Java tutorial
/* * Grapht, an open source dependency injector. * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt) * Copyright 2010-2014 Regents of the University of Minnesota * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.grouplens.grapht.solver; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.grouplens.grapht.CachePolicy; import org.grouplens.grapht.reflect.Desire; import org.grouplens.grapht.reflect.QualifierMatcher; import org.grouplens.grapht.reflect.Satisfaction; import org.grouplens.grapht.util.ClassProxy; import org.grouplens.grapht.util.Preconditions; import org.grouplens.grapht.util.Types; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.EnumSet; /** * Foundational implementation of {@link BindRule}. * * @author <a href="http://grouplens.org">GroupLens Research</a> */ final class BindRuleImpl implements BindRule, Serializable { private static final long serialVersionUID = -1L; private final Satisfaction satisfaction; private final EnumSet<BindingFlag> flags; private final QualifierMatcher qualifier; private final Class<?> depType; private final Class<?> implType; private final CachePolicy policy; private transient volatile int hashCode; /** * Create a bind rule that matches a desire when the desired type equals * <tt>depType</tt> and the desire's qualifier matches <tt>qualifier</tt> * . * * @param depType The dependency type this bind rule matches * @param satisfaction The Satisfaction used by applied desires * @param policy The CachePolicy for nodes created by this bind rule * @param qualifier The Qualifier the bind rule applies to * @param flags The flags to apply to this bind rule and its results. * @throws NullPointerException if arguments are null */ public BindRuleImpl(@Nonnull Class<?> depType, @Nonnull Satisfaction satisfaction, @Nonnull CachePolicy policy, @Nonnull QualifierMatcher qualifier, EnumSet<BindingFlag> flags) { Preconditions.notNull("dependency type", depType); Preconditions.notNull("satisfaction", satisfaction); Preconditions.notNull("policy", policy); Preconditions.notNull("qualifier matcher", qualifier); this.qualifier = qualifier; this.satisfaction = satisfaction; this.implType = satisfaction.getErasedType(); this.policy = policy; this.depType = Types.box(depType); this.flags = flags.clone(); // verify that the satisfaction produces proper types Preconditions.isAssignable(this.depType, this.implType); } /** * As the other constructor, but this is used for type to type bindings * where the implementation type is not yet instantiable, so there is no * satisfaction for the applied desires. * * @param depType The dependency type this bind rule matches * @param implType The implementation type that is bound * @param policy The CachePolicy for nodes created by this bind rule * @param qualifier The Qualifier the bind rule applies to * @param flags The flags to apply to this bind rule and its results. * @throws NullPointerException if arguments are null */ public BindRuleImpl(@Nonnull Class<?> depType, @Nonnull Class<?> implType, @Nonnull CachePolicy policy, @Nonnull QualifierMatcher qualifier, EnumSet<BindingFlag> flags) { Preconditions.notNull("dependency type", depType); Preconditions.notNull("implementation type", implType); Preconditions.notNull("policy", policy); Preconditions.notNull("qualifier matcher", qualifier); this.qualifier = qualifier; this.satisfaction = null; this.implType = Types.box(implType); this.policy = policy; this.depType = Types.box(depType); this.flags = flags.clone(); // verify that implType extends depType Preconditions.isAssignable(this.depType, this.implType); } /** * Get the rule's qualifier matcher. * * @return The annotation {@link QualifierMatcher} matched by this bind rule. */ public QualifierMatcher getQualifierMatcher() { return qualifier; } @Override public CachePolicy getCachePolicy() { return policy; } @Override public Desire apply(Desire desire) { // TODO Separate bind rules into different classes based on sat vs. type targets if (satisfaction != null) { return desire.restrict(satisfaction); } else { return desire.restrict(implType); } } @Override public EnumSet<BindingFlag> getFlags() { return flags; } @Override public boolean isTerminal() { return flags.contains(BindingFlag.TERMINAL); } @Override public boolean matches(Desire desire) { // bind rules match type by equality if (desire.getDesiredType().equals(depType)) { // if the type is equal, then rely on the qualifier matcher return qualifier.matches(desire.getInjectionPoint().getQualifier()); } // the type and {@link Qualifier}s are not a match, so return false return false; } @Override public BindRuleBuilder newCopyBuilder() { BindRuleBuilder bld = new BindRuleBuilder(); bld.setDependencyType(depType).setQualifierMatcher(qualifier).setCachePolicy(policy).setFlags(flags); if (satisfaction != null) { bld.setSatisfaction(satisfaction); } else { bld.setImplementation(implType); } return bld; } @Override public int compareTo(BindRule other) { if (other instanceof BindRuleImpl) { return qualifier.compareTo(((BindRuleImpl) other).qualifier); } else { throw new IllegalArgumentException("incompatible bind rule"); } } @Override public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof BindRuleImpl) { EqualsBuilder eq = new EqualsBuilder(); BindRuleImpl or = (BindRuleImpl) o; return eq.append(depType, or.depType).append(implType, or.implType).append(flags, or.flags) .append(qualifier, or.qualifier).append(policy, or.policy).append(satisfaction, or.satisfaction) .isEquals(); } else { return false; } } @Override public int hashCode() { if (hashCode == 0) { HashCodeBuilder hcb = new HashCodeBuilder(); hashCode = hcb.append(flags).append(depType).append(implType).append(qualifier).append(policy) .append(satisfaction).toHashCode(); } return hashCode; } @Override public String toString() { String i = (satisfaction == null ? implType.getSimpleName() : satisfaction.toString()); return "Bind(" + qualifier + ":" + depType.getSimpleName() + " -> " + i + ", " + flags + ")"; } private Object writeReplace() { return new SerialProxy(satisfaction, flags, qualifier, depType, implType, policy); } private void readObject(ObjectInputStream stream) throws ObjectStreamException { throw new InvalidObjectException("must use serialization proxy"); } /** * Serialization proxy class. */ private static class SerialProxy implements Serializable { private static final long serialVersionUID = 2L; private final ClassProxy depType; private final QualifierMatcher qualifier; private final ClassProxy implType; @Nullable private final Satisfaction satisfaction; private final EnumSet<BindingFlag> flags; private final CachePolicy cachePolicy; private SerialProxy(@Nullable Satisfaction sat, EnumSet<BindingFlag> flags, QualifierMatcher qmatch, Class<?> stype, Class<?> itype, CachePolicy policy) { satisfaction = sat; this.flags = flags; qualifier = qmatch; depType = ClassProxy.of(stype); implType = ClassProxy.of(itype); cachePolicy = policy; } private Object readResolve() throws ObjectStreamException { try { if (satisfaction == null) { return new BindRuleImpl(depType.resolve(), implType.resolve(), cachePolicy, qualifier, flags); } else { return new BindRuleImpl(depType.resolve(), satisfaction, cachePolicy, qualifier, flags); } } catch (ClassNotFoundException e) { InvalidObjectException ex = new InvalidObjectException("cannot resolve type"); ex.initCause(e); throw ex; } } } }