1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.grapher;
18 
19 import com.google.common.base.Joiner;
20 import com.google.common.collect.Lists;
21 import com.google.inject.Key;
22 import com.google.inject.TypeLiteral;
23 import com.google.inject.internal.ProviderMethod;
24 import com.google.inject.internal.util.StackTraceElements;
25 import com.google.inject.spi.ElementSource;
26 import java.lang.annotation.Annotation;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Member;
29 import java.lang.reflect.Method;
30 import java.util.List;
31 
32 /**
33  * Reasonable implementation for {@link NameFactory}. Mostly takes various {@link
34  * Object#toString()}s and strips package names out of them so that they'll fit on the graph.
35  *
36  * @author phopkins@gmail.com (Pete Hopkins)
37  */
38 public class ShortNameFactory implements NameFactory {
39   @Override
getMemberName(Member member)40   public String getMemberName(Member member) {
41     if (member instanceof Constructor) {
42       return "<init>";
43     } else if (member instanceof Method) {
44       return "#" + member.getName() + "(...)";
45     } else {
46       return member.getName();
47     }
48   }
49 
50   @Override
getAnnotationName(Key<?> key)51   public String getAnnotationName(Key<?> key) {
52     Annotation annotation = key.getAnnotation();
53     Class<? extends Annotation> annotationType = key.getAnnotationType();
54     if (annotation != null) {
55       annotationType = annotation.annotationType();
56 
57       String annotationString = annotation.toString();
58       String canonicalName = annotationType.getName();
59       String simpleName = annotationType.getSimpleName();
60 
61       return annotationString.replace(canonicalName, simpleName).replace("()", "");
62     } else if (annotationType != null) {
63       return "@" + annotationType.getSimpleName();
64     } else {
65       return "";
66     }
67   }
68 
69   @Override
getClassName(Key<?> key)70   public String getClassName(Key<?> key) {
71     TypeLiteral<?> typeLiteral = key.getTypeLiteral();
72     return stripPackages(typeLiteral.toString());
73   }
74 
75   @Override
getInstanceName(Object instance)76   public String getInstanceName(Object instance) {
77     if (instance instanceof ProviderMethod) {
78       return getMethodString(((ProviderMethod<?>) instance).getMethod());
79     }
80 
81     if (instance instanceof CharSequence) {
82       return "\"" + instance + "\"";
83     }
84 
85     try {
86       if (instance.getClass().getMethod("toString").getDeclaringClass().equals(Object.class)) {
87         return stripPackages(instance.getClass().getName());
88       }
89     } catch (SecurityException e) {
90       throw new AssertionError(e);
91     } catch (NoSuchMethodException e) {
92       throw new AssertionError(e);
93     }
94 
95     return instance.toString();
96   }
97 
98   /**
99    * Returns a name for a Guice "source" object. This will typically be either a {@link
100    * StackTraceElement} for when the binding is made to the instance, or a {@link Method} when a
101    * provider method is used.
102    */
103   @Override
getSourceName(Object source)104   public String getSourceName(Object source) {
105     if (source instanceof ElementSource) {
106       source = ((ElementSource) source).getDeclaringSource();
107     }
108     if (source instanceof Method) {
109       source = StackTraceElements.forMember((Method) source);
110     }
111 
112     if (source instanceof StackTraceElement) {
113       return getFileString((StackTraceElement) source);
114     }
115 
116     return stripPackages(source.toString());
117   }
118 
getFileString(StackTraceElement stackTraceElement)119   protected String getFileString(StackTraceElement stackTraceElement) {
120     return stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber();
121   }
122 
getMethodString(Method method)123   protected String getMethodString(Method method) {
124     List<String> paramStrings = Lists.newArrayList();
125     for (Class<?> paramType : method.getParameterTypes()) {
126       paramStrings.add(paramType.getSimpleName());
127     }
128 
129     String paramString = Joiner.on(", ").join(paramStrings);
130     return "#" + method.getName() + "(" + paramString + ")";
131   }
132 
133   /**
134    * Eliminates runs of lowercase characters and numbers separated by periods. Seems to remove
135    * packages from fully-qualified type names pretty well.
136    */
stripPackages(String str)137   private String stripPackages(String str) {
138     return str.replaceAll("(^|[< .\\(])([a-z0-9]+\\.)*", "$1");
139   }
140 }
141