All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.inject.rebind.util.MemberCollector Maven / Gradle / Ivy

Go to download

GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.

There is a newer version: 2.1.2
Show newest version
/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.gwt.inject.rebind.util;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.inject.Inject;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.HashSet;

/**
 * This class can be used to collect a type's members, including those of its
 * (recursive) superclasses and interfaces.  The collector treats overrides
 * correctly, i.e. it returns the method defined the closest to the provided
 * type.
 * 

Note: The collector uses internal caching and can be called with the same * arguments repeatedly without repeated performance costs. *

This class is not thread-safe. */ public class MemberCollector { public static final MethodFilter ALL_METHOD_FILTER = new MethodFilter() { public boolean accept(JMethod method) { return true; } }; // TODO(schmitt): Add constructor collector? /** * Filter used during the collection of methods to restrict the kind of * collected methods. *

* Note: The method filter influences override detection! If a method A * overrides a method A*, without a filter only A would be collected. If the * filter only accepts A* and not A, A will not be collected, and A* will be * collected despite being overriden by A. */ public static interface MethodFilter { boolean accept(JMethod method); } /** * Filter used during the collection of fields to restrict the kind of * collected fields. */ public static interface FieldFilter { boolean accept(JField field); } /** * Comparator which detects methods that are override-equal. * * The comparator assumes that both classes have been investigated for java * specification compliance. */ private static final Comparator METHOD_COMPARATOR = new Comparator() { public int compare(JMethod m1, JMethod m2) { if (m1 == m2) { return 0; } int nameCompare = m1.getName().compareTo(m2.getName()); if (nameCompare != 0) { return nameCompare; } if (m1.getParameters().length != m2.getParameters().length) { return m1.getParameters().length - m2.getParameters().length; } for (int i = 0; i < m1.getParameters().length; i++) { String param1 = m1.getParameters()[i].getType().getQualifiedSourceName(); String param2 = m2.getParameters()[i].getType().getQualifiedSourceName(); int paramCompare = param1.compareTo(param2); if (paramCompare != 0) { return paramCompare; } } /* If either of the methods is private, it is either (a) in the * superclass, and thus invisible to the subclass, or (b) in the * subclass, which implies that the method must be private in the * superclass as well. * * If either of the methods has default access and the classes are not in * the same package then they are invisible to each other and thus not * override-equivalent. */ if (m1.isPrivate() || m2.isPrivate() || ((m1.isDefaultAccess() || m2.isDefaultAccess()) && !samePackage(m1, m2))) { return m1.getEnclosingType().getQualifiedSourceName().compareTo( m2.getEnclosingType().getQualifiedSourceName()); } // Methods have same name, parameter types and compatible visibility return 0; } private boolean samePackage(JMethod m1, JMethod m2) { JPackage p1 = m1.getEnclosingType().getPackage(); JPackage p2 = m2.getEnclosingType().getPackage(); if (p1 == null || p2 == null) { return p1 == p2; } return (p1.isDefault() && p2.isDefault()) || (p1.getName().equals(p2.getName())); } }; /** * Internal method cache: Type name -> Method Set */ private final Map> methodMultiMap = new HashMap>(); /** * Internal field cache: Type name -> Method Set */ private final Map> fieldMultiMap = new HashMap>(); private final TreeLogger logger; /** * Method filter that this collector operates with. */ private MethodFilter methodFilter; /** * Field filter that this collector operates with. */ private FieldFilter fieldFilter; /** * Locking status. The collector is locked once it started to accumulate * members. No filters can be set on the collector after it has been locked. */ private boolean locked; @Inject public MemberCollector(TreeLogger logger) { this.logger = logger; this.locked = false; } /** * Sets this collector's method filter. This method can only be called * before members are requested. * * @param filter new method filter for this collector * @throws IllegalStateException if the filter is set after members have been * requested */ public void setMethodFilter(MethodFilter filter) throws IllegalStateException { if (locked) { String msg = "A filter can only be set on this collector before members are requested!"; logger.log(TreeLogger.Type.ERROR, msg); throw new IllegalStateException(msg); } this.methodFilter = filter; } /** * Sets this collector's field filter. This method can only be called before * members are requested. * * @param filter new field filter for this collector * @throws IllegalStateException if the filter is set after members have been * requested */ public void setFieldFilter(FieldFilter filter) throws IllegalStateException { if (locked) { String msg = "A filter can only be set on this collector before members are requested!"; logger.log(TreeLogger.Type.ERROR, msg); throw new IllegalStateException(msg); } this.fieldFilter = filter; } /** * Returns all methods in the provided type, including those of the type's * (recursive) super classes and interfaces. Treats overloads correctly. If * no method filter is set will return an empty set. * * @param type type for which methods are collected * @return all methods for the given type */ public Collection getMethods(JClassType type) { collect(type); String typeName = type.getParameterizedQualifiedSourceName(); return Collections.unmodifiableCollection(methodMultiMap.get(typeName)); } /** * Returns all fields in the provided type, including those of the type's * (recursive) super classes. If no field filter is set will return an empty * set. * * @param type type for which fields are collected * @return all fields for the given type */ public Collection getFields(JClassType type) { collect(type); String typeName = type.getParameterizedQualifiedSourceName(); return Collections.unmodifiableCollection(fieldMultiMap.get(typeName)); } private void collect(JClassType type) { locked = true; String typeName = type.getParameterizedQualifiedSourceName(); if (methodMultiMap.containsKey(typeName)) { return; } // Type hasn't been collected yet. Set typeMethods = new TreeSet(METHOD_COMPARATOR); Set typeFields = new HashSet(); accumulateMembers(type, typeMethods, typeFields); methodMultiMap.put(typeName, typeMethods); fieldMultiMap.put(typeName, typeFields); } private void accumulateMembers(JClassType type, Set methodAccu, Set fieldAccu) { String typeName = type.getParameterizedQualifiedSourceName(); if (methodFilter != null) { if (methodMultiMap.containsKey(typeName)) { for (JMethod method : methodMultiMap.get(typeName)) { methodAccu.add(method); } } else { for (JMethod method : type.getMethods()) { if (methodFilter.accept(method)) { methodAccu.add(method); logger.log(TreeLogger.TRACE, "Found method: " + type.getName() + "#" + method.getReadableDeclaration()); } else { logger.log(TreeLogger.DEBUG, "Ignoring method: " + type.getName() + "#" + method.getReadableDeclaration()); } } } } if (fieldFilter != null) { if (fieldMultiMap.containsKey(typeName)) { for (JField field : fieldMultiMap.get(typeName)) { fieldAccu.add(field); } } else { for (JField field : type.getFields()) { if (fieldFilter.accept(field)) { fieldAccu.add(field); logger.log(TreeLogger.TRACE, "Found field: " + type.getName() + "#" + field.getName()); } else { logger.log(TreeLogger.DEBUG, "Ignoring field: " + type.getName() + "#" + field.getName()); } } } } for (JClassType ancestor : type.getImplementedInterfaces()) { accumulateMembers(ancestor, methodAccu, fieldAccu); } JClassType ancestor = type.getSuperclass(); if (ancestor != null) { accumulateMembers(ancestor, methodAccu, fieldAccu); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy