/*
* Copyright (C) 2010 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.guiceberry;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Module;
import com.google.inject.testing.guiceberry.junit3.GuiceBerryEnvRemapper;
/**
* See {@link GuiceBerryEnvSelector}.
*
* <p>This is the default implementation of {@link GuiceBerryEnvSelector}. The GuiceBerry Env
* to use is the class (or its name) given as a parameter to one the {@link #of}
* static factory methods, except when the {@link #override} feature is used.
*
* @author Luiz-Otavio "Z" Zorzella
*/
public class DefaultEnvSelector implements GuiceBerryEnvSelector {
public static final String LINK_TO_UPGRADING_DOC =
"For more details, see http://guiceberry.googlecode.com, section 'Upgrading from 2.0 to 3.0'";
private static final String OVERRIDE_SYSTEM_PROPERY_NAME = "GuiceBerryEnvSelectorOverride";
private final String clazzName;
private DefaultEnvSelector(String clazzName) {
this.clazzName = clazzName;
}
/**
* Specifies {@code clazz} as the GuiceBerry Env to use for a test, except
* when the {@link #override} feature is used.
*
* @see #of(String)
*/
public static GuiceBerryEnvSelector of(Class<? extends Module> guiceBerryEnvClazz) {
return of(guiceBerryEnvClazz.getName());
}
/**
* Use this version of the static factory method instead of {@link #of(Class)}
* if you wish to not have a compile-time dependency between your test and
* your GuiceBerry Env. See TODO for more details.
*
* @see #of(Class)
*/
public synchronized static GuiceBerryEnvSelector of(String guiceBerryEnvClazzName) {
Class<? extends Module> override = getOverride(guiceBerryEnvClazzName);
if (System.getProperty(GuiceBerryEnvRemapper.GUICE_BERRY_ENV_REMAPPER_PROPERTY_NAME) != null) {
System.out.println(String.format(
"********* ATTENTION ***********\n" +
"I see you have the deprecated '%s' system property set, which is. " +
"honored anymore. " +
LINK_TO_UPGRADING_DOC,
GuiceBerryEnvRemapper.GUICE_BERRY_ENV_REMAPPER_PROPERTY_NAME));
}
if (override != null) {
return new DefaultEnvSelector(override.getName());
} else {
return new DefaultEnvSelector(guiceBerryEnvClazzName);
}
}
public Class<? extends Module> guiceBerryEnvToUse(TestDescription testDescription) {
Class<? extends Module> result = getGbeFromClazzName();
Class<? extends Module> override = getOverride(result.getName());
if (override != null) {
result = override;
}
return result;
}
@VisibleForTesting
@SuppressWarnings("unchecked")
static Class<? extends Module> getOverride(String guiceBerryEnvName) {
String overrideName = getOverrideName(guiceBerryEnvName);
if (overrideName != null) {
if (System.getProperty(GuiceBerryEnvRemapper.GUICE_BERRY_ENV_REMAPPER_PROPERTY_NAME) != null) {
throw new IllegalArgumentException(String.format(
"Both the '%s' and the deprecated '%s' system properties are set. " +
"To fix this, stop using the deprecated system property. " +
LINK_TO_UPGRADING_DOC,
OVERRIDE_SYSTEM_PROPERY_NAME,
GuiceBerryEnvRemapper.GUICE_BERRY_ENV_REMAPPER_PROPERTY_NAME));
}
@SuppressWarnings("rawtypes")
Class clazz;
try {
clazz = DefaultEnvSelector.class.getClassLoader().loadClass(overrideName);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(String.format(
"Class '%s' does not exist, and it is being declared as a '%s' override (though the '%s' System Property).",
overrideName,
DefaultEnvSelector.class.getName(),
OVERRIDE_SYSTEM_PROPERY_NAME
), e);
}
if (Module.class.isAssignableFrom(clazz)) {
return clazz;
}
throw new IllegalArgumentException(String.format(
"Class '%s' is being declared as a GuiceBerryEnvSelector, but does not implement that interface",
overrideName));
}
return null;
}
@SuppressWarnings("unchecked")
private Class<? extends Module> getGbeFromClazzName() {
Class<?> temp = getGbeClassFromClassName(clazzName);
if (!Module.class.isAssignableFrom(temp)) {
String msg = String.format(
"The '%s' class must be a Guice Module (i.e. implement com.google.inject.Module).",
clazzName);
throw new IllegalArgumentException(msg);
}
return (Class<? extends Module>) temp;
}
static Class<?> getGbeClassFromClassName(String gbeName) {
Class<?> className;
try {
className = DefaultEnvSelector.class.getClassLoader().loadClass(gbeName);
} catch (ClassNotFoundException e) {
String msg = String.format(
"Class '%s' was not found.",
gbeName.toString());
throw new IllegalArgumentException(msg, e);
}
return className;
}
/**
* Overrides the {@code declaredGuiceBerryEnv} with the
* {@code guiceBerryEnvOverride}.
*
* <p>The {@link DefaultEnvSelector} class provides a simple mechanism for
* overriding a particular GuiceBerry Env with another. If, for a given
* GuiceBerry Env, a {@link System} property named like the pattern in
* {@link #buildSystemPropertyName(String)} is set, the class whose name is
* the value for that property is used instead of the declared GuiceBerry Env.
*
* <p>For details about this feature, see tutorial TODO
*
* <p>This method is a convenience method in case you wish to set that system
* property programmatically.
*
* <p>Note that this override is honored by code inside the {@link #of}
* methods, so this {@link #override} method must be called <em>before</em>
* calling those.
*
* @throws IllegalArgumentException if you have either already called this
* method before, or otherwise set the {@link #OVERRIDE_SYSTEM_PROPERY_NAME}
* (say by passing a -D system property to the java runtime).
*
* @see #clearOverride(Class)
*/
public static synchronized void override(
Class<? extends Module> declaredGuiceBerryEnv,
Class<? extends Module> guiceBerryEnvOverride) {
if (isOverridden(declaredGuiceBerryEnv.getName())) {
throw new IllegalArgumentException(String.format(
"The GuiceBerry Env '%s' is already overridden by '%s'.",
declaredGuiceBerryEnv.getName(), getOverrideName(declaredGuiceBerryEnv.getName())));
}
System.setProperty(buildSystemPropertyName(declaredGuiceBerryEnv.getName()),
guiceBerryEnvOverride.getName());
}
/**
* Clears the {@link System} property override for the
* {@code declaredGuiceBerryEnv}.
*/
public static synchronized void clearOverride(
Class<? extends Module> declaredGuiceBerryEnv) {
System.clearProperty(buildSystemPropertyName(declaredGuiceBerryEnv.getName()));
}
/**
* Returns true if the {@code declaredGuiceBerryEnv} is being overridden.
*/
public static synchronized boolean isOverridden(
Class<? extends Module> declaredGuiceBerryEnvClass) {
return isOverridden(declaredGuiceBerryEnvClass.getName());
}
/**
* Returns true if the {@code declaredGuiceBerryEnv} is being overridden.
*/
public static synchronized boolean isOverridden(String declaredGuiceBerryEnvName) {
return getOverrideName(declaredGuiceBerryEnvName) != null;
}
/**
* Returns the name of the GuiceBerry Env that overrides
* {@code declaredGuiceBerryEnvName}, or {@code null} if there is no override.
*/
private static String getOverrideName(String declaredGuiceBerryEnvName) {
return System.getProperty(buildSystemPropertyName(declaredGuiceBerryEnvName));
}
/**
* Returns the name of the {@link System} property that, when set, will cause
* {@link DefaultEnvSelector#of} to override the {@code declaredGuiceBerryEnv}
* with the value of the {@link System} property.
*/
public static String buildSystemPropertyName(String declaredGuiceBerryEnvName) {
return OVERRIDE_SYSTEM_PROPERY_NAME + "_" + declaredGuiceBerryEnvName;
}
}