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.base.Preconditions;
     20 import com.google.common.collect.ImmutableSet;
     21 import com.google.common.collect.Lists;
     22 
     23 import java.util.List;
     24 
     25 /**
     26  * Provides access to the calling line of code.
     27  *
     28  * @author crazybob (at) google.com (Bob Lee)
     29  */
     30 public final class SourceProvider {
     31 
     32   /** Indicates that the source is unknown. */
     33   public static final Object UNKNOWN_SOURCE = "[unknown source]";
     34 
     35   private final SourceProvider parent;
     36   private final ImmutableSet<String> classNamesToSkip;
     37 
     38   public static final SourceProvider DEFAULT_INSTANCE
     39       = new SourceProvider(ImmutableSet.of(SourceProvider.class.getName()));
     40 
     41   private SourceProvider(Iterable<String> classesToSkip) {
     42     this(null, classesToSkip);
     43   }
     44 
     45   private SourceProvider(SourceProvider parent, Iterable<String> classesToSkip) {
     46     this.parent = parent;
     47 
     48     ImmutableSet.Builder<String> classNamesToSkipBuilder = ImmutableSet.builder();
     49     for (String classToSkip : classesToSkip) {
     50       if (parent == null || !parent.shouldBeSkipped(classToSkip)) {
     51         classNamesToSkipBuilder.add(classToSkip);
     52       }
     53     }
     54     this.classNamesToSkip = classNamesToSkipBuilder.build();
     55   }
     56 
     57   /** Returns a new instance that also skips {@code moreClassesToSkip}. */
     58   public SourceProvider plusSkippedClasses(Class... moreClassesToSkip) {
     59     return new SourceProvider(this, asStrings(moreClassesToSkip));
     60   }
     61 
     62   /** Returns true if the className should be skipped. */
     63   private boolean shouldBeSkipped(String className) {
     64     return (parent != null && parent.shouldBeSkipped(className))
     65         || classNamesToSkip.contains(className);
     66   }
     67 
     68   /** Returns the class names as Strings */
     69   private static List<String> asStrings(Class... classes) {
     70     List<String> strings = Lists.newArrayList();
     71     for (Class c : classes) {
     72       strings.add(c.getName());
     73     }
     74     return strings;
     75   }
     76 
     77   /**
     78    * Returns the calling line of code. The selected line is the nearest to the top of the stack that
     79    * is not skipped.
     80    */
     81   public StackTraceElement get(StackTraceElement[] stackTraceElements) {
     82     Preconditions.checkNotNull(stackTraceElements, "The stack trace elements cannot be null.");
     83     for (final StackTraceElement element : stackTraceElements) {
     84       String className = element.getClassName();
     85 
     86       if (!shouldBeSkipped(className)) {
     87         return element;
     88       }
     89     }
     90     throw new AssertionError();
     91   }
     92 
     93   /**
     94    * Returns the non-skipped module class name.
     95    */
     96   public Object getFromClassNames(List<String> moduleClassNames) {
     97     Preconditions.checkNotNull(moduleClassNames, "The list of module class names cannot be null.");
     98     for (final String moduleClassName : moduleClassNames) {
     99       if (!shouldBeSkipped(moduleClassName)) {
    100         return new StackTraceElement(moduleClassName, "configure", null, -1);
    101       }
    102     }
    103     return UNKNOWN_SOURCE;
    104   }
    105 }
    106