001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau; 014 015import static org.apache.juneau.Context.*; 016 017import java.lang.reflect.*; 018import java.util.*; 019 020import org.apache.juneau.annotation.*; 021import org.apache.juneau.csv.annotation.*; 022import org.apache.juneau.html.annotation.*; 023import org.apache.juneau.http.*; 024import org.apache.juneau.internal.*; 025import org.apache.juneau.jso.annotation.*; 026import org.apache.juneau.json.annotation.*; 027import org.apache.juneau.jsonschema.annotation.*; 028import org.apache.juneau.msgpack.annotation.*; 029import org.apache.juneau.oapi.annotation.*; 030import org.apache.juneau.parser.annotation.*; 031import org.apache.juneau.plaintext.annotation.*; 032import org.apache.juneau.reflect.*; 033import org.apache.juneau.serializer.*; 034import org.apache.juneau.serializer.annotation.*; 035import org.apache.juneau.soap.annotation.*; 036import org.apache.juneau.svl.*; 037import org.apache.juneau.transform.*; 038import org.apache.juneau.uon.annotation.*; 039import org.apache.juneau.urlencoding.annotation.*; 040import org.apache.juneau.xml.annotation.*; 041 042/** 043 * Base builder class for building instances of any context objects configured through property stores. 044 */ 045public abstract class ContextBuilder { 046 047 /** Contains all the modifiable settings for the implementation class. */ 048 private final PropertyStoreBuilder psb; 049 050 /** 051 * Constructor. 052 * Default settings. 053 */ 054 public ContextBuilder() { 055 this.psb = PropertyStore.create(); 056 } 057 058 /** 059 * Constructor that takes in an initial set of configuration properties. 060 * 061 * @param ps The initial configuration settings for this builder. 062 */ 063 public ContextBuilder(PropertyStore ps) { 064 if (ps == null) 065 ps = PropertyStore.DEFAULT; 066 this.psb = ps.builder(); 067 } 068 069 /** 070 * Constructor. 071 * 072 * <p> 073 * Used in cases where multiple context builder are sharing the same property store builder. 074 * <br>(e.g. <c>HtmlDocBuilder</c>) 075 * 076 * @param psb The property store builder to use. 077 */ 078 protected ContextBuilder(PropertyStoreBuilder psb) { 079 this.psb = psb; 080 } 081 082 /** 083 * Returns access to the inner property store builder. 084 * 085 * <p> 086 * Used in conjunction with {@link #ContextBuilder(PropertyStoreBuilder)} when builders share property store builders. 087 * 088 * @return The inner property store builder. 089 */ 090 protected PropertyStoreBuilder getPropertyStoreBuilder() { 091 return psb; 092 } 093 094 /** 095 * Build the object. 096 * 097 * @return 098 * The built object. 099 * <br>Subsequent calls to this method will create new instances (unless context object is cacheable). 100 */ 101 public abstract Context build(); 102 103 /** 104 * Copies the settings from the specified property store into this builder. 105 * 106 * <h5 class='section'>Example:</h5> 107 * <p class='bcode w800'> 108 * <jc>// Create a free-form set of properties.</jc> 109 * PropertyStore ps = PropertyStore 110 * .<jsm>create</jsm>() 111 * .set(<jsf>BEAN_sortMaps</jsf>, <jk>true</jk>) 112 * .set(<jsf>BEAN_sortProperties</jsf>, <jk>true</jk>) 113 * .build(); 114 * 115 * <jc>// Create a serializer that uses those settings.</jc> 116 * WriterSerializer s = JsonSerializer 117 * .<jsm>create</jsm>() 118 * .apply(ps) 119 * .build(); 120 * </p> 121 * 122 * @param copyFrom The property store whose settings are being copied. 123 * @return This object (for method chaining). 124 */ 125 @FluentSetter 126 public ContextBuilder apply(PropertyStore copyFrom) { 127 this.psb.apply(copyFrom); 128 return this; 129 } 130 131 /** 132 * Applies a set of annotations to this property store. 133 * 134 * <p> 135 * The {@link AnnotationList} object is an ordered list of annotations and the classes/methods/packages they were found on. 136 * 137 * <h5 class='section'>Example:</h5> 138 * <p class='bcode w800'> 139 * <jc>// A class annotated with a config annotation.</jc> 140 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 141 * <jk>public class</jk> MyClass {...} 142 * 143 * <jc>// Find all annotations that themselves are annotated with @PropertyStoreApply.</jc> 144 * AnnotationList al = ClassInfo.<jsm>of</jsm>(MyClass.<jk>class</jk>) 145 * .getAnnotationList(ConfigAnnotationFilter.<jsf>INSTANCE</jsf>); 146 * 147 * <jc>// Use the default VarResolver to resolve any variables in the annotation fields.</jc> 148 * VarResolverSession vs = VarResolver.<jsf>DEFAULT</jsf>.createSession(); 149 * 150 * <jc>// Apply any settings found on the annotations.</jc> 151 * WriterSerializer s = JsonSerializer 152 * .<jsm>create</jsm>() 153 * .applyAnnotations(al, vs) 154 * .build(); 155 * </p> 156 * 157 * @param al The list of all annotations annotated with {@link PropertyStoreApply}. 158 * @param r The string resolver for resolving variables in annotation values. 159 * @return This object (for method chaining). 160 */ 161 @FluentSetter 162 public ContextBuilder applyAnnotations(AnnotationList al, VarResolverSession r) { 163 this.psb.applyAnnotations(al, r); 164 return this; 165 } 166 167 /** 168 * Applies any of the various <ja>@XConfig</ja> annotations on the specified class to this context. 169 * 170 * <p> 171 * Any annotations found that themselves are annotated with {@link PropertyStoreApply} will be resolved and 172 * applied as properties to this builder. These annotations include: 173 * <ul class='javatree'> 174 * <li class ='ja'>{@link BeanConfig} 175 * <li class ='ja'>{@link CsvConfig} 176 * <li class ='ja'>{@link HtmlConfig} 177 * <li class ='ja'>{@link HtmlDocConfig} 178 * <li class ='ja'>{@link JsoConfig} 179 * <li class ='ja'>{@link JsonConfig} 180 * <li class ='ja'>{@link JsonSchemaConfig} 181 * <li class ='ja'>{@link MsgPackConfig} 182 * <li class ='ja'>{@link OpenApiConfig} 183 * <li class ='ja'>{@link ParserConfig} 184 * <li class ='ja'>{@link PlainTextConfig} 185 * <li class ='ja'>{@link SerializerConfig} 186 * <li class ='ja'>{@link SoapXmlConfig} 187 * <li class ='ja'>{@link UonConfig} 188 * <li class ='ja'>{@link UrlEncodingConfig} 189 * <li class ='ja'>{@link XmlConfig} 190 * <li class ='ja'><c>RdfConfig</c> 191 * </ul> 192 * 193 * <p> 194 * Annotations on classes are appended in the following order: 195 * <ol> 196 * <li>On the package of this class. 197 * <li>On interfaces ordered parent-to-child. 198 * <li>On parent classes ordered parent-to-child. 199 * <li>On this class. 200 * </ol> 201 * 202 * <p> 203 * The default var resolver {@link VarResolver#DEFAULT} is used to resolve any variables in annotation field values. 204 * 205 * <h5 class='section'>Example:</h5> 206 * <p class='bcode w800'> 207 * <jc>// A class annotated with a config annotation.</jc> 208 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 209 * <jk>public class</jk> MyClass {...} 210 * 211 * <jc>// Apply any settings found on the annotations.</jc> 212 * WriterSerializer s = JsonSerializer 213 * .<jsm>create</jsm>() 214 * .applyAnnotations(MyClass.<jk>class</jk>) 215 * .build(); 216 * </p> 217 * 218 * @param fromClasses The classes on which the annotations are defined. 219 * @return This object (for method chaining). 220 */ 221 @FluentSetter 222 public ContextBuilder applyAnnotations(Class<?>...fromClasses) { 223 for (Class<?> c : fromClasses) 224 applyAnnotations(ClassInfo.of(c).getAnnotationList(ConfigAnnotationFilter.INSTANCE), VarResolver.DEFAULT.createSession()); 225 return this; 226 } 227 228 /** 229 * Applies any of the various <ja>@XConfig</ja> annotations on the specified method to this context. 230 * 231 * <p> 232 * Any annotations found that themselves are annotated with {@link PropertyStoreApply} will be resolved and 233 * applied as properties to this builder. These annotations include: 234 * <ul class='javatree'> 235 * <li class ='ja'>{@link BeanConfig} 236 * <li class ='ja'>{@link CsvConfig} 237 * <li class ='ja'>{@link HtmlConfig} 238 * <li class ='ja'>{@link HtmlDocConfig} 239 * <li class ='ja'>{@link JsoConfig} 240 * <li class ='ja'>{@link JsonConfig} 241 * <li class ='ja'>{@link JsonSchemaConfig} 242 * <li class ='ja'>{@link MsgPackConfig} 243 * <li class ='ja'>{@link OpenApiConfig} 244 * <li class ='ja'>{@link ParserConfig} 245 * <li class ='ja'>{@link PlainTextConfig} 246 * <li class ='ja'>{@link SerializerConfig} 247 * <li class ='ja'>{@link SoapXmlConfig} 248 * <li class ='ja'>{@link UonConfig} 249 * <li class ='ja'>{@link UrlEncodingConfig} 250 * <li class ='ja'>{@link XmlConfig} 251 * <li class ='ja'><c>RdfConfig</c> 252 * </ul> 253 * 254 * <p> 255 * Annotations on methods are appended in the following order: 256 * <ol> 257 * <li>On the package of the method class. 258 * <li>On interfaces ordered parent-to-child. 259 * <li>On parent classes ordered parent-to-child. 260 * <li>On the method class. 261 * <li>On this method and matching methods ordered parent-to-child. 262 * </ol> 263 * 264 * <p> 265 * The default var resolver {@link VarResolver#DEFAULT} is used to resolve any variables in annotation field values. 266 * 267 * <h5 class='section'>Example:</h5> 268 * <p class='bcode w800'> 269 * <jc>// A method annotated with a config annotation.</jc> 270 * <jk>public class</jk> MyClass { 271 * <ja>@BeanConfig</ja>(sortProperties=<js>"$S{sortProperties,false}"</js>) 272 * <jk>public void</jk> myMethod() {...} 273 * } 274 * 275 * <jc>// Apply any settings found on the annotations.</jc> 276 * WriterSerializer s = JsonSerializer 277 * .<jsm>create</jsm>() 278 * .applyAnnotations(MyClass.<jk>class</jk>.getMethod(<js>"myMethod"</js>)) 279 * .build(); 280 * </p> 281 * 282 * @param fromMethods The methods on which the annotations are defined. 283 * @return This object (for method chaining). 284 */ 285 @FluentSetter 286 public ContextBuilder applyAnnotations(Method...fromMethods) { 287 for (Method m : fromMethods) 288 applyAnnotations(MethodInfo.of(m).getAnnotationList(ConfigAnnotationFilter.INSTANCE), VarResolver.DEFAULT.createSession()); 289 return this; 290 } 291 292 /** 293 * Build a new instance of the specified object. 294 * 295 * <p> 296 * Creates a new instance of the specified context-based class, or an existing instance if one with the equivalent 297 * property store was already created. 298 * 299 * @param c The subclass of {@link Context} to instantiate. 300 * @return A new object using the settings defined in this builder. 301 */ 302 303 public <T extends Context> T build(Class<T> c) { 304 return ContextCache.INSTANCE.create(c, getPropertyStore()); 305 } 306 307 /** 308 * Returns a read-only snapshot of the current property store on this builder. 309 * 310 * @return A property store object. 311 */ 312 public PropertyStore getPropertyStore() { 313 return psb.build(); 314 } 315 316 //----------------------------------------------------------------------------------------------------------------- 317 // Properties 318 //----------------------------------------------------------------------------------------------------------------- 319 320 /** 321 * <i><l>Context</l> configuration property: </i> Debug mode. 322 * 323 * <p> 324 * Enables the following additional information during serialization: 325 * <ul class='spaced-list'> 326 * <li> 327 * When bean getters throws exceptions, the exception includes the object stack information 328 * in order to determine how that method was invoked. 329 * <li> 330 * Enables {@link Serializer#BEANTRAVERSE_detectRecursions}. 331 * </ul> 332 * 333 * <p> 334 * Enables the following additional information during parsing: 335 * <ul class='spaced-list'> 336 * <li> 337 * When bean setters throws exceptions, the exception includes the object stack information 338 * in order to determine how that method was invoked. 339 * </ul> 340 * 341 * <h5 class='section'>Example:</h5> 342 * <p class='bcode w800'> 343 * <jc>// Create a serializer with debug enabled.</jc> 344 * WriterSerializer s = JsonSerializer 345 * .<jsm>create</jsm>() 346 * .debug() 347 * .build(); 348 * 349 * <jc>// Same, but use property.</jc> 350 * WriterSerializer s = JsonSerializer 351 * .<jsm>create</jsm>() 352 * .set(<jsf>BEAN_debug</jsf>, <jk>true</jk>) 353 * .build(); 354 * 355 * <jc>// Create a POJO model with a recursive loop.</jc> 356 * <jk>public class</jk> A { 357 * <jk>public</jk> Object <jf>f</jf>; 358 * } 359 * A a = <jk>new</jk> A(); 360 * a.<jf>f</jf> = a; 361 * 362 * <jc>// Throws a SerializeException and not a StackOverflowError</jc> 363 * String json = s.serialize(a); 364 * </p> 365 * 366 * <ul class='seealso'> 367 * <li class='jf'>{@link Context#CONTEXT_debug} 368 * </ul> 369 * 370 * @return This object (for method chaining). 371 */ 372 @FluentSetter 373 public ContextBuilder debug() { 374 return set(CONTEXT_debug, true); 375 } 376 377 /** 378 * <i><l>Context</l> configuration property: </i> Locale. 379 * 380 * <p> 381 * Specifies the default locale for serializer and parser sessions when not specified via {@link SessionArgs#locale(Locale)}. 382 * Typically used for POJO swaps that need to deal with locales such as swaps that convert <l>Date</l> and <l>Calendar</l> 383 * objects to strings by accessing it via the session passed into the {@link PojoSwap#swap(BeanSession, Object)} and 384 * {@link PojoSwap#unswap(BeanSession, Object, ClassMeta, String)} methods. 385 * 386 * <h5 class='section'>Example:</h5> 387 * <p class='bcode w800'> 388 * <jc>// Define a POJO swap that skips serializing beans if we're in the UK.</jc> 389 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 390 * <ja>@Override</ja> 391 * public String swap(BeanSession session, MyBean o) throws Exception { 392 * <jk>if</jk> (session.getLocale().equals(Locale.<jsf>UK</jsf>)) 393 * <jk>return null</jk>; 394 * <jk>return</jk> o.toString(); 395 * } 396 * } 397 * 398 * <jc>// Create a serializer that uses the specified locale if it's not passed in through session args.</jc> 399 * WriterSerializer s = JsonSerializer 400 * .<jsm>create</jsm>() 401 * .locale(Locale.<jsf>UK</jsf>) 402 * .pojoSwaps(MyBeanSwap.<jk>class</jk>) 403 * .build(); 404 * 405 * <jc>// Same, but use property.</jc> 406 * WriterSerializer s = JsonSerializer 407 * .<jsm>create</jsm>() 408 * .set(<jsf>CONTEXT_locale</jsf>, Locale.<jsf>UK</jsf>) 409 * .addTo(<jsf>BEAN_pojoSwaps</jsf>, MyBeanSwap.<jk>class</jk>) 410 * .build(); 411 * 412 * <jc>// Define on session-args instead.</jc> 413 * SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().locale(Locale.<jsf>UK</jsf>); 414 * <jk>try</jk> (WriterSerializerSession session = s.createSession(sessionArgs)) { 415 * 416 * <jc>// Produces "null" if in the UK.</jc> 417 * String json = s.serialize(<jk>new</jk> MyBean()); 418 * } 419 * </p> 420 * 421 * <ul class='seealso'> 422 * <li class='jf'>{@link Context#CONTEXT_locale} 423 * </ul> 424 * 425 * @param value The new value for this property. 426 * @return This object (for method chaining). 427 */ 428 @FluentSetter 429 public ContextBuilder locale(Locale value) { 430 return set(CONTEXT_locale, value); 431 } 432 433 /** 434 * <i><l>Context</l> configuration property: </i> Media type. 435 * 436 * <p> 437 * Specifies the default media type for serializer and parser sessions when not specified via {@link SessionArgs#mediaType(MediaType)}. 438 * Typically used for POJO swaps that need to serialize the same POJO classes differently depending on 439 * the specific requested media type. For example, a swap could handle a request for media types <js>"application/json"</js> 440 * and <js>"application/json+foo"</js> slightly differently even though they're both being handled by the same JSON 441 * serializer or parser. 442 * 443 * <h5 class='section'>Example:</h5> 444 * <p class='bcode w800'> 445 * <jc>// Define a POJO swap that skips serializing beans if the media type is application/json.</jc> 446 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 447 * <ja>@Override</ja> 448 * public String swap(BeanSession session, MyBean o) throws Exception { 449 * <jk>if</jk> (session.getMediaType().equals(<js>"application/json"</js>)) 450 * <jk>return null</jk>; 451 * <jk>return</jk> o.toString(); 452 * } 453 * } 454 * 455 * <jc>// Create a serializer that uses the specified media type if it's not passed in through session args.</jc> 456 * WriterSerializer s = JsonSerializer 457 * .<jsm>create</jsm>() 458 * .mediaType(MediaType.<jsf>JSON</jsf>) 459 * .build(); 460 * 461 * <jc>// Same, but use property.</jc> 462 * WriterSerializer s = JsonSerializer 463 * .<jsm>create</jsm>() 464 * .set(<jsf>CONTEXT_mediaType</jsf>, MediaType.<jsf>JSON</jsf>) 465 * .build(); 466 * 467 * <jc>// Define on session-args instead.</jc> 468 * SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().mediaType(MediaType.<jsf>JSON</jsf>); 469 * <jk>try</jk> (WriterSerializerSession session = s.createSession(sessionArgs)) { 470 * 471 * <jc>// Produces "null" since it's JSON.</jc> 472 * String json = s.serialize(<jk>new</jk> MyBean()); 473 * } 474 * </p> 475 * 476 * <ul class='seealso'> 477 * <li class='jf'>{@link Context#CONTEXT_mediaType} 478 * </ul> 479 * 480 * @param value The new value for this property. 481 * @return This object (for method chaining). 482 */ 483 @FluentSetter 484 public ContextBuilder mediaType(MediaType value) { 485 return set(CONTEXT_mediaType, value); 486 } 487 488 /** 489 * <i><l>Context</l> configuration property: </i> TimeZone. 490 * 491 * <p> 492 * Specifies the default time zone for serializer and parser sessions when not specified via {@link SessionArgs#timeZone(TimeZone)}. 493 * Typically used for POJO swaps that need to deal with timezones such as swaps that convert <l>Date</l> and <l>Calendar</l> 494 * objects to strings by accessing it via the session passed into the {@link PojoSwap#swap(BeanSession, Object)} and 495 * {@link PojoSwap#unswap(BeanSession, Object, ClassMeta, String)} methods. 496 * 497 * <h5 class='section'>Example:</h5> 498 * <p class='bcode w800'> 499 * <jc>// Define a POJO swap that skips serializing beans if the time zone is GMT.</jc> 500 * <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap<MyBean> { 501 * <ja>@Override</ja> 502 * public String swap(BeanSession session, MyBean o) throws Exception { 503 * <jk>if</jk> (session.getTimeZone().equals(TimeZone.<jsf>GMT</jsf>)) 504 * <jk>return null</jk>; 505 * <jk>return</jk> o.toString(); 506 * } 507 * } 508 * 509 * <jc>// Create a serializer that uses GMT if the timezone is not specified in the session args.</jc> 510 * WriterSerializer s = JsonSerializer 511 * .<jsm>create</jsm>() 512 * .timeZone(TimeZone.<jsf>GMT</jsf>) 513 * .build(); 514 * 515 * <jc>// Same, but use property.</jc> 516 * WriterSerializer s = JsonSerializer 517 * .<jsm>create</jsm>() 518 * .set(<jsf>CONTEXT_timeZone</jsf>, TimeZone.<jsf>GMT</jsf>) 519 * .build(); 520 * 521 * <jc>// Define on session-args instead.</jc> 522 * SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().timeZone(TimeZone.<jsf>GMT</jsf>); 523 * <jk>try</jk> (WriterSerializerSession ss = JsonSerializer.<jsf>DEFAULT</jsf>.createSession(sessionArgs)) { 524 * 525 * <jc>// Produces "null" since the time zone is GMT.</jc> 526 * String json = s.serialize(<jk>new</jk> MyBean()); 527 * } 528 * </p> 529 * 530 * <ul class='seealso'> 531 * <li class='jf'>{@link Context#CONTEXT_timeZone} 532 * </ul> 533 * 534 * @param value The new value for this property. 535 * @return This object (for method chaining). 536 */ 537 @FluentSetter 538 public ContextBuilder timeZone(TimeZone value) { 539 return set(CONTEXT_timeZone, value); 540 } 541 542 /** 543 * Sets a free-form configuration property on this object. 544 * 545 * <p> 546 * Provides the ability to specify configuration property values in a generic fashion. 547 * 548 * <p> 549 * Property names must have the following format that identify their datatype... 550 * <p class='bcode w800'> 551 * <js>"{class}.{name}.{type}"</js> 552 * </p> 553 * <p> 554 * ...where the parts consist of the following... 555 * <ul> 556 * <li><js>"{class}"</js> - The group name of the property (e.g. <js>"JsonSerializer"</js>). 557 * <br>It's always going to be the simple class name of the class it's associated with. 558 * <li><js>"{name}"</js> - The property name (e.g. <js>"useWhitespace"</js>). 559 * <li><js>"{type}"</js> - The property data type. 560 * <br>A 1 or 2 character string that identifies the data type of the property. 561 * <br>Valid values are: 562 * <ul> 563 * <li><js>"s"</js> - <c>String</c> 564 * <li><js>"b"</js> - <c>Boolean</c> 565 * <li><js>"i"</js> - <c>Integer</c> 566 * <li><js>"c"</js> - <c>Class</c> 567 * <li><js>"o"</js> - <c>Object</c> 568 * <li><js>"ss"</js> - <c>TreeSet<String></c> 569 * <li><js>"si"</js> - <c>TreeSet<Integer></c> 570 * <li><js>"sc"</js> - <c>TreeSet<Class></c> 571 * <li><js>"ls"</js> - <c>Linkedlist<String></c> 572 * <li><js>"li"</js> - <c>Linkedlist<Integer></c> 573 * <li><js>"lc"</js> - <c>Linkedlist<Class></c> 574 * <li><js>"lo"</js> - <c>Linkedlist<Object></c> 575 * <li><js>"sms"</js> - <c>TreeMap<String,String></c> 576 * <li><js>"smi"</js> - <c>TreeMap<String,Integer></c> 577 * <li><js>"smc"</js> - <c>TreeMap<String,Class></c> 578 * <li><js>"smo"</js> - <c>TreeMap<String,Object></c> 579 * <li><js>"oms"</js> - <c>LinkedHashMap<String,String></c> 580 * <li><js>"omi"</js> - <c>LinkedHashMap<String,Integer></c> 581 * <li><js>"omc"</js> - <c>LinkedHashMap<String,Class></c> 582 * <li><js>"omo"</js> - <c>LinkedHashMap<String,Object></c> 583 * </ul> 584 * </ul> 585 * 586 * <p> 587 * For example, <js>"BeanContext.swaps.lc"</js> refers to a property on the <l>BeanContext</l> class 588 * called <l>swaps</l> that has a data type of <l>List<Class></l>. 589 * 590 * <p> 591 * Property values get 'normalized' when they get set. 592 * For example, calling <c>set(<js>"BeanContext.debug.b"</js>, <js>"true"</js>)</c> will cause the property 593 * value to be converted to a boolean. This allows the underlying {@link PropertyStore} class to be comparable 594 * and useful in determining whether a cached instance of a context object can be returned. 595 * 596 * <h5 class='section'>Example:</h5> 597 * <p class='bcode w800'> 598 * <jc>// Create a serializer that sorts maps and bean properties.</jc> 599 * WriterSerializer s = JsonSerializer 600 * .<jsm>create</jsm>() 601 * .sortMaps() 602 * .sortProperties() 603 * .build(); 604 * 605 * <jc>// Same, but use generic set() method.</jc> 606 * WriterSerializer s = JsonSerializer 607 * .<jsm>create</jsm>() 608 * .set(<jsf>BEAN_sortMaps</jsf>, <jk>true</jk>) 609 * .set(<jsf>BEAN_sortProperties</jsf>, <jk>true</jk>) 610 * .build(); 611 * </p> 612 * 613 * <p> 614 * As a general rule, builders don't typically have "unsetter" methods. For example, once you've set strict 615 * mode on the <l>ParserBuilder</l> class, a method does not exist for unsetting it. 616 * This method can be used in these rare cases where you need to unset a value by setting it to <jk>null</jk>. 617 * 618 * <h5 class='section'>Example:</h5> 619 * <p class='bcode w800'> 620 * <jc>// Clone an existing serializer and unset the sort settings.</jc> 621 * s = s.<jsm>builder</jsm>() 622 * .set(<jsf>BEAN_sortMaps</jsf>, <jk>null</jk>) 623 * .set(<jsf>BEAN_sortProperties</jsf>, <jk>null</jk>) 624 * .build(); 625 * </p> 626 * 627 * <ul class='seealso'> 628 * <li class='jc'>{@link PropertyStore} 629 * </ul> 630 * 631 * @param name The property name. 632 * @param value 633 * The property value. 634 * <br>The valid value types depend on the property type: 635 * <ul> 636 * <li><js>"s"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 637 * <li><js>"b"</js> - Any <l>Object</l> converted to a <l>Boolean</l> using <c>Boolean.<jsm>parseBoolean</jsm>(value.toString())</c>. 638 * <li><js>"i"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 639 * <li><js>"c"</js> - Only <l>Class</l> objects are allowed. 640 * <li><js>"o"</js> - Left as-is. 641 * <li><js>"ss"</js>,<js>"si"</js>,<js>"sc"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 642 * <li><js>"ls"</js>,<js>"li"</js>,<js>"lc"</js>,<js>"lo"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 643 * <li><js>"sms"</js>,<js>"smi"</js>,<js>"smc"</js>,<js>"smo"</js> - Any sorted map of any convertible <l>Objects</l> or a JSON Object string. 644 * <li><js>"oms"</js>,<js>"omi"</js>,<js>"omc"</js>,<js>"omo"</js> - Any ordered map of any convertible <l>Objects</l> or a JSON Object string. 645 * </ul> 646 * 647 * @return This object (for method chaining). 648 */ 649 @FluentSetter 650 public ContextBuilder set(String name, Object value) { 651 psb.set(name, value); 652 return this; 653 } 654 655 /** 656 * Peeks at a free-form configuration property on this object. 657 * 658 * <p> 659 * Allows you to look at the raw value of a configuration property while it's still in the builder. 660 * 661 * @param key The property name. 662 * @return 663 * The raw value of the configuration property as it was passed in through this API. 664 * <br><jk>null</jk> if not set. 665 */ 666 public Object peek(String key) { 667 return psb.peek(key); 668 } 669 670 /** 671 * Peeks at a configuration property on this object. 672 * 673 * <p> 674 * Allows you to look at the raw value of a configuration property while it's still in the builder. 675 * 676 * <p> 677 * Converts the value from the current raw form into the specified data type. 678 * 679 * @param c The type to convert to. 680 * @param key The property name. 681 * @return 682 * The converted value of the configuration property after conversion from the raw value. 683 * <br><jk>null</jk> if not set. 684 * @param <T> The type to convert to. 685 */ 686 public <T> T peek(Class<T> c, String key) { 687 return psb.peek(c, key); 688 } 689 690 /** 691 * Sets multiple free-form configuration properties on this object replacing all previous values. 692 * 693 * <p> 694 * Identical in function to {@link #set(String, Object)} but allows you to specify multiple values at once. 695 * 696 * <h5 class='section'>Example:</h5> 697 * <p class='bcode w800'> 698 * <jc>// Create a serializer that sorts maps and bean properties.</jc> 699 * WriterSerializer s = JsonSerializer 700 * .<jsm>create</jsm>() 701 * .sortMaps() 702 * .sortProperties() 703 * .build(); 704 * 705 * <jc>// Same, but use generic set(Map) method.</jc> 706 * WriterSerializer s = JsonSerializer 707 * .<jsm>create</jsm>() 708 * .set( 709 * AMap.<jsm>of</jsm>( 710 * <jsf>BEAN_sortMaps</jsf>, <jk>true</jk>, 711 * <jsf>BEAN_sortProperties</jsf>, <jk>true</jk> 712 * ) 713 * ) 714 * .build(); 715 * </p> 716 * 717 * <ul class='seealso'> 718 * <li class='jc'>{@link PropertyStore} 719 * <li class='jm'>{@link #set(String, Object)} 720 * </ul> 721 * 722 * @param properties 723 * The properties to set on this class. 724 * <br>The keys must be strings. 725 * <br>The valid value types depend on the property type: 726 * <ul> 727 * <li><js>"s"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 728 * <li><js>"b"</js> - Any <l>Object</l> converted to a <l>Boolean</l> using <c>Boolean.<jsm>parseBoolean</jsm>(value.toString())</c>. 729 * <li><js>"i"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 730 * <li><js>"c"</js> - Only <l>Class</l> objects are allowed. 731 * <li><js>"o"</js> - Left as-is. 732 * <li><js>"ss"</js>,<js>"si"</js>,<js>"sc"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 733 * <li><js>"ls"</js>,<js>"li"</js>,<js>"lc"</js>,<js>"lo"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 734 * <li><js>"sms"</js>,<js>"smi"</js>,<js>"smc"</js>,<js>"smo"</js> - Any sorted map of any convertible <l>Objects</l> or a JSON Object string. 735 * <li><js>"oms"</js>,<js>"omi"</js>,<js>"omc"</js>,<js>"omo"</js> - Any ordered map of any convertible <l>Objects</l> or a JSON Object string. 736 * </ul> 737 * @return This object (for method chaining). 738 */ 739 @FluentSetter 740 public ContextBuilder set(Map<String,Object> properties) { 741 psb.set(properties); 742 return this; 743 } 744 745 /** 746 * Adds multiple free-form configuration properties on this object without first clearing out any previous values. 747 * 748 * <p> 749 * Identical in function to {@link #set(String, Object)} but allows you to specify multiple values at once. 750 * 751 * <h5 class='section'>Example:</h5> 752 * <p class='bcode w800'> 753 * <jc>// Create a serializer that sorts bean properties.</jc> 754 * WriterSerializer s = JsonSerializer 755 * .<jsm>create</jsm>() 756 * .sortMaps() 757 * .sortProperties() 758 * .build(); 759 * 760 * <jc>// Same, but use generic add() method.</jc> 761 * WriterSerializer s = JsonSerializer 762 * .<jsm>create</jsm>() 763 * .add( 764 * AMap.<jsm>of</jsm>( 765 * <jsf>BEAN_sortMaps</jsf>, <jk>true</jk>, 766 * <jsf>BEAN_sortProperties</jsf>, <jk>true</jk> 767 * ) 768 * ) 769 * .build(); 770 * </p> 771 * 772 * <ul class='seealso'> 773 * <li class='jc'>{@link PropertyStore} 774 * <li class='jm'>{@link #set(String, Object)} 775 * </ul> 776 * 777 * @param properties 778 * The properties to set on this class. 779 * <br>The keys must be strings. 780 * <br>The valid value types depend on the property type: 781 * <ul> 782 * <li><js>"s"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 783 * <li><js>"b"</js> - Any <l>Object</l> converted to a <l>Boolean</l> using <c>Boolean.<jsm>parseBoolean</jsm>(value.toString())</c>. 784 * <li><js>"i"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 785 * <li><js>"c"</js> - Only <l>Class</l> objects are allowed. 786 * <li><js>"o"</js> - Left as-is. 787 * <li><js>"ss"</js>,<js>"si"</js>,<js>"sc"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 788 * <li><js>"ls"</js>,<js>"li"</js>,<js>"lc"</js>,<js>"lo"</js> - Any collection or array of any convertible <l>Objects</l> or a JSON Array string. 789 * <li><js>"sms"</js>,<js>"smi"</js>,<js>"smc"</js>,<js>"smo"</js> - Any sorted map of any convertible <l>Objects</l> or a JSON Object string. 790 * <li><js>"oms"</js>,<js>"omi"</js>,<js>"omc"</js>,<js>"omo"</js> - Any ordered map of any convertible <l>Objects</l> or a JSON Object string. 791 * </ul> 792 * @return This object (for method chaining). 793 */ 794 @FluentSetter 795 public ContextBuilder add(Map<String,Object> properties) { 796 psb.add(properties); 797 return this; 798 } 799 800 /** 801 * Adds a free-form value to a SET property. 802 * 803 * <p> 804 * SET properties are those properties with one of the following type parts: 805 * <ul> 806 * <li><js>"ss"</js> - <c>TreeSet<String></c> 807 * <li><js>"si"</js> - <c>TreeSet<Integer></c> 808 * <li><js>"sc"</js> - <c>TreeSet<Class></c> 809 * </ul> 810 * 811 * <p> 812 * For example, the {@link BeanContext#BEAN_notBeanClasses} property which has the value <js>"BeanContext.notBeanClasses.sc"</js>. 813 * 814 * <h5 class='section'>Example:</h5> 815 * <p class='bcode w800'> 816 * <jc>// Create a serializer that forces MyNotBean classes to be converted to strings.</jc> 817 * WriterSerializer s = JsonSerializer 818 * .<jsm>create</jsm>() 819 * .notBeanClasses(MyNotBean.<jk>class</jk>) 820 * .build(); 821 * 822 * <jc>// Same, but use generic addTo() method.</jc> 823 * WriterSerializer s = JsonSerializer 824 * .<jsm>create</jsm>() 825 * .addTo(<jsf>BEAN_notBeanClasses</jsf>, MyNotBean.<jk>class</jk>) 826 * .build(); 827 * </p> 828 * 829 * <ul class='seealso'> 830 * <li class='jc'>{@link PropertyStore} 831 * <li class='jm'>{@link #set(String, Object)} 832 * </ul> 833 * 834 * @param name The property name. 835 * @param value 836 * The new value to add to the SET property. 837 * <br>The valid value types depend on the property type: 838 * <ul> 839 * <li><js>"ss"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 840 * <li><js>"si"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 841 * <li><js>"sc"</js> - Only <l>Class</l> objects are allowed. 842 * </ul> 843 * @return This object (for method chaining). 844 * @throws ConfigException If property is not a SET property. 845 */ 846 @FluentSetter 847 public ContextBuilder addTo(String name, Object value) { 848 psb.addTo(name, value); 849 return this; 850 } 851 852 /** 853 * Adds a free-form value to the end of a LIST property. 854 * 855 * <p> 856 * LIST properties are those properties with one of the following type parts: 857 * <ul> 858 * <li><js>"ls"</js> - <c>Linkedlist<String></c> 859 * <li><js>"li"</js> - <c>Linkedlist<Integer></c> 860 * <li><js>"lc"</js> - <c>Linkedlist<Class></c> 861 * <li><js>"lo"</js> - <c>Linkedlist<Object></c> 862 * </ul> 863 * 864 * <p> 865 * For example, the {@link BeanContext#BEAN_swaps} property which has the value <js>"BeanContext.swaps.lo"</js>. 866 * 867 * <h5 class='section'>Example:</h5> 868 * <p class='bcode w800'> 869 * <jc>// Create a serializer that converts Temporal objects to Basic ISO date strings.</jc> 870 * WriterSerializer s = JsonSerializer 871 * .<jsm>create</jsm>() 872 * .swaps(TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 873 * .build(); 874 * 875 * <jc>// Same, but use generic appendTo() method.</jc> 876 * WriterSerializer s = JsonSerializer 877 * .<jsm>create</jsm>() 878 * .appendTo(<jsf>BEAN_swaps</jsf>, TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 879 * .build(); 880 * </p> 881 * 882 * <ul class='seealso'> 883 * <li class='jc'>{@link PropertyStore} 884 * <li class='jm'>{@link #set(String, Object)} 885 * </ul> 886 * 887 * @param name The property name. 888 * @param value 889 * The new value to add to the LIST property. 890 * <br>The valid value types depend on the property type: 891 * <ul> 892 * <li><js>"ls"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 893 * <li><js>"li"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 894 * <li><js>"lc"</js> - Only <l>Class</l> objects are allowed. 895 * <li><js>"lo"</js> - Left as-is. 896 * </ul> 897 * @return This object (for method chaining). 898 * @throws ConfigException If property is not a LIST property. 899 */ 900 @FluentSetter 901 public ContextBuilder appendTo(String name, Object value) { 902 psb.appendTo(name, value); 903 return this; 904 } 905 906 /** 907 * Adds a free-form value to the beginning of a LIST property. 908 * 909 * <p> 910 * LIST properties are those properties with one of the following type parts: 911 * <ul> 912 * <li><js>"ls"</js> - <c>Linkedlist<String></c> 913 * <li><js>"li"</js> - <c>Linkedlist<Integer></c> 914 * <li><js>"lc"</js> - <c>Linkedlist<Class></c> 915 * <li><js>"lo"</js> - <c>Linkedlist<Object></c> 916 * </ul> 917 * 918 * <p> 919 * For example, the {@link BeanContext#BEAN_swaps} property which has the value <js>"BeanContext.swaps.lo"</js>. 920 * 921 * <h5 class='section'>Example:</h5> 922 * <p class='bcode w800'> 923 * <jc>// Create a serializer that converts Temporal objects to Basic ISO date strings.</jc> 924 * WriterSerializer s = JsonSerializer 925 * .<jsm>create</jsm>() 926 * .swaps(TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 927 * .build(); 928 * 929 * <jc>// Same, but use generic prependTo() method.</jc> 930 * WriterSerializer s = JsonSerializer 931 * .<jsm>create</jsm>() 932 * .prependTo(<jsf>BEAN_swaps</jsf>, TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 933 * .build(); 934 * </p> 935 * 936 * <ul class='seealso'> 937 * <li class='jc'>{@link PropertyStore} 938 * <li class='jm'>{@link #set(String, Object)} 939 * </ul> 940 * 941 * @param name The property name. 942 * @param value 943 * The new value to add to the LIST property. 944 * <br>The valid value types depend on the property type: 945 * <ul> 946 * <li><js>"ls"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 947 * <li><js>"li"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 948 * <li><js>"lc"</js> - Only <l>Class</l> objects are allowed. 949 * <li><js>"lo"</js> - Left as-is. 950 * </ul> 951 * @return This object (for method chaining). 952 * @throws ConfigException If property is not a LIST property. 953 */ 954 @FluentSetter 955 public ContextBuilder prependTo(String name, Object value) { 956 psb.prependTo(name, value); 957 return this; 958 } 959 960 /** 961 * Adds or overwrites a free-form entry in a MAP property. 962 * 963 * <p> 964 * MAP properties are those properties with one of the following type parts: 965 * <ul> 966 * <li><js>"sms"</js> - <c>TreeMap<String,String></c> 967 * <li><js>"smi"</js> - <c>TreeMap<String,Integer></c> 968 * <li><js>"smc"</js> - <c>TreeMap<String,Class></c> 969 * <li><js>"smo"</js> - <c>TreeMap<String,Object></c> 970 * <li><js>"oms"</js> - <c>LinkedHashMap<String,String></c> 971 * <li><js>"omi"</js> - <c>LinkedHashMap<String,Integer></c> 972 * <li><js>"omc"</js> - <c>LinkedHashMap<String,Class></c> 973 * <li><js>"omo"</js> - <c>LinkedHashMap<String,Object></c> 974 * </ul> 975 * 976 * <p> 977 * For example, the {@link BeanContext#BEAN_implClasses} property which has the value <js>"BeanContext.implClasses.smc"</js>. 978 * 979 * <h5 class='section'>Example:</h5> 980 * <p class='bcode w800'> 981 * <jc>// Create a serializer that specifies the concrete implementation class for an interface.</jc> 982 * WriterSerializer s = JsonSerializer 983 * .<jsm>create</jsm>() 984 * .implClass(MyInterface.<jk>class</jk>, MyImplementation.<jk>class</jk>) 985 * .build(); 986 * 987 * <jc>// Same, but use generic putTo() method.</jc> 988 * WriterSerializer s = JsonSerializer 989 * .<jsm>create</jsm>() 990 * .putTo(<jsf>BEAN_implClasses</jsf>, MyInterface.<jk>class</jk>.getName(), MyImplementation.<jk>class</jk>) 991 * .build(); 992 * </p> 993 * 994 * <ul class='seealso'> 995 * <li class='jc'>{@link PropertyStore} 996 * <li class='jm'>{@link #set(String, Object)} 997 * </ul> 998 * 999 * @param name The property name. 1000 * @param key The property value map key. 1001 * @param value 1002 * The property value map value. 1003 * <br>The valid value types depend on the property type: 1004 * <ul> 1005 * <li><js>"sms"</js>,<js>"oms"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 1006 * <li><js>"smi"</js>,<js>"omi"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 1007 * <li><js>"smc"</js>,<js>"omc"</js> - Only <l>Class</l> objects are allowed. 1008 * <li><js>"smo"</js>,<js>"omo"</js> - Left as-is. 1009 * </ul> 1010 * @return This object (for method chaining). 1011 * @throws ConfigException If property is not a MAP property. 1012 */ 1013 @FluentSetter 1014 public ContextBuilder putTo(String name, String key, Object value) { 1015 psb.putTo(name, key, value); 1016 return this; 1017 } 1018 1019 /** 1020 * Adds or overwrites multiple free-form entries in a MAP property. 1021 * 1022 * <p> 1023 * MAP properties are those properties with one of the following type parts: 1024 * <ul> 1025 * <li><js>"sms"</js> - <c>TreeMap<String,String></c> 1026 * <li><js>"smi"</js> - <c>TreeMap<String,Integer></c> 1027 * <li><js>"smc"</js> - <c>TreeMap<String,Class></c> 1028 * <li><js>"smo"</js> - <c>TreeMap<String,Object></c> 1029 * <li><js>"oms"</js> - <c>LinkedHashMap<String,String></c> 1030 * <li><js>"omi"</js> - <c>LinkedHashMap<String,Integer></c> 1031 * <li><js>"omc"</js> - <c>LinkedHashMap<String,Class></c> 1032 * <li><js>"omo"</js> - <c>LinkedHashMap<String,Object></c> 1033 * </ul> 1034 * 1035 * <p> 1036 * For example, the {@link BeanContext#BEAN_implClasses} property which has the value <js>"BeanContext.implClasses.smc"</js>. 1037 * 1038 * <h5 class='section'>Example:</h5> 1039 * <p class='bcode w800'> 1040 * <jc>// Create a serializer that specifies the concrete implementation class for an interface.</jc> 1041 * WriterSerializer s = JsonSerializer 1042 * .<jsm>create</jsm>() 1043 * .implClass(MyInterface.<jk>class</jk>, MyImplementation.<jk>class</jk>) 1044 * .build(); 1045 * 1046 * <jc>// Same, but use generic putAllTo() method.</jc> 1047 * WriterSerializer s = JsonSerializer 1048 * .<jsm>create</jsm>() 1049 * .putAllTo(<jsf>BEAN_implClasses</jsf>, 1050 * AMap.<jsm>of</jsm>(MyInterface.<jk>class</jk>.getName(), MyImplementation.<jk>class</jk>) 1051 * ) 1052 * .build(); 1053 * </p> 1054 * 1055 * <ul class='seealso'> 1056 * <li class='jc'>{@link PropertyStore} 1057 * <li class='jm'>{@link #set(String, Object)} 1058 * </ul> 1059 * 1060 * @param name The property name. 1061 * @param value 1062 * Either a JSON Object string or a {@link Map} whose valid value types depend on the property type: 1063 * <ul> 1064 * <li><js>"sms"</js>,<js>"oms"</js> - Any <l>Object</l> converted to a <l>String</l> using <c>value.toString()</c>. 1065 * <li><js>"smi"</js>,<js>"omi"</js> - Any <l>Object</l> converted to an <l>Integer</l> using <c>Integer.<jsm>valueOf</jsm>(value.toString())</c>. 1066 * <li><js>"smc"</js>,<js>"omc"</js> - Only <l>Class</l> objects are allowed. 1067 * <li><js>"smo"</js>,<js>"omo"</js> - Left as-is. 1068 * </ul> 1069 * @return This object (for method chaining). 1070 * @throws ConfigException If property is not a MAP property. 1071 */ 1072 @FluentSetter 1073 public ContextBuilder putAllTo(String name, Object value) { 1074 psb.putAllTo(name, value); 1075 return this; 1076 } 1077 1078 /** 1079 * Removes a free-form value from a SET, LIST, or MAP property. 1080 * 1081 * <h5 class='section'>Example:</h5> 1082 * <p class='bcode w800'> 1083 * <jc>// Create a serializer that specifies the concrete implementation class for an interface.</jc> 1084 * WriterSerializer s = JsonSerializer 1085 * .<jsm>create</jsm>() 1086 * .swaps(TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 1087 * .build(); 1088 * 1089 * <jc>// Clone the previous serializer but remove the swap.</jc> 1090 * s = s 1091 * .<jsm>builder</jsm>() 1092 * .removeFrom(<jsf>BEAN_swaps</jsf>, TemoralCalendarSwap.BasicIsoDate.<jk>class</jk>) 1093 * .build(); 1094 * </p> 1095 * 1096 * <ul class='seealso'> 1097 * <li class='jc'>{@link PropertyStore} 1098 * <li class='jm'>{@link #set(String, Object)} 1099 * </ul> 1100 * 1101 * @param name The property name. 1102 * @param value The property value in the SET/LIST/MAP property. 1103 * @return This object (for method chaining). 1104 * @throws ConfigException If property is not a SET/LIST/MAP property. 1105 */ 1106 @FluentSetter 1107 public ContextBuilder removeFrom(String name, Object value) { 1108 psb.removeFrom(name, value); 1109 return this; 1110 } 1111 1112 // <FluentSetters> 1113 1114 // </FluentSetters> 1115}