View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.annotation;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import org.apache.juneau.*;
24  import org.apache.juneau.commons.reflect.ExecutableException;
25  import org.junit.jupiter.api.*;
26  
27  class ParentPropertyAnnotation_Test extends TestBase {
28  
29  	private static final String CNAME = ParentPropertyAnnotation_Test.class.getName();
30  
31  	//------------------------------------------------------------------------------------------------------------------
32  	// Basic tests
33  	//------------------------------------------------------------------------------------------------------------------
34  
35  	ParentProperty a1 = ParentPropertyAnnotation.create()
36  		.description("a")
37  		.on("b")
38  		.build();
39  
40  	ParentProperty a2 = ParentPropertyAnnotation.create()
41  		.description("a")
42  		.on("b")
43  		.build();
44  
45  	@Test void a01_basic() {
46  		assertBean(a1, "description,on", "[a],[b]");
47  	}
48  
49  	@Test void a02_testEquivalency() {
50  		assertEquals(a2, a1);
51  		assertNotEqualsAny(a1.hashCode(), 0, -1);
52  		assertEquals(a1.hashCode(), a2.hashCode());
53  	}
54  
55  	//------------------------------------------------------------------------------------------------------------------
56  	// PropertyStore equivalency.
57  	//------------------------------------------------------------------------------------------------------------------
58  
59  	@Test void b01_testEquivalencyInPropertyStores() {
60  		var bc1 = BeanContext.create().annotations(a1).build();
61  		var bc2 = BeanContext.create().annotations(a2).build();
62  		assertSame(bc1, bc2);
63  	}
64  
65  	//------------------------------------------------------------------------------------------------------------------
66  	// Other methods.
67  	//------------------------------------------------------------------------------------------------------------------
68  
69  	public static class C1 {
70  		public int f1;
71  		public void m1() {}  // NOSONAR
72  	}
73  	public static class C2 {
74  		public int f2;
75  		public void m2() {}  // NOSONAR
76  	}
77  
78  	@Test void c01_otherMethods() throws Exception {
79  		var c1 = ParentPropertyAnnotation.create("a").on("b").build();
80  		var c2 = ParentPropertyAnnotation.create().on(C1.class.getField("f1")).on(C2.class.getField("f2")).build();
81  		var c3 = ParentPropertyAnnotation.create().on(C1.class.getMethod("m1")).on(C2.class.getMethod("m2")).build();
82  
83  		assertBean(c1, "on", "[a,b]");
84  		assertBean(c2, "on", "["+CNAME+"$C1.f1,"+CNAME+"$C2.f2]");
85  		assertBean(c3, "on", "["+CNAME+"$C1.m1(),"+CNAME+"$C2.m2()]");
86  	}
87  
88  	//------------------------------------------------------------------------------------------------------------------
89  	// Comparison with declared annotations.
90  	//------------------------------------------------------------------------------------------------------------------
91  
92  	@ParentProperty(
93  		description={ "a" },
94  		on="b"
95  	)
96  	public static class D1 {}
97  	ParentProperty d1 = D1.class.getAnnotationsByType(ParentProperty.class)[0];
98  
99  	@ParentProperty(
100 		description={ "a" },
101 		on="b"
102 	)
103 	public static class D2 {}
104 	ParentProperty d2 = D2.class.getAnnotationsByType(ParentProperty.class)[0];
105 
106 	@Test void d01_comparisonWithDeclarativeAnnotations() {
107 		assertEqualsAll(a1, d1, d2);
108 		assertNotEqualsAny(a1.hashCode(), 0, -1);
109 		assertEqualsAll(a1.hashCode(), d1.hashCode(), d2.hashCode());
110 	}
111 
112 	//------------------------------------------------------------------------------------------------------------------
113 	// Property functionality tests.
114 	//------------------------------------------------------------------------------------------------------------------
115 
116 	public static class ParentBean {
117 		public int value = 42;
118 	}
119 
120 	public static class TestBeanWithParentPropertyField {
121 		@ParentProperty
122 		public ParentBean parent;
123 	}
124 
125 	public static class TestBeanWithParentPropertyMethod {
126 		private ParentBean parent;
127 
128 		@ParentProperty
129 		protected void setParent(ParentBean parent) {
130 			this.parent = parent;
131 		}
132 
133 		public ParentBean getParent() {
134 			return parent;
135 		}
136 	}
137 
138 	@Test void e01_parentPropertyField() throws Exception {
139 		var bc = BeanContext.DEFAULT;
140 		var cm = bc.getClassMeta(TestBeanWithParentPropertyField.class);
141 		var prop = cm.getParentProperty();
142 		assertNotNull(prop, "ParentProperty should be found");
143 		assertTrue(prop.canWrite(), "Should have setter");
144 		assertTrue(prop.canRead(), "Should have getter");
145 
146 		var bean = new TestBeanWithParentPropertyField();
147 		var parent = new ParentBean();
148 		prop.set(bean, parent);
149 		assertSame(parent, prop.get(bean));
150 		assertSame(parent, bean.parent);
151 	}
152 
153 	@Test void e02_parentPropertyMethod() throws Exception {
154 		var bc = BeanContext.DEFAULT;
155 		var cm = bc.getClassMeta(TestBeanWithParentPropertyMethod.class);
156 		var prop = cm.getParentProperty();
157 		assertNotNull(prop, "ParentProperty should be found");
158 		assertTrue(prop.canWrite(), "Should have setter");
159 		assertTrue(prop.canRead(), "Should have getter");
160 
161 		var bean = new TestBeanWithParentPropertyMethod();
162 		var parent = new ParentBean();
163 		prop.set(bean, parent);
164 		assertSame(parent, prop.get(bean));
165 		assertSame(parent, bean.getParent());
166 	}
167 
168 	@Test void e03_parentPropertyNotFound() {
169 		var bc = BeanContext.DEFAULT;
170 		var cm = bc.getClassMeta(String.class);
171 		var prop = cm.getParentProperty();
172 		assertNull(prop, "ParentProperty should not be found on String class");
173 	}
174 
175 	public static class TestBeanWithReadOnlyParentProperty {
176 		private ParentBean parent = new ParentBean();
177 
178 		@ParentProperty
179 		public ParentBean getParent() {
180 			return parent;
181 		}
182 	}
183 
184 	@Test void e04_readOnlyParentProperty() throws Exception {
185 		var bc = BeanContext.DEFAULT;
186 		var cm = bc.getClassMeta(TestBeanWithReadOnlyParentProperty.class);
187 		var prop = cm.getParentProperty();
188 		assertNotNull(prop, "ParentProperty should be found even if read-only");
189 		assertFalse(prop.canWrite(), "Should not have setter");
190 		assertTrue(prop.canRead(), "Should have getter");
191 
192 		var bean = new TestBeanWithReadOnlyParentProperty();
193 		var parent = (ParentBean)prop.get(bean);
194 		assertNotNull(parent, "Should be able to get parent");
195 		assertEquals(42, parent.value);
196 
197 		// Verify that set() throws an exception
198 		var newParent = new ParentBean();
199 		var ex = assertThrows(ExecutableException.class, () -> prop.set(bean, newParent));
200 		assertTrue(ex.getMessage().contains("No setter defined"), "Should throw exception when trying to set read-only property");
201 	}
202 }