package fi.utu.ville.exercises.stub;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.logging.Logger;
import org.reflections.Reflections;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.UI;
import fi.utu.ville.exercises.model.ExerciseTypeDescriptor;
import fi.utu.ville.standardutils.StandardUIFactory;
import fi.utu.ville.standardutils.ui.DynamicStyles;
/**
* An abstract class that can be extended to create a simple Vaadin application that can be used for testing and developing Ville-Exercise-Types
*
* @author Riku Haavisto
*/
@Theme(value = "ville-theme")
@Title(value = "Ville exercise-type stub")
@StyleSheet({ "fonts/Lato.ttf", "fonts/fonts.css" })
public abstract class VilleExerStubUI extends UI {
/**
*
*/
private static final long serialVersionUID = -6292932364056918285L;
private static final Logger logger = Logger.getLogger(VilleExerStubUI.class
.getName());
private static final String LOCALES_TO_TEST_PARAM = "localesToTest";
private static final String STUB_RES_PATH_PARAM = "stubResPath";
private static final String TYPES_TO_LOAD_PARAM = "typesToLoad";
@SuppressWarnings("rawtypes")
private static final Class<? extends ExerciseTypeDescriptor> edTypeClass = ExerciseTypeDescriptor.class;
@Override
public void init(VaadinRequest req) {
setStyleName("main-style");
// parses required data (exer-types to test, locales to test,
// resource-directory)
// and then initiates the stub-UI
List<ExerciseTypeDescriptor<?, ?>> typesToLoad = getTypesToLoad();
List<Locale> localesToTest = getLocalesToTest();
String stubResPath = getStubResourceBaseDir();
if (typesToLoad.isEmpty() || stubResPath == null
|| localesToTest.isEmpty()) {
setContent(StandardUIFactory
.getWarningPanel("Types-to-load and locales-to-test must be non-empty, and stub-base-resource-dir not null. <br/>"
+ "Types-to-load: "
+ typesToLoad
+ "; locales-to-test: "
+ localesToTest
+ "; stub-res-path: " + stubResPath));
} else {
// add the stub-styles implementation for Dynamic-Styles
DynamicStyles.registerDynamicStylesFactory(new StubStylesFactory());
StubSessionData.initIfNeeded(typesToLoad, stubResPath,
localesToTest);
setContent(new StubStartView());
}
}
/**
* Returns the list of {@link ExerciseTypeDescriptor}s describing the exercise-types that will be loaded for testing in the stub.
*
* @return list of {@link ExerciseTypeDescriptor}s to be loaded for testing
*/
protected List<ExerciseTypeDescriptor<?, ?>> getTypesToLoad() {
{
Set<Class<?>> potentialClasses;
List<ExerciseTypeDescriptor<?, ?>> res = new ArrayList<ExerciseTypeDescriptor<?, ?>>();
String toLoadClasses = VaadinServlet.getCurrent()
.getServletContext().getInitParameter(TYPES_TO_LOAD_PARAM);
if (toLoadClasses != null && !toLoadClasses.equals("")) {
potentialClasses = new HashSet<Class<?>>();
String[] classes = toLoadClasses.split(";");
for (String cls : classes) {
try {
Class<?> aCls = Class.forName(cls);
potentialClasses.add(aCls);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} else {
potentialClasses = findPotentialClasses();
}
for (Class<?> potentialClass : potentialClasses) {
ExerciseTypeDescriptor<?, ?> etDesc = parseDescFromCls(potentialClass);
if (etDesc != null) {
res.add(etDesc);
}
}
return res;
}
}
/**
* Method for trying to find {@link ExerciseTypeDescriptor}s by reflection if none are explicitly set to be loaded.
*
* @return all {@link ExerciseTypeDescriptor}-instances that might represent {@link ExerciseTypeDescriptor} that could be loaded for testing in the stub
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Set<Class<?>> findPotentialClasses() {
logger.info("No exer-types to load were explicitly set");
logger.info("Trying to find exer-types from classpath with package fi.utu.ville.*");
Reflections reflections = new Reflections("fi.utu.ville", "edu.vserver");
Set<Class<? extends ExerciseTypeDescriptor>> subTypes = reflections
.getSubTypesOf(ExerciseTypeDescriptor.class);
return (Set) subTypes;
}
/**
* Instantiates an {@link ExerciseTypeDescriptor} from the given {@link Class} if that is possible (eg. class is not abstract helper class).
*
* @param parseFrom
* {@link Class}-file representing the class that will be parsed
* @return {@link ExerciseTypeDescriptor} parsed or null if one cannot be parsed
*/
private static ExerciseTypeDescriptor<?, ?> parseDescFromCls(
Class<?> parseFrom) {
ExerciseTypeDescriptor<?, ?> res = null;
// abstract-classes can only represent 'helpers'
try {
// first try to find a field containing type-descriptor as a
// singleton-like instance
Field[] clsFields = parseFrom.getFields();
for (Field field : clsFields) {
if (Modifier.isStatic(field.getModifiers())
&& edTypeClass.isAssignableFrom(field.getType())) {
res = edTypeClass.cast(field.get(null));
}
}
// if no field is found try to instantiate the class itself (if not
// abstract)
if (res == null && !Modifier.isAbstract(parseFrom.getModifiers())
&& edTypeClass.isAssignableFrom(parseFrom)) {
res = edTypeClass.cast(parseFrom.newInstance());
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return res;
}
/**
* Returns path to directory where the stub should store it files.
*
* @return path to the base-directory of stub-resources
*/
protected String getStubResourceBaseDir() {
String pathToRes = VaadinServlet.getCurrent().getServletContext()
.getInitParameter(STUB_RES_PATH_PARAM);
if (pathToRes == null || pathToRes.equals("")) {
pathToRes = VaadinServlet.getCurrent().getServletContext()
.getRealPath("/VILLE/stub");
// likely deployed as war, use temp-location
if (pathToRes == null) {
logger.warning("Placing VILLE-stub-resources-directory under temp");
pathToRes = System.getProperty("java.io.tmpdir") + "/VILLE/stub";
}
}
logger.info("Used VILLE-stub resource path: " + pathToRes);
return pathToRes;
}
/**
* Return a list of Locales that will be available in the testing UI.
*
* @return {@link List} of {@link Locale}s
*/
protected List<Locale> getLocalesToTest() {
String localesStr = VaadinServlet.getCurrent().getServletContext()
.getInitParameter(LOCALES_TO_TEST_PARAM);
List<Locale> res = new ArrayList<Locale>();
if (localesStr != null && !localesStr.equals("")) {
String[] locales = localesStr.split(";");
for (String aLocal : locales) {
if (aLocal.contains("_")) {
String[] langCountry = aLocal.split("_");
res.add(new Locale(langCountry[0], langCountry[1]));
} else {
res.add(new Locale(aLocal));
}
}
} else {
res.add(Locale.ENGLISH);
}
return res;
}
}