]> source.dussan.org Git - vaadin-framework.git/commitdiff
Use @Inherited for annotations that have inherited semantic (#12351).
authorDenis Anisimov <denis@vaadin.com>
Sat, 22 Nov 2014 10:27:01 +0000 (12:27 +0200)
committerelmot <elmot@vaadin.com>
Thu, 14 Apr 2016 10:34:11 +0000 (13:34 +0300)
Change-Id: I70f0d79095bfd05b83905df6a3eedd1a5e93b48e

WebContent/release-notes.html
server/src/com/vaadin/annotations/PreserveOnRefresh.java
server/src/com/vaadin/annotations/Push.java
server/src/com/vaadin/annotations/Theme.java
server/src/com/vaadin/annotations/Title.java
server/src/com/vaadin/annotations/VaadinServletConfiguration.java
server/src/com/vaadin/annotations/Widgetset.java
server/src/com/vaadin/server/UIProvider.java
server/tests/src/com/vaadin/server/UIProviderTest.java [new file with mode: 0644]

index bccb867c4d7d568bc1f2158d40db197fbb5a7db0..debf544ed59c7af396dc78ca0eede293d1fe7ad0 100644 (file)
                 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>
index 7f4ef3ffe56f28d1c2bd7c1b8c54c98f2bf0a5c2..86723b97fcadef6ad317664e721b1c40d8df5f9b 100644 (file)
@@ -17,6 +17,7 @@
 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;
@@ -45,6 +46,7 @@ import com.vaadin.ui.UI;
  */
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
+@Inherited
 public @interface PreserveOnRefresh {
     // Empty marker annotation
 }
index b6a28c1560f83f7160697bdaf15c92c69947ec13..736bc4488b2671cd4facffa6feefa4abcd4a60b7 100644 (file)
@@ -17,6 +17,7 @@
 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;
@@ -38,6 +39,7 @@ import com.vaadin.ui.UI;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Inherited
 public @interface Push {
     /**
      * Returns the {@link PushMode} to use for the annotated UI. The default
index 61c47389add430e6c4d960db79aba42fb94dc6ef..03fa1179bc488077db1559333fc5b073391a0b1a 100644 (file)
@@ -17,6 +17,7 @@
 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;
@@ -28,6 +29,7 @@ import com.vaadin.ui.UI;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Inherited
 public @interface Theme {
     /**
      * @return simple name of the theme
index 38a3d75f178e24b4ba8746952fdae95751d4955a..07eaf17e33840fe6711e4d30e9413ab60d5b71b0 100644 (file)
@@ -17,6 +17,7 @@
 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;
@@ -28,6 +29,7 @@ import com.vaadin.ui.UI;
  */
 @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.
index 00af38ecc1b99799931df955d712f80a9de2bdcd..907f2c3a0cdd85a382556ddab5291f7588c98ea8 100644 (file)
@@ -18,6 +18,7 @@ package com.vaadin.annotations;
 
 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;
@@ -44,6 +45,7 @@ import com.vaadin.ui.UI;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Inherited
 public @interface VaadinServletConfiguration {
     /**
      * Defines the init parameter name for methods in
index c6ef6a7194a42b0b15f0a09e82bdb2505f0cb4ce..2dcf93af135f7ba846b327c57f8e407cd071dbc7 100644 (file)
@@ -17,6 +17,7 @@
 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;
@@ -28,6 +29,7 @@ import com.vaadin.ui.UI;
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Inherited
 public @interface Widgetset {
     /**
      * @return name of the widgetset
index d3d834cad79cceaf4233cf291d3aa61ff5efa827..4ed86b9c31b0e8b61e4cf025469193d35b7eb628 100644 (file)
@@ -18,6 +18,7 @@ package com.vaadin.server;
 
 import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
 
 import com.vaadin.annotations.PreserveOnRefresh;
 import com.vaadin.annotations.Push;
@@ -43,8 +44,15 @@ public abstract class UIProvider implements Serializable {
 
     /**
      * 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
@@ -55,18 +63,26 @@ public abstract class UIProvider implements Serializable {
      */
     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) {
diff --git a/server/tests/src/com/vaadin/server/UIProviderTest.java b/server/tests/src/com/vaadin/server/UIProviderTest.java
new file mode 100644 (file)
index 0000000..579a3f1
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 {
+    }
+
+}