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.serializer;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.nio.charset.Charset;
23  import java.util.function.*;
24  
25  import org.apache.juneau.*;
26  import org.apache.juneau.commons.reflect.*;
27  import org.apache.juneau.json.*;
28  import org.apache.juneau.msgpack.*;
29  import org.apache.juneau.serializer.annotation.*;
30  import org.apache.juneau.svl.*;
31  import org.junit.jupiter.api.*;
32  
33  /**
34   * Tests the @SerializerConfig annotation.
35   */
36  class SerializerConfigAnnotation_Test extends TestBase {
37  
38  	private static void check(String expected, Object o) {
39  		assertEquals(expected, TO_STRING.apply(o));
40  	}
41  
42  	private static final Function<Object,String> TO_STRING = t -> {
43  		if (t == null)
44  			return null;
45  		if (t instanceof AA)
46  			return "AA";
47  		return t.toString();
48  	};
49  
50  	static VarResolverSession sr = VarResolver.create().vars(XVar.class).build().createSession();
51  
52  	//-----------------------------------------------------------------------------------------------------------------
53  	// Basic tests
54  	//-----------------------------------------------------------------------------------------------------------------
55  
56  	public static class AA extends SerializerListener {}
57  
58  	@SerializerConfig(
59  		addBeanTypes="$X{true}",
60  		addRootType="$X{true}",
61  		binaryFormat="$X{HEX}",
62  		detectRecursions="$X{true}",
63  		ignoreRecursions="$X{true}",
64  		initialDepth="$X{1}",
65  		listener=AA.class,
66  		maxDepth="$X{1}",
67  		maxIndent="$X{1}",
68  		quoteChar="$X{'}",
69  		sortCollections="$X{true}",
70  		sortMaps="$X{true}",
71  		trimEmptyCollections="$X{true}",
72  		trimEmptyMaps="$X{true}",
73  		keepNullProperties="$X{false}",
74  		trimStrings="$X{true}",
75  		uriContext="{}",
76  		uriRelativity="$X{RESOURCE}",
77  		uriResolution="$X{ABSOLUTE}",
78  		useWhitespace="$X{true}"
79  	)
80  	static class A {}
81  	static ClassInfo a = ClassInfo.of(A.class);
82  
83  	@Test void a01_basicWriterSerializer() {
84  		var al = AnnotationWorkList.of(sr, rstream(a.getAnnotations()));
85  		var x = JsonSerializer.create().apply(al).build().getSession();
86  		check("true", ((SerializerSession)x).isAddBeanTypes());
87  		check("true", x.isAddRootType());
88  		check("true", x.isDetectRecursions());
89  		check("true", x.isIgnoreRecursions());
90  		check("1", x.getInitialDepth());
91  		check("AA", x.getListener());
92  		check("1", x.getMaxDepth());
93  		check("1", x.getMaxIndent());
94  		check("'", x.getQuoteChar());
95  		check("true", x.isSortCollections());
96  		check("true", x.isSortMaps());
97  		check("true", x.isTrimEmptyCollections());
98  		check("true", x.isTrimEmptyMaps());
99  		check("false", x.isKeepNullProperties());
100 		check("true", x.isTrimStrings());
101 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
102 		check("RESOURCE", x.getUriRelativity());
103 		check("ABSOLUTE", x.getUriResolution());
104 		check("true", x.isUseWhitespace());
105 	}
106 
107 	@Test void a02_basicOutputStreamSerializer() {
108 		var al = AnnotationWorkList.of(sr, rstream(a.getAnnotations()));
109 		var x = MsgPackSerializer.create().apply(al).build().getSession();
110 		check("true", ((SerializerSession)x).isAddBeanTypes());
111 		check("true", x.isAddRootType());
112 		check("HEX", x.getBinaryFormat());
113 		check("true", x.isDetectRecursions());
114 		check("true", x.isIgnoreRecursions());
115 		check("1", x.getInitialDepth());
116 		check("AA", x.getListener());
117 		check("1", x.getMaxDepth());
118 		check("true", x.isSortCollections());
119 		check("true", x.isSortMaps());
120 		check("true", x.isTrimEmptyCollections());
121 		check("true", x.isTrimEmptyMaps());
122 		check("false", x.isKeepNullProperties());
123 		check("true", x.isTrimStrings());
124 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
125 		check("RESOURCE", x.getUriRelativity());
126 		check("ABSOLUTE", x.getUriResolution());
127 	}
128 
129 	//-----------------------------------------------------------------------------------------------------------------
130 	// Annotation with no values.
131 	//-----------------------------------------------------------------------------------------------------------------
132 
133 	@SerializerConfig()
134 	static class B {}
135 	static ClassInfo b = ClassInfo.of(B.class);
136 
137 	@Test void b01_noValuesWriterSerializer() {
138 		var al = AnnotationWorkList.of(sr, rstream(b.getAnnotations()));
139 		var x = JsonSerializer.create().apply(al).build().getSession();
140 		check("false", ((SerializerSession)x).isAddBeanTypes());
141 		check("false", x.isAddRootType());
142 		check(null, x.getListener());
143 		check("100", x.getMaxIndent());
144 		check("\"", x.getQuoteChar());
145 		check("false", x.isSortCollections());
146 		check("false", x.isSortMaps());
147 		check("false", x.isTrimEmptyCollections());
148 		check("false", x.isTrimEmptyMaps());
149 		check("false", x.isKeepNullProperties());
150 		check("false", x.isTrimStrings());
151 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
152 		check("RESOURCE", x.getUriRelativity());
153 		check("NONE", x.getUriResolution());
154 		check("false", x.isUseWhitespace());
155 	}
156 
157 	@Test void b02_noValuesOutputStreamSerializer() {
158 		var al = AnnotationWorkList.of(sr, rstream(b.getAnnotations()));
159 		var x = MsgPackSerializer.create().apply(al).build().getSession();
160 		check("false", ((SerializerSession)x).isAddBeanTypes());
161 		check("false", x.isAddRootType());
162 		check("HEX", x.getBinaryFormat());
163 		check(null, x.getListener());
164 		check("false", x.isSortCollections());
165 		check("false", x.isSortMaps());
166 		check("false", x.isTrimEmptyCollections());
167 		check("false", x.isTrimEmptyMaps());
168 		check("false", x.isKeepNullProperties());
169 		check("false", x.isTrimStrings());
170 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
171 		check("RESOURCE", x.getUriRelativity());
172 		check("NONE", x.getUriResolution());
173 	}
174 
175 	//-----------------------------------------------------------------------------------------------------------------
176 	// No annotation.
177 	//-----------------------------------------------------------------------------------------------------------------
178 
179 	static class C {}
180 	static ClassInfo c = ClassInfo.of(C.class);
181 
182 	@Test void c01_noAnnotationWriterSerializer() {
183 		var al = AnnotationWorkList.of(sr, rstream(c.getAnnotations()));
184 		var x = JsonSerializer.create().apply(al).build().getSession();
185 		check("false", ((SerializerSession)x).isAddBeanTypes());
186 		check("false", x.isAddRootType());
187 		check(null, x.getListener());
188 		check("100", x.getMaxIndent());
189 		check("\"", x.getQuoteChar());
190 		check("false", x.isSortCollections());
191 		check("false", x.isSortMaps());
192 		check("false", x.isTrimEmptyCollections());
193 		check("false", x.isTrimEmptyMaps());
194 		check("false", x.isKeepNullProperties());
195 		check("false", x.isTrimStrings());
196 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
197 		check("RESOURCE", x.getUriRelativity());
198 		check("NONE", x.getUriResolution());
199 		check("false", x.isUseWhitespace());
200 	}
201 
202 	@Test void c02_noAnnotationOutputStreamSerializer() {
203 		var al = AnnotationWorkList.of(sr, rstream(c.getAnnotations()));
204 		var x = MsgPackSerializer.create().apply(al).build().getSession();
205 		check("false", ((SerializerSession)x).isAddBeanTypes());
206 		check("false", x.isAddRootType());
207 		check("HEX", x.getBinaryFormat());
208 		check(null, x.getListener());
209 		check("false", x.isSortCollections());
210 		check("false", x.isSortMaps());
211 		check("false", x.isTrimEmptyCollections());
212 		check("false", x.isTrimEmptyMaps());
213 		check("false", x.isKeepNullProperties());
214 		check("false", x.isTrimStrings());
215 		check("{aContextRoot=/,aPathInfo=/,aServletPath=/,rContextRoot=/,rPath=/,rResource=/}", x.getUriContext());
216 		check("RESOURCE", x.getUriRelativity());
217 		check("NONE", x.getUriResolution());
218 	}
219 
220 	//-----------------------------------------------------------------------------------------------------------------
221 	// Error cases - invalid values
222 	//-----------------------------------------------------------------------------------------------------------------
223 
224 	@SerializerConfig(
225 		quoteChar="$X{ab}"
226 	)
227 	static class D {}
228 	static ClassInfo d = ClassInfo.of(D.class);
229 
230 	@Test void d01_invalidQuoteChar_throwsException() {
231 		var al = AnnotationWorkList.of(sr, rstream(d.getAnnotations()));
232 		assertThrows(ConfigException.class, () -> {
233 			JsonSerializer.create().apply(al).build();
234 		});
235 	}
236 
237 	@SerializerConfig(
238 		fileCharset="$X{UTF-8}",
239 		streamCharset="$X{ISO-8859-1}"
240 	)
241 	static class E {}
242 	static ClassInfo e = ClassInfo.of(E.class);
243 
244 	@Test void d02_charsetWithNonDefaultValue() {
245 		var al = AnnotationWorkList.of(sr, rstream(e.getAnnotations()));
246 		var x = JsonSerializer.create().apply(al).build().getSession();
247 		check("UTF-8", x.getFileCharset().name());
248 		check("ISO-8859-1", x.getStreamCharset().name());
249 	}
250 
251 	@SerializerConfig(
252 		fileCharset="$X{default}",
253 		streamCharset="$X{DEFAULT}"
254 	)
255 	static class E2 {}
256 	static ClassInfo e2 = ClassInfo.of(E2.class);
257 
258 	@Test void d02b_charsetWithDefaultValue() {
259 		var al = AnnotationWorkList.of(sr, rstream(e2.getAnnotations()));
260 		var x = JsonSerializer.create().apply(al).build().getSession();
261 		check(Charset.defaultCharset().name(), x.getFileCharset().name());
262 		check(Charset.defaultCharset().name(), x.getStreamCharset().name());
263 	}
264 
265 	@SerializerConfig(
266 		initialDepth="$X{abc}",
267 		maxDepth="$X{xyz}",
268 		maxIndent="$X{invalid}"
269 	)
270 	static class F {}
271 	static ClassInfo f = ClassInfo.of(F.class);
272 
273 	@Test void d03_invalidInteger_throwsException() {
274 		var al = AnnotationWorkList.of(sr, rstream(f.getAnnotations()));
275 		assertThrows(ConfigException.class, () -> {
276 			JsonSerializer.create().apply(al).build();
277 		});
278 	}
279 }