
com.google.gwt.inject.rebind.util.NameGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gin Show documentation
Show all versions of gin Show documentation
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.
/*
* 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.dev.util.Preconditions;
import com.google.gwt.inject.rebind.output.FragmentPackageName;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* Helper to generate various names for members of a {@code Ginjector}
* implementation.
*/
public class NameGenerator {
private class CacheKey {
private final String prefix;
private final Key> key;
CacheKey(String prefix, Key> key) {
this.prefix = prefix;
this.key = key;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CacheKey) {
CacheKey other = (CacheKey) obj;
return other.prefix.equals(prefix) && other.key.equals(key);
}
return false;
}
@Override
public int hashCode() {
return prefix.hashCode() * 31 + key.hashCode();
}
}
/**
* "Mangled key name" cache: Key -> mangled name
*/
private final Map methodKeyCache = new LinkedHashMap();
/**
* Map of known method names.
*/
private final Set methodNames = new LinkedHashSet();
/**
* Returns the name of an assisted injection helper method.
*/
public String getAssistedInjectMethodName(Key> factoryKey, String methodName) {
return mangle("assistedInject_" + methodName, factoryKey);
}
/**
* Returns the name of a getter for a child injector.
*/
public String getChildInjectorGetterMethodName(String childInjectorClassName) {
return convertToValidMemberName("getChild_" + childInjectorClassName);
}
/**
* Returnst he name of a getter for an injector fragment.
*/
public String getFragmentGetterMethodName(FragmentPackageName fragmentPackageName) {
return "getFragment_" + fragmentPackageName.toString().replace(".", "_");
}
/**
* Returns the key's getter method name. The method with that name can be
* called to retrieve an instance of the type described by the key.
*
* @return getter method name
*/
public String getGetterMethodName(Key> key) {
return mangle("get_", key);
}
/**
* Computes the name of a single fragment of a Ginjector.
*
* @param injectorClassName the simple name of the injector's class (not
* including its package)
*/
public String getFragmentClassName(String injectorClassName,
FragmentPackageName fragmentPackageName) {
// Sanity check.
Preconditions.checkArgument(!injectorClassName.contains("."),
"The injector class must be a simple name, but it was \"%s\"", injectorClassName);
// Note that the fragment package name is not actually included in the
// fragment. This reduces the length of the fragment's class name, which is
// important because some systems have small limits on the maximum length of
// a file (e.g., ~256 characters). However, it means that other parts of
// Gin must reference the fragment using its canonical class name, to avoid
// ambiguity.
return injectorClassName + "_fragment";
}
/**
* Computes the canonical name (including package) of a single fragment of a
* Ginjector.
*/
public String getFragmentCanonicalClassName(String injectorClassName,
FragmentPackageName fragmentPackageName) {
return fragmentPackageName + "." + getFragmentClassName(injectorClassName, fragmentPackageName);
}
/**
* Computes the field name of a single fragment of an injector.
*/
public String getFragmentFieldName(FragmentPackageName fragmentPackageName) {
return convertToValidMemberName("fieldFragment_" + fragmentPackageName);
}
/**
* Computes the name of the field in which the Ginjector interface
* implementation is stored.
*/
public String getGinjectorInterfaceFieldName() {
return "fieldGinjectorInterface";
}
/**
* Computes the name of the method used to retrieve the Ginjector interface
* implementation.
*/
public String getGinjectorInterfaceGetterMethodName() {
return "getGinjectorInterface";
}
/**
* Returns the type's member inject method name. The method with that name
* can be called with a single parameter to inject members of that parameter.
*
* @return member inject method name
*/
public String getMemberInjectMethodName(TypeLiteral> type) {
return mangle("memberInject_", Key.get(type));
}
/**
* Returns the key's singleton field name.
*
* @return singleton field name
*/
public String getSingletonFieldName(Key> key) {
return mangle("singleton_", key);
}
/**
* Returns a new valid (i.e. unique) method name based on {@code base}.
*
* Note: Method names are considered "used" after being returned by this
* method, whether they're actually used to write a new method or not.
*
* @param base base on which method name gets created
* @return valid method name
*/
public String createMethodName(String base) {
while (methodNames.contains(base)) {
// TODO(schmitt): Make more efficient for repeated calls with same base?
base += "_";
}
methodNames.add(base);
return base;
}
/**
* Reserves the given name to prevent new methods to be created with it.
*
* @param name name to be reserved
*/
public void markAsUsed(String name) throws IllegalArgumentException {
methodNames.add(name);
}
private String mangle(String prefix, Key> key) {
CacheKey cacheKey = new CacheKey(prefix, key);
String cached = methodKeyCache.get(cacheKey);
if (cached != null) {
return cached;
}
// TODO(bstoler): This algorithm is kinda crazy because the annotation
// values are of unbounded length. One option
// is to use mangled(type) + mangled(annotation type) + counter, where
// counter is used just to distinguish different annotation values.
String name = prefix + key.toString();
name = convertToValidMemberName(name);
name = createMethodName(name);
methodKeyCache.put(cacheKey, name);
return name;
}
public String convertToValidMemberName(String name) {
name = name.replaceAll("\\s+", "_");
name = name.replaceAll("[^\\p{Alnum}_]", "\\$");
return name;
}
// Static for access from enum.
public static String replaceLast(String source, char toReplace, char with) {
StringBuilder sb = new StringBuilder(source);
int index = sb.lastIndexOf(String.valueOf(toReplace));
if (index != -1) {
sb.setCharAt(index, with);
}
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy