001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.google.inject.spi;
016:
017: import java.util.Collections;
018: import java.util.HashSet;
019: import java.util.Set;
020:
021: /**
022: * Provides access to the default {@link SourceProvider} implementation and
023: * common controls for certain implementations.
024: *
025: * @author crazybob@google.com (Bob Lee)
026: */
027: public class SourceProviders {
028:
029: private SourceProviders() {
030: }
031:
032: public static final Object UNKNOWN_SOURCE = "[unknown source]";
033:
034: static final SourceProvider DEFAULT_INSTANCE = new StacktraceSourceProvider();
035:
036: static Set<String> skippedClassNames = Collections.emptySet();
037:
038: static {
039: skip(SourceProviders.class);
040: skip(StacktraceSourceProvider.class);
041: }
042:
043: /**
044: * Instructs stacktrace-based providers to skip the given class in the stack
045: * trace when determining the source. Use this to keep the binder from
046: * logging utility methods as the sources of bindings (i.e. it will skip to
047: * the utility methods' callers instead).
048: *
049: * <p>Skipping only takes place after this method is called.
050: */
051: public synchronized static void skip(Class<?> clazz) {
052: Set<String> copy = new HashSet<String>();
053: copy.addAll(skippedClassNames);
054: copy.add(clazz.getName());
055: skippedClassNames = Collections.unmodifiableSet(copy);
056: }
057:
058: /**
059: * Gets the set of class names which should be skipped by stacktrace-based
060: * providers.
061: */
062: public synchronized static Set<String> getSkippedClassNames() {
063: return skippedClassNames;
064: }
065:
066: static ThreadLocal<SourceProvider[]> localSourceProvider = new ThreadLocal<SourceProvider[]>() {
067: protected SourceProvider[] initialValue() {
068: return new SourceProvider[] { DEFAULT_INSTANCE };
069: }
070: };
071:
072: /**
073: * Returns the current source obtained from the default provider.
074: */
075: public static Object defaultSource() {
076: return localSourceProvider.get()[0].source();
077: }
078:
079: /**
080: * Sets the default source provider, runs the given command, and then
081: * restores the previous default source provider.
082: */
083: public static void withDefault(SourceProvider sourceProvider,
084: Runnable r) {
085: // We use a holder so we perform only 1 thread local access instead of 3.
086: SourceProvider[] holder = localSourceProvider.get();
087: SourceProvider previous = holder[0];
088: try {
089: holder[0] = sourceProvider;
090: r.run();
091: } finally {
092: holder[0] = previous;
093: }
094: }
095:
096: static class StacktraceSourceProvider implements SourceProvider {
097: public Object source() {
098: // Search up the stack until we find a class outside of this one.
099: Set<String> skippedClassNames = getSkippedClassNames();
100: for (final StackTraceElement element : new Throwable()
101: .getStackTrace()) {
102: String className = element.getClassName();
103: if (!skippedClassNames.contains(className)) {
104: return element;
105: }
106: }
107: throw new AssertionError();
108: }
109: }
110: }
|