Home | History | Annotate | Download | only in util
      1 /**
      2  * Copyright (C) 2006 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.internal.util;
     18 
     19 import com.google.common.cache.CacheBuilder;
     20 import com.google.common.cache.CacheLoader;
     21 import com.google.common.cache.LoadingCache;
     22 import com.google.common.collect.MapMaker;
     23 
     24 import java.io.IOException;
     25 import java.lang.reflect.Constructor;
     26 import java.lang.reflect.Member;
     27 import java.util.Map;
     28 
     29 /**
     30  * Creates stack trace elements for members.
     31  *
     32  * @author crazybob (at) google.com (Bob Lee)
     33  */
     34 public class StackTraceElements {
     35 
     36   private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
     37   private static final InMemoryStackTraceElement[] EMPTY_INMEMORY_STACK_TRACE =
     38       new InMemoryStackTraceElement[0];
     39 
     40   /*if[AOP]*/
     41   static final LoadingCache<Class<?>, LineNumbers> lineNumbersCache =
     42       CacheBuilder.newBuilder().weakKeys().softValues().build(
     43           new CacheLoader<Class<?>, LineNumbers>() {
     44             public LineNumbers load(Class<?> key) {
     45               try {
     46                 return new LineNumbers(key);
     47               }
     48               catch (IOException e) {
     49                 throw new RuntimeException(e);
     50               }
     51             }
     52           });
     53   /*end[AOP]*/
     54 
     55   private static Map<Object, Object> cache = new MapMaker().makeMap();
     56   private static final String UNKNOWN_SOURCE = "Unknown Source";
     57 
     58   public static Object forMember(Member member) {
     59     if (member == null) {
     60       return SourceProvider.UNKNOWN_SOURCE;
     61     }
     62 
     63     Class declaringClass = member.getDeclaringClass();
     64 
     65     /*if[AOP]*/
     66     LineNumbers lineNumbers = lineNumbersCache.getUnchecked(declaringClass);
     67     String fileName = lineNumbers.getSource();
     68     Integer lineNumberOrNull = lineNumbers.getLineNumber(member);
     69     int lineNumber = lineNumberOrNull == null ? lineNumbers.getFirstLine() : lineNumberOrNull;
     70     /*end[AOP]*/
     71     /*if[NO_AOP]
     72     String fileName = null;
     73     int lineNumber = -1;
     74     end[NO_AOP]*/
     75 
     76     Class<? extends Member> memberType = Classes.memberType(member);
     77     String memberName = memberType == Constructor.class ? "<init>" : member.getName();
     78     return new StackTraceElement(declaringClass.getName(), memberName, fileName, lineNumber);
     79   }
     80 
     81   public static Object forType(Class<?> implementation) {
     82     /*if[AOP]*/
     83     LineNumbers lineNumbers = lineNumbersCache.getUnchecked(implementation);
     84     int lineNumber = lineNumbers.getFirstLine();
     85     String fileName = lineNumbers.getSource();
     86     /*end[AOP]*/
     87     /*if[NO_AOP]
     88     String fileName = null;
     89     int lineNumber = -1;
     90     end[NO_AOP]*/
     91 
     92     return new StackTraceElement(implementation.getName(), "class", fileName, lineNumber);
     93   }
     94 
     95   /**
     96    * Clears the internal cache for {@link StackTraceElement StackTraceElements}.
     97    */
     98   public static void clearCache() {
     99     cache.clear();
    100   }
    101 
    102   /**
    103    * Returns encoded in-memory version of {@link StackTraceElement StackTraceElements}.
    104    */
    105   public static InMemoryStackTraceElement[] convertToInMemoryStackTraceElement(
    106       StackTraceElement[] stackTraceElements) {
    107     if (stackTraceElements.length == 0) {
    108       return EMPTY_INMEMORY_STACK_TRACE;
    109     }
    110     InMemoryStackTraceElement[] inMemoryStackTraceElements =
    111         new InMemoryStackTraceElement[stackTraceElements.length];
    112     for (int i = 0; i < stackTraceElements.length; i++) {
    113       inMemoryStackTraceElements[i] =
    114           weakIntern(new InMemoryStackTraceElement(stackTraceElements[i]));
    115     }
    116     return inMemoryStackTraceElements;
    117   }
    118 
    119   /**
    120    * Decodes in-memory stack trace elements to regular {@link StackTraceElement StackTraceElements}.
    121    */
    122   public static StackTraceElement[] convertToStackTraceElement(
    123       InMemoryStackTraceElement[] inMemoryStackTraceElements) {
    124     if (inMemoryStackTraceElements.length == 0) {
    125       return EMPTY_STACK_TRACE;
    126     }
    127     StackTraceElement[] stackTraceElements =
    128         new StackTraceElement[inMemoryStackTraceElements.length];
    129     for (int i = 0; i < inMemoryStackTraceElements.length; i++) {
    130       String declaringClass = inMemoryStackTraceElements[i].getClassName();
    131       String methodName = inMemoryStackTraceElements[i].getMethodName();
    132       int lineNumber = inMemoryStackTraceElements[i].getLineNumber();
    133       stackTraceElements[i] =
    134           new StackTraceElement(declaringClass, methodName, UNKNOWN_SOURCE, lineNumber);
    135     }
    136     return stackTraceElements;
    137   }
    138 
    139   private static InMemoryStackTraceElement weakIntern(
    140       InMemoryStackTraceElement inMemoryStackTraceElement) {
    141     InMemoryStackTraceElement cached =
    142         (InMemoryStackTraceElement) cache.get(inMemoryStackTraceElement);
    143     if (cached != null) {
    144       return cached;
    145     }
    146     inMemoryStackTraceElement = new InMemoryStackTraceElement(
    147         weakIntern(inMemoryStackTraceElement.getClassName()),
    148         weakIntern(inMemoryStackTraceElement.getMethodName()),
    149         inMemoryStackTraceElement.getLineNumber());
    150     cache.put(inMemoryStackTraceElement, inMemoryStackTraceElement);
    151     return inMemoryStackTraceElement;
    152   }
    153 
    154   private static String weakIntern(String s) {
    155     String cached = (String) cache.get(s);
    156     if (cached != null) {
    157       return cached;
    158     }
    159     cache.put(s, s);
    160     return s;
    161   }
    162 
    163   /**
    164    * In-Memory version of {@link StackTraceElement} that does not store the file name.
    165    */
    166   public static class InMemoryStackTraceElement {
    167     private String declaringClass;
    168     private String methodName;
    169     private int lineNumber;
    170 
    171     InMemoryStackTraceElement(StackTraceElement ste) {
    172       this(ste.getClassName(), ste.getMethodName(), ste.getLineNumber());
    173     }
    174 
    175     InMemoryStackTraceElement(String declaringClass, String methodName, int lineNumber) {
    176       this.declaringClass = declaringClass;
    177       this.methodName = methodName;
    178       this.lineNumber = lineNumber;
    179     }
    180 
    181     String getClassName() {
    182       return declaringClass;
    183     }
    184 
    185     String getMethodName() {
    186       return methodName;
    187     }
    188 
    189     int getLineNumber() {
    190       return lineNumber;
    191     }
    192 
    193     @Override
    194     public boolean equals(Object obj) {
    195       if (obj == this) {
    196         return true;
    197       }
    198       if (!(obj instanceof InMemoryStackTraceElement)) {
    199         return false;
    200       }
    201       InMemoryStackTraceElement e = (InMemoryStackTraceElement) obj;
    202       return e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber &&
    203           methodName.equals(e.methodName);
    204     }
    205 
    206     @Override
    207     public int hashCode() {
    208       int result = 31 * declaringClass.hashCode() + methodName.hashCode();
    209       result = 31 * result + lineNumber;
    210       return result;
    211     }
    212 
    213     @Override
    214     public String toString() {
    215       return declaringClass + "." + methodName + "(" + lineNumber + ")";
    216     }
    217   }
    218 }
    219