/* * Copyright 2000-2016 Vaadin Ltd. * * 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.vaadin.tests.components; import java.io.File; import java.io.FileInputStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.server.VaadinRequest; import com.vaadin.ui.Component; import com.vaadin.ui.declarative.Design; /** * Declarative test UI. Provides simple instantiation of HTML designs located * under {@code uitest/src}. Also provides {@link OnLoad} annotation that lets * you easily hook up methods to run after the UI has been created. Note: you * <i>must</i> add the {@link DeclarativeUI} annotation to your subclass; not * doing this will result in program failure. */ @SuppressWarnings("serial") public class DeclarativeTestUI extends AbstractTestUI { private Logger logger; private Component component; /** * Class marker indicating the design .html file to load */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public static @interface DeclarativeUI { String value(); /** * Set this property to true if you provide an absolute path to your * design; otherwise, the DeclarativeTestUI logic will look for the HTML * design file under {@code vaadin_project/uitest/src/<package path>/}. */ boolean absolutePath() default false; } /** * Method marker interface indicating that a method should be run after the * declarative UI has been created */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public static @interface OnLoad { } /** * Figure out the proper path for the HTML design file */ private String getDesignPath() { Class<?> clazz = getClass(); String designFilePath = null; if (clazz.getAnnotation(DeclarativeUI.class).absolutePath()) { designFilePath = ""; } else { // This is rather nasty.. but it works well enough for now. String userDir = System.getProperty("user.dir"); designFilePath = userDir + "/uitest/src/" + clazz.getPackage().getName().replace('.', '/') + "/"; } String designFileName = clazz.getAnnotation(DeclarativeUI.class) .value(); return designFilePath + designFileName; } private Component readDesign() throws Exception { String path = getDesignPath(); getLogger().log(Level.INFO, "Reading design from " + path); File file = new File(path); return Design.read(new FileInputStream(file)); } @Override protected void setup(VaadinRequest request) { Class<?> clazz = getClass(); if (clazz.isAnnotationPresent(DeclarativeUI.class)) { // Create component try { component = readDesign(); } catch (Exception e1) { getLogger().log(Level.SEVERE, "Error reading design", e1); return; } addComponent(component); // Call on-load methods (if applicable) Method[] methods = clazz.getMethods(); for (Method m : methods) { if (m.isAnnotationPresent(OnLoad.class)) { try { m.invoke(this, (Object[]) null); } catch (IllegalAccessException e) { getLogger().log(Level.SEVERE, "Error invoking @OnLoad method", e); return; } catch (IllegalArgumentException e) { getLogger().log(Level.SEVERE, "Error invoking @OnLoad method", e); return; } catch (InvocationTargetException e) { getLogger().log(Level.SEVERE, "Error invoking @OnLoad method", e); return; } } } } else { throw new IllegalStateException( "Cannot find declarative UI annotation"); } } /** * Get access to the declaratively created component. This method typecasts * the component to the receiving type; if there's a mismatch between what * you expect and what's written in the design, this will fail with a * ClassCastException. * * @return a Vaadin component */ @SuppressWarnings("unchecked") public <T extends Component> T getComponent() { try { return (T) component; } catch (ClassCastException ex) { getLogger().log(Level.SEVERE, "Component code/design type mismatch", ex); } return null; } /** * Get access to the logger of this class * * @return a Logger instance */ protected Logger getLogger() { if (logger == null) { logger = Logger.getLogger(getClass().getName()); } return logger; } }