This default can be changed in deployment configuration.</li>
<li>Server-side timings of request processing are only sent to the client when not in production mode. Using the
timings in TestBench tests requires the server not to be in production mode.</li>
+ <li>The annotations @PreserveOnRefresh, @Push, @Theme, @Title, @VaadinServletConfiguration and @Widgetset now use
+ @Inherited. The annotation is also looked up in extended interfaces for backwards compatibility.</li>
</ul>
<h3 id="knownissues">Known Issues and Limitations</h3>
<ul>
package com.vaadin.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
+@Inherited
public @interface PreserveOnRefresh {
// Empty marker annotation
}
package com.vaadin.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Inherited
public @interface Push {
/**
* Returns the {@link PushMode} to use for the annotated UI. The default
package com.vaadin.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Inherited
public @interface Theme {
/**
* @return simple name of the theme
package com.vaadin.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Inherited
public @interface Title {
/**
* Gets the HTML title that should be used if the UI is used on it's own.
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Inherited
public @interface VaadinServletConfiguration {
/**
* Defines the init parameter name for methods in
package com.vaadin.annotations;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
+@Inherited
public @interface Widgetset {
/**
* @return name of the widgetset
import java.io.Serializable;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.annotations.Push;
/**
* Helper to get an annotation for a class. If the annotation is not present
- * on the target class, its super classes and implemented interfaces are
- * also searched for the annotation.
+ * on the target class, its super classes and directly implemented
+ * interfaces are also searched for the annotation. Interfaces implemented
+ * by superclasses are not taken into account.
+ * <p>
+ * Note that searching implemented interfaces for {@code @Inherited}
+ * annotations and searching for superclasses for non-inherited annotations
+ * do not follow the standard semantics and are supported for backwards
+ * compatibility. Future versions of the framework might only support the
+ * standard semantics of {@code @Inherited}.
*
* @param clazz
* the class from which the annotation should be found
*/
protected static <T extends Annotation> T getAnnotationFor(Class<?> clazz,
Class<T> annotationType) {
- // Find from the class hierarchy
- Class<?> currentType = clazz;
- while (currentType != Object.class) {
- T annotation = currentType.getAnnotation(annotationType);
+ // Don't discover hierarchy if annotation is inherited
+ if (annotationType.getAnnotation(Inherited.class) != null) {
+ T annotation = clazz.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
- } else {
- currentType = currentType.getSuperclass();
+ }
+ } else {
+ // Find from the class hierarchy
+ Class<?> currentType = clazz;
+ while (currentType != Object.class) {
+ T annotation = currentType.getAnnotation(annotationType);
+ if (annotation != null) {
+ return annotation;
+ } else {
+ currentType = currentType.getSuperclass();
+ }
}
}
- // Find from an implemented interface
+ // Find from a directly implemented interface
for (Class<?> iface : clazz.getInterfaces()) {
T annotation = iface.getAnnotation(annotationType);
if (annotation != null) {
--- /dev/null
+/*
+ * Copyright 2000-2014 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.server;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.annotations.Theme;
+import com.vaadin.annotations.Widgetset;
+
+/**
+ * Tests for {@link UIProvider} class.
+ *
+ * @author Vaadin Ltd
+ */
+public class UIProviderTest {
+
+ @Test
+ public void getAnnotationFor_widgetsetAnnotationForSubclass_annotationFound() {
+ Assert.assertNotNull("Widgetset annotation is not found for subclass",
+ UIProvider.getAnnotationFor(TestClass.class, Widgetset.class));
+ }
+
+ @Test
+ public void getAnnotationFor_themeAnnotationForSubclass_annotationFound() {
+ Assert.assertNotNull("Theme annotation is not found for subclass",
+ UIProvider.getAnnotationFor(TestClass.class, Theme.class));
+ }
+
+ @Test
+ public void getAnnotationFor_themeAnnotationForSubclass_annotationOverridden() {
+ Assert.assertEquals(
+ "Theme annotation is not overridden correctly in subclass",
+ "c", UIProvider.getAnnotationFor(TestClass.class, Theme.class)
+ .value());
+ }
+
+ @Test
+ public void getAnnotationFor_notInheritedAnnotationForSubclass_annotationFound() {
+ Assert.assertNotNull(
+ "TestAnnotation annotation is not found for subclass",
+ UIProvider.getAnnotationFor(TestClass.class,
+ TestAnnotation.class));
+ }
+
+ @Test
+ public void getAnnotationFor_directAnnotationForSubclass_annotationFound() {
+ Assert.assertNotNull(
+ "TestAnnotation1 annotation is not found for subclass",
+ UIProvider.getAnnotationFor(TestClass.class,
+ TestAnnotation1.class));
+ }
+
+ @Test
+ public void getAnnotationFor_annotationInheritedFromInterface_annotationFound() {
+ Assert.assertNotNull(
+ "Theme annotation is not inherited from interface", UIProvider
+ .getAnnotationFor(ClassImplementingInterface.class,
+ Theme.class));
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public @interface TestAnnotation {
+
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ public @interface TestAnnotation1 {
+
+ }
+
+ @Widgetset("a")
+ @Theme("b")
+ @TestAnnotation
+ public static class TestSuperClass {
+
+ }
+
+ @TestAnnotation1
+ @Theme("c")
+ public static class TestClass extends TestSuperClass {
+
+ }
+
+ @Theme("d")
+ public interface InterfaceWithAnnotation {
+ }
+
+ public static class ClassImplementingInterface implements
+ InterfaceWithAnnotation {
+ }
+
+}