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 java.util.*;
016
017import org.apache.juneau.annotation.*;
018import org.apache.juneau.collections.*;
019import org.apache.juneau.http.*;
020import org.apache.juneau.internal.*;
021import org.apache.juneau.json.*;
022import org.apache.juneau.serializer.*;
023import org.apache.juneau.transform.*;
024
025/**
026 * A reusable stateless thread-safe read-only configuration, typically used for creating one-time use {@link Session}
027 * objects.
028 *
029 * <p>
030 * Contexts are created through the {@link ContextBuilder#build()} method (and subclasses of {@link ContextBuilder}).
031 *
032 * <p>
033 * Subclasses MUST implement the following constructor:
034 *
035 * <p class='bcode w800'>
036 *    <jk>public</jk> T(PropertyStore);
037 * </p>
038 *
039 * <p>
040 * Besides that restriction, a context object can do anything you desire.
041 * <br>However, it MUST be thread-safe and all fields should be declared final to prevent modification.
042 * <br>It should NOT be used for storing temporary or state information.
043 *
044 * @see PropertyStore
045 */
046@ConfigurableContext
047public abstract class Context {
048
049   static final String PREFIX = "Context";
050
051   /**
052    * Configuration property:  Debug mode.
053    *
054    * <h5 class='section'>Property:</h5>
055    * <ul class='spaced-list'>
056    *    <li><b>ID:</b>  {@link org.apache.juneau.Context#CONTEXT_debug CONTEXT_debug}
057    *    <li><b>Name:</b>  <js>"Context.debug.b"</js>
058    *    <li><b>Data type:</b>  <jk>boolean</jk>
059    *    <li><b>System property:</b>  <c>Context.debug</c>
060    *    <li><b>Environment variable:</b>  <c>CONTEXT_DEBUG</c>
061    *    <li><b>Default:</b>  <jk>false</jk>
062    *    <li><b>Session property:</b>  <jk>true</jk>
063    *    <li><b>Annotations:</b>
064    *       <ul>
065    *          <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#debug()}
066    *       </ul>
067    *    <li><b>Methods:</b>
068    *       <ul>
069    *          <li class='jm'>{@link org.apache.juneau.ContextBuilder#debug()}
070    *          <li class='jm'>{@link org.apache.juneau.SessionArgs#debug(Boolean)}
071    *       </ul>
072    * </ul>
073    *
074    * <h5 class='section'>Description:</h5>
075    *
076    * <p>
077    * Enables the following additional information during serialization:
078    * <ul class='spaced-list'>
079    *    <li>
080    *       When bean getters throws exceptions, the exception includes the object stack information
081    *       in order to determine how that method was invoked.
082    *    <li>
083    *       Enables {@link Serializer#BEANTRAVERSE_detectRecursions}.
084    * </ul>
085    *
086    * <p>
087    * Enables the following additional information during parsing:
088    * <ul class='spaced-list'>
089    *    <li>
090    *       When bean setters throws exceptions, the exception includes the object stack information
091    *       in order to determine how that method was invoked.
092    * </ul>
093    *
094    * <h5 class='section'>Example:</h5>
095    * <p class='bcode w800'>
096    *    <jc>// Create a serializer with debug enabled.</jc>
097    *    WriterSerializer s = JsonSerializer
098    *       .<jsm>create</jsm>()
099    *       .debug()
100    *       .build();
101    *
102    *    <jc>// Same, but use property.</jc>
103    *    WriterSerializer s = JsonSerializer
104    *       .<jsm>create</jsm>()
105    *       .set(<jsf>BEAN_debug</jsf>, <jk>true</jk>)
106    *       .build();
107    *
108    *    <jc>// Create a POJO model with a recursive loop.</jc>
109    *    <jk>public class</jk> A {
110    *       <jk>public</jk> Object <jf>f</jf>;
111    *    }
112    *    A a = <jk>new</jk> A();
113    *    a.<jf>f</jf> = a;
114    *
115    *    <jc>// Throws a SerializeException and not a StackOverflowError</jc>
116    *    String json = s.serialize(a);
117    * </p>
118    */
119   public static final String CONTEXT_debug = PREFIX + ".debug.b";
120
121   /**
122    * Configuration property:  Locale.
123    *
124    * <h5 class='section'>Property:</h5>
125    * <ul class='spaced-list'>
126    *    <li><b>ID:</b>  {@link org.apache.juneau.Context#CONTEXT_locale CONTEXT_locale}
127    *    <li><b>Name:</b>  <js>"Context.locale.s"</js>
128    *    <li><b>Data type:</b>  {@link java.util.Locale}
129    *    <li><b>System property:</b>  <c>Context.locale</c>
130    *    <li><b>Environment variable:</b>  <c>CONTEXT_LOCALE</c>
131    *    <li><b>Default:</b>  <jk>null</jk> (defaults to {@link java.util.Locale#getDefault()})
132    *    <li><b>Session property:</b>  <jk>true</jk>
133    *    <li><b>Annotations:</b>
134    *       <ul>
135    *          <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#locale()}
136    *       </ul>
137    *    <li><b>Methods:</b>
138    *       <ul>
139    *          <li class='jm'>{@link org.apache.juneau.ContextBuilder#locale(Locale)}
140    *          <li class='jm'>{@link org.apache.juneau.SessionArgs#locale(Locale)}
141    *       </ul>
142    * </ul>
143    *
144    * <h5 class='section'>Description:</h5>
145    *
146    * <p>
147    * Specifies the default locale for serializer and parser sessions when not specified via {@link SessionArgs#locale(Locale)}.
148    * Typically used for POJO swaps that need to deal with locales such as swaps that convert <l>Date</l> and <l>Calendar</l>
149    * objects to strings by accessing it via the session passed into the {@link PojoSwap#swap(BeanSession, Object)} and
150    * {@link PojoSwap#unswap(BeanSession, Object, ClassMeta, String)} methods.
151    *
152    * <h5 class='section'>Example:</h5>
153    * <p class='bcode w800'>
154    *    <jc>// Define a POJO swap that skips serializing beans if we're in the UK.</jc>
155    *    <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap&lt;MyBean&gt; {
156    *       <ja>@Override</ja>
157    *       public String swap(BeanSession session, MyBean o) throws Exception {
158    *          <jk>if</jk> (session.getLocale().equals(Locale.<jsf>UK</jsf>))
159    *             <jk>return null</jk>;
160    *          <jk>return</jk> o.toString();
161    *       }
162    *    }
163    *
164    *    <jc>// Create a serializer that uses the specified locale if it's not passed in through session args.</jc>
165    *    WriterSerializer s = JsonSerializer
166    *       .<jsm>create</jsm>()
167    *       .locale(Locale.<jsf>UK</jsf>)
168    *       .pojoSwaps(MyBeanSwap.<jk>class</jk>)
169    *       .build();
170    *
171    *    <jc>// Same, but use property.</jc>
172    *    WriterSerializer s = JsonSerializer
173    *       .<jsm>create</jsm>()
174    *       .set(<jsf>CONTEXT_locale</jsf>, Locale.<jsf>UK</jsf>)
175    *       .addTo(<jsf>BEAN_pojoSwaps</jsf>, MyBeanSwap.<jk>class</jk>)
176    *       .build();
177    *
178    *    <jc>// Define on session-args instead.</jc>
179    *    SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().locale(Locale.<jsf>UK</jsf>);
180    *    <jk>try</jk> (WriterSerializerSession session = s.createSession(sessionArgs)) {
181    *
182    *       <jc>// Produces "null" if in the UK.</jc>
183    *       String json = s.serialize(<jk>new</jk> MyBean());
184    *    }
185    * </p>
186    */
187   public static final String CONTEXT_locale = PREFIX + ".locale.s";
188
189   /**
190    * Configuration property:  Media type.
191    *
192    * <h5 class='section'>Property:</h5>
193    * <ul class='spaced-list'>
194    *    <li><b>ID:</b>  {@link org.apache.juneau.Context#CONTEXT_mediaType CONTEXT_mediaType}
195    *    <li><b>Name:</b>  <js>"Context.mediaType.s"</js>
196    *    <li><b>Data type:</b>  {@link org.apache.juneau.http.MediaType}
197    *    <li><b>System property:</b>  <c>Context.mediaType</c>
198    *    <li><b>Environment variable:</b>  <c>CONTEXT_MEDIATYPE</c>
199    *    <li><b>Default:</b>  <jk>null</jk>
200    *    <li><b>Session property:</b>  <jk>true</jk>
201    *    <li><b>Annotations:</b>
202    *       <ul>
203    *          <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#mediaType()}
204    *       </ul>
205    *    <li><b>Methods:</b>
206    *       <ul>
207    *          <li class='jm'>{@link org.apache.juneau.ContextBuilder#mediaType(MediaType)}
208    *          <li class='jm'>{@link org.apache.juneau.SessionArgs#mediaType(MediaType)}
209    *       </ul>
210    * </ul>
211    *
212    * <h5 class='section'>Description:</h5>
213    *
214    * <p>
215    * Specifies the default media type for serializer and parser sessions when not specified via {@link SessionArgs#mediaType(MediaType)}.
216    * Typically used for POJO swaps that need to serialize the same POJO classes differently depending on
217    * the specific requested media type.   For example, a swap could handle a request for media types <js>"application/json"</js>
218    * and <js>"application/json+foo"</js> slightly differently even though they're both being handled by the same JSON
219    * serializer or parser.
220    *
221    * <h5 class='section'>Example:</h5>
222    * <p class='bcode w800'>
223    *    <jc>// Define a POJO swap that skips serializing beans if the media type is application/json.</jc>
224    *    <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap&lt;MyBean&gt; {
225    *       <ja>@Override</ja>
226    *       public String swap(BeanSession session, MyBean o) throws Exception {
227    *          <jk>if</jk> (session.getMediaType().equals(<js>"application/json"</js>))
228    *             <jk>return null</jk>;
229    *          <jk>return</jk> o.toString();
230    *       }
231    *    }
232    *
233    *    <jc>// Create a serializer that uses the specified media type if it's not passed in through session args.</jc>
234    *    WriterSerializer s = JsonSerializer
235    *       .<jsm>create</jsm>()
236    *       .mediaType(MediaType.<jsf>JSON</jsf>)
237    *       .build();
238    *
239    *    <jc>// Same, but use property.</jc>
240    *    WriterSerializer s = JsonSerializer
241    *       .<jsm>create</jsm>()
242    *       .set(<jsf>CONTEXT_mediaType</jsf>, MediaType.<jsf>JSON</jsf>)
243    *       .build();
244    *
245    *    <jc>// Define on session-args instead.</jc>
246    *    SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().mediaType(MediaType.<jsf>JSON</jsf>);
247    *    <jk>try</jk> (WriterSerializerSession session = s.createSession(sessionArgs)) {
248    *
249    *       <jc>// Produces "null" since it's JSON.</jc>
250    *       String json = s.serialize(<jk>new</jk> MyBean());
251    *    }
252    * </p>
253    */
254   public static final String CONTEXT_mediaType = PREFIX + ".mediaType.s";
255
256   /**
257    * Configuration property:  Time zone.
258    *
259    * <h5 class='section'>Property:</h5>
260    * <ul class='spaced-list'>
261    *    <li><b>ID:</b>  {@link org.apache.juneau.Context#CONTEXT_timeZone CONTEXT_timeZone}
262    *    <li><b>Name:</b>  <js>"Context.timeZone.s"</js>
263    *    <li><b>Data type:</b>  {@link java.util.TimeZone}
264    *    <li><b>System property:</b>  <c>Context.timeZone</c>
265    *    <li><b>Environment variable:</b>  <c>CONTEXT_TIMEZONE</c>
266    *    <li><b>Default:</b>  <jk>null</jk>
267    *    <li><b>Session property:</b>  <jk>true</jk>
268    *    <li><b>Annotations:</b>
269    *       <ul>
270    *          <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#timeZone()}
271    *       </ul>
272    *    <li><b>Methods:</b>
273    *       <ul>
274    *          <li class='jm'>{@link org.apache.juneau.ContextBuilder#timeZone(TimeZone)}
275    *          <li class='jm'>{@link org.apache.juneau.SessionArgs#timeZone(TimeZone)}
276    *       </ul>
277    * </ul>
278    *
279    * <h5 class='section'>Description:</h5>
280    *
281    * <p>
282    * Specifies the default time zone for serializer and parser sessions when not specified via {@link SessionArgs#timeZone(TimeZone)}.
283    * Typically used for POJO swaps that need to deal with timezones such as swaps that convert <l>Date</l> and <l>Calendar</l>
284    * objects to strings by accessing it via the session passed into the {@link PojoSwap#swap(BeanSession, Object)} and
285    * {@link PojoSwap#unswap(BeanSession, Object, ClassMeta, String)} methods.
286    *
287    * <h5 class='section'>Example:</h5>
288    * <p class='bcode w800'>
289    *    <jc>// Define a POJO swap that skips serializing beans if the time zone is GMT.</jc>
290    *    <jk>public class</jk> MyBeanSwap <jk>extends</jk> StringSwap&lt;MyBean&gt; {
291    *       <ja>@Override</ja>
292    *       public String swap(BeanSession session, MyBean o) throws Exception {
293    *          <jk>if</jk> (session.getTimeZone().equals(TimeZone.<jsf>GMT</jsf>))
294    *             <jk>return null</jk>;
295    *          <jk>return</jk> o.toString();
296    *       }
297    *    }
298    *
299    *    <jc>// Create a serializer that uses GMT if the timezone is not specified in the session args.</jc>
300    *    WriterSerializer s = JsonSerializer
301    *       .<jsm>create</jsm>()
302    *       .timeZone(TimeZone.<jsf>GMT</jsf>)
303    *       .build();
304    *
305    *    <jc>// Same, but use property.</jc>
306    *    WriterSerializer s = JsonSerializer
307    *       .<jsm>create</jsm>()
308    *       .set(<jsf>CONTEXT_timeZone</jsf>, TimeZone.<jsf>GMT</jsf>)
309    *       .build();
310    *
311    *    <jc>// Define on session-args instead.</jc>
312    *    SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().timeZone(TimeZone.<jsf>GMT</jsf>);
313    *    <jk>try</jk> (WriterSerializerSession ss = JsonSerializer.<jsf>DEFAULT</jsf>.createSession(sessionArgs)) {
314    *
315    *       <jc>// Produces "null" since the time zone is GMT.</jc>
316    *       String json = s.serialize(<jk>new</jk> MyBean());
317    *    }
318    * </p>
319    */
320   public static final String CONTEXT_timeZone = PREFIX + ".timeZone.s";
321
322
323   private final PropertyStore propertyStore;
324   private final int identityCode;
325
326   private final boolean debug;
327   private final Locale locale;
328   private final TimeZone timeZone;
329   private final MediaType mediaType;
330
331   /**
332    * Constructor for this class.
333    *
334    * <p>
335    * Subclasses MUST implement the same public constructor.
336    *
337    * @param ps The read-only configuration for this context object.
338    * @param allowReuse If <jk>true</jk>, subclasses that share the same property store values can be reused.
339    */
340   public Context(PropertyStore ps, boolean allowReuse) {
341      this.propertyStore = ps == null ? PropertyStore.DEFAULT : ps;
342      this.identityCode = allowReuse ? new HashCode().add(getClass().getName()).add(ps).get() : System.identityHashCode(this);
343      debug = getBooleanProperty(CONTEXT_debug, false);
344      locale = getInstanceProperty(CONTEXT_locale, Locale.class, Locale.getDefault());
345      timeZone = getInstanceProperty(CONTEXT_timeZone, TimeZone.class, null);
346      mediaType = getInstanceProperty(CONTEXT_mediaType, MediaType.class, null);
347   }
348
349   /**
350    * Returns the raw property value with the specified name.
351    *
352    * @param key The property name.
353    * @return The property value, or <jk>null</jk> if it doesn't exist.
354    */
355   public Object getProperty(String key) {
356      return propertyStore.getProperty(key);
357   }
358
359   /**
360    * Returns the property value with the specified name.
361    *
362    * @param key The property name.
363    * @param c The class to cast or convert the value to.
364    * @param def The default value.
365    * @return The property value, or the default value if it doesn't exist.
366    */
367   public final <T> T getProperty(String key, Class<T> c, T def) {
368      return propertyStore.getProperty(key, c, def);
369   }
370
371   /**
372    * Shortcut for calling <code>getProperty(key, Boolean.<jk>class</jk>, def)</code>.
373    *
374    * @param key The property name.
375    * @param def The default value.
376    * @return The property value, or the default value if it doesn't exist.
377    */
378   public final Boolean getBooleanProperty(String key, Boolean def) {
379      return getProperty(key, Boolean.class, def);
380   }
381
382   /**
383    * Shortcut for calling <code>getProperty(key, Integer.<jk>class</jk>, def)</code>.
384    *
385    * @param key The property name.
386    * @param def The default value.
387    * @return The property value, or the default value if it doesn't exist.
388    */
389   public final Integer getIntegerProperty(String key, Integer def) {
390      return getProperty(key, Integer.class, def);
391   }
392
393   /**
394    * Shortcut for calling <code>getProperty(key, Long.<jk>class</jk>, def)</code>.
395    *
396    * @param key The property name.
397    * @param def The default value.
398    * @return The property value, or the default value if it doesn't exist.
399    */
400   public final Long getLongProperty(String key, Long def) {
401      return getProperty(key, Long.class, def);
402   }
403
404   /**
405    * Shortcut for calling <code>getProperty(key, String.<jk>class</jk>, def)</code>.
406    *
407    * @param key The property name.
408    * @param def The default value.
409    * @return The property value, or the default value if it doesn't exist.
410    */
411   public final String getStringProperty(String key, String def) {
412      return getProperty(key, String.class, def);
413   }
414
415   /**
416    * Returns a property as a parsed comma-delimited list of strings.
417    *
418    * @param key The property name.
419    * @param def The default value.
420    * @return The property value, or the default value if it doesn't exist.
421    */
422   public final String[] getCdlProperty(String key, String def) {
423      return StringUtils.split(StringUtils.emptyIfNull(getProperty(key, String.class, def)));
424   }
425
426   /**
427    * Same as {@link #getStringProperty(String, String)} but returns a blank instead of the default value if it resolves to <js>"NONE"</js>.
428    *
429    * @param key The property name.
430    * @param def The default value.
431    * @return The property value, or the default value if it doesn't exist.
432    */
433   public final String getStringPropertyWithNone(String key, String def) {
434      String s = getProperty(key, String.class, def);
435      return "NONE".equalsIgnoreCase(s) ? "" : s;
436   }
437
438   /**
439    * Returns the class property with the specified name.
440    *
441    * @param key The property name.
442    * @param type The class type of the property.
443    * @param def The default value.
444    * @return The property value, or the default value if it doesn't exist.
445    */
446   public final <T> Class<? extends T> getClassProperty(String key, Class<T> type, Class<? extends T> def) {
447      return propertyStore.getClassProperty(key, type, def);
448   }
449
450   /**
451    * Returns the array property value with the specified name.
452    *
453    * @param key The property name.
454    * @param eType The class type of the elements in the property.
455    * @return The property value, or the default value if it doesn't exist.
456    */
457   public final <T> T[] getArrayProperty(String key, Class<T> eType) {
458      return propertyStore.getArrayProperty(key, eType);
459   }
460
461   /**
462    * Returns the array property value with the specified name.
463    *
464    * @param key The property name.
465    * @param eType The class type of the elements in the property.
466    * @param def The default value.
467    * @return The property value, or the default value if it doesn't exist.
468    */
469   public final <T> T[] getArrayProperty(String key, Class<T> eType, T[] def) {
470      return propertyStore.getArrayProperty(key, eType, def);
471   }
472
473   /**
474    * Returns the class array property with the specified name.
475    *
476    * @param key The property name.
477    * @return The property value, or an empty array if it doesn't exist.
478    */
479   public final Class<?>[] getClassArrayProperty(String key) {
480      return propertyStore.getClassArrayProperty(key);
481   }
482
483   /**
484    * Returns the class array property with the specified name.
485    *
486    * @param key The property name.
487    * @param def The default value.
488    * @return The property value, or an empty array if it doesn't exist.
489    */
490   public final Class<?>[] getClassArrayProperty(String key, Class<?>[] def) {
491      return propertyStore.getClassArrayProperty(key, def);
492   }
493
494   /**
495    * Returns the class array property with the specified name.
496    *
497    * @param key The property name.
498    * @param eType The class type of the elements in the property.
499    * @return The property value, or an empty array if it doesn't exist.
500    */
501   public final <T> Class<T>[] getClassArrayProperty(String key, Class<T> eType) {
502      return propertyStore.getClassArrayProperty(key, eType);
503   }
504
505   /**
506    * Returns the set property with the specified name.
507    *
508    * @param key The property name.
509    * @param eType The class type of the elements in the property.
510    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
511    */
512   public final <T> Set<T> getSetProperty(String key, Class<T> eType) {
513      return propertyStore.getSetProperty(key, eType);
514   }
515
516   /**
517    * Returns the set property with the specified name.
518    *
519    * @param key The property name.
520    * @param eType The class type of the elements in the property.
521    * @param def The default value if the property doesn't exist or is empty.
522    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or the default value if it doesn't exist or is empty.
523    */
524   public final <T> Set<T> getSetProperty(String key, Class<T> eType, Set<T> def) {
525      return propertyStore.getSetProperty(key, eType, def);
526   }
527
528   /**
529    * Returns the class set property with the specified name.
530    *
531    * @param key The property name.
532    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
533    */
534   public final Set<Class<?>> getClassSetProperty(String key) {
535      return propertyStore.getClassSetProperty(key);
536   }
537
538   /**
539    * Returns the class set property with the specified name.
540    *
541    * @param key The property name.
542    * @param eType The class type of the elements in the property.
543    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
544    */
545   public final <T> Set<Class<T>> getClassSetProperty(String key, Class<T> eType) {
546      return propertyStore.getClassSetProperty(key, eType);
547   }
548
549   /**
550    * Returns the list property with the specified name.
551    *
552    * @param key The property name.
553    * @param eType The class type of the elements in the property.
554    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
555    */
556   public final <T> List<T> getListProperty(String key, Class<T> eType) {
557      return propertyStore.getListProperty(key, eType);
558   }
559
560   /**
561    * Returns the list property with the specified name.
562    *
563    * @param key The property name.
564    * @param eType The class type of the elements in the property.
565    * @param def The default value if the property doesn't exist or is empty.
566    * @return The property value as an unmodifiable <c>ArrayList</c>, or the default value if it doesn't exist or is empty.
567    */
568   public final <T> List<T> getListProperty(String key, Class<T> eType, List<T> def) {
569      return propertyStore.getListProperty(key, eType, def);
570   }
571
572   /**
573    * Returns the class list property with the specified name.
574    *
575    * @param key The property name.
576    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
577    */
578   public final List<Class<?>> getClassListProperty(String key) {
579      return propertyStore.getClassListProperty(key);
580   }
581
582   /**
583    * Returns the class list property with the specified name.
584    *
585    * @param key The property name.
586    * @param eType The class type of the elements in the property.
587    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
588    */
589   public final <T> List<Class<T>> getClassListProperty(String key, Class<T> eType) {
590      return propertyStore.getClassListProperty(key, eType);
591   }
592
593   /**
594    * Returns the map property with the specified name.
595    *
596    * @param key The property name.
597    * @param eType The class type of the elements in the property.
598    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
599    */
600   public final <T> Map<String,T> getMapProperty(String key, Class<T> eType) {
601      return propertyStore.getMapProperty(key, eType);
602   }
603
604   /**
605    * Returns the class map property with the specified name.
606    *
607    * @param key The property name.
608    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
609    */
610   public final Map<String,Class<?>> getClassMapProperty(String key) {
611      return propertyStore.getClassMapProperty(key);
612   }
613
614   /**
615    * Returns the class map property with the specified name.
616    *
617    * @param key The property name.
618    * @param eType The class type of the elements in the property.
619    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
620    */
621   public final <T> Map<String,Class<T>> getClassMapProperty(String key, Class<T> eType) {
622      return propertyStore.getClassMapProperty(key, eType);
623   }
624
625   /**
626    * Returns an instance of the specified class, string, or object property.
627    *
628    * <p>
629    * If instantiating a class, assumes the class has a no-arg constructor.
630    * Otherwise, throws a runtime exception.
631    *
632    * @param key The property name.
633    * @param type The class type of the property.
634    * @param def
635    *    The default value if the property doesn't exist.
636    *    <br>Can either be an instance of <c>T</c>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>, or <jk>null</jk>.
637    * @return A new property instance.
638    */
639   public <T> T getInstanceProperty(String key, Class<T> type, Object def) {
640      return propertyStore.getInstanceProperty(key, type, def);
641   }
642
643   /**
644    * Returns an instance of the specified class, string, or object property.
645    *
646    * @param key The property name.
647    * @param type The class type of the property.
648    * @param def
649    *    The default value if the property doesn't exist.
650    *    <br>Can either be an instance of <c>T</c>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
651    * @param resolver
652    *    The resolver to use for instantiating objects.
653    * @param args
654    *    Arguments to pass to the constructor.
655    *    Constructors matching the arguments are always used before no-arg constructors.
656    * @return A new property instance.
657    */
658   public <T> T getInstanceProperty(String key, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
659      return propertyStore.getInstanceProperty(key, type, def, resolver, args);
660   }
661
662   /**
663    * Returns an instance of the specified class, string, or object property.
664    *
665    * @param key The property name.
666    * @param outer The outer object if the class we're instantiating is an inner class.
667    * @param type The class type of the property.
668    * @param def
669    *    The default value if the property doesn't exist.
670    *    <br>Can either be an instance of <c>T</c>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
671    * @param resolver
672    *    The resolver to use for instantiating objects.
673    * @param args
674    *    Arguments to pass to the constructor.
675    *    Constructors matching the arguments are always used before no-arg constructors.
676    * @return A new property instance.
677    */
678   public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
679      return propertyStore.getInstanceProperty(key, outer, type, def, resolver, args);
680   }
681
682   /**
683    * Returns the specified property as an array of instantiated objects.
684    *
685    * @param key The property name.
686    * @param type The class type of the property.
687    * @param def The default object to return if the property doesn't exist.
688    * @return A new property instance.
689    */
690   public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def) {
691      return propertyStore.getInstanceArrayProperty(key, type, def);
692   }
693
694   /**
695    * Returns the specified property as an array of instantiated objects.
696    *
697    * @param key The property name.
698    * @param type The class type of the property.
699    * @param def The default object to return if the property doesn't exist.
700    * @param resolver
701    *    The resolver to use for instantiating objects.
702    * @param args
703    *    Arguments to pass to the constructor.
704    *    Constructors matching the arguments are always used before no-arg constructors.
705    * @return A new property instance.
706    */
707   public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
708      return propertyStore.getInstanceArrayProperty(key, type, def, resolver, args);
709   }
710
711   /**
712    * Returns the specified property as an array of instantiated objects.
713    *
714    * @param key The property name.
715    * @param outer The outer object if the class we're instantiating is an inner class.
716    * @param type The class type of the property.
717    * @param def The default object to return if the property doesn't exist.
718    * @param resolver
719    *    The resolver to use for instantiating objects.
720    * @param args
721    *    Arguments to pass to the constructor.
722    *    Constructors matching the arguments are always used before no-arg constructors.
723    * @return A new property instance.
724    */
725   public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
726      return propertyStore.getInstanceArrayProperty(key, outer, type, def, resolver, args);
727   }
728
729   /**
730    * Returns the keys found in the specified property group.
731    *
732    * <p>
733    * The keys are NOT prefixed with group names.
734    *
735    * @param group The group name.
736    * @return The set of property keys, or an empty set if the group was not found.
737    */
738   public Set<String> getPropertyKeys(String group) {
739      return propertyStore.getPropertyKeys(group);
740   }
741
742   /**
743    * Returns the property store associated with this context.
744    *
745    * @return The property store associated with this context.
746    */
747   @BeanIgnore
748   public final PropertyStore getPropertyStore() {
749      return propertyStore;
750   }
751
752   /**
753    * Creates a builder from this context object.
754    *
755    * <p>
756    * Builders are used to define new contexts (e.g. serializers, parsers) based on existing configurations.
757    *
758    * @return A new ContextBuilder object.
759    */
760   public ContextBuilder builder() {
761      return null;
762   }
763
764   /**
765    * Create a new bean session based on the properties defined on this context.
766    *
767    * <p>
768    * Use this method for creating sessions if you don't need to override any
769    * properties or locale/timezone currently set on this context.
770    *
771    * @return A new session object.
772    */
773   public Session createSession() {
774      return createSession(createDefaultSessionArgs());
775   }
776
777   /**
778    * Create a new session based on the properties defined on this context combined with the specified
779    * runtime args.
780    *
781    * <p>
782    * Use this method for creating sessions if you don't need to override any
783    * properties or locale/timezone currently set on this context.
784    *
785    * @param args
786    *    The session arguments.
787    * @return A new session object.
788    */
789   public abstract Session createSession(SessionArgs args);
790
791   /**
792    * Defines default session arguments used when calling the {@link #createSession()} method.
793    *
794    * @return A SessionArgs object, possibly a read-only reusable instance.
795    */
796   public abstract SessionArgs createDefaultSessionArgs();
797
798   @Override /* Object */
799   public int hashCode() {
800      return identityCode;
801   }
802
803   /**
804    * Returns a uniqueness identity code for this context.
805    *
806    * @return A uniqueness identity code.
807    */
808   public int identityCode() {
809      return identityCode;
810   }
811
812   @Override /* Object */
813   public boolean equals(Object o) {
814      // Context objects are considered equal if they're the same class and have the same set of properties.
815      if (o == null)
816         return false;
817      if (o.getClass() != this.getClass())
818         return false;
819      Context c = (Context)o;
820      return (c.propertyStore.equals(propertyStore));
821   }
822
823   //-----------------------------------------------------------------------------------------------------------------
824   // Properties
825   //-----------------------------------------------------------------------------------------------------------------
826
827   /**
828    * Debug mode.
829    *
830    * @see #CONTEXT_debug
831    * @return
832    *    <jk>true</jk> if debug mode is enabled.
833    */
834   protected boolean isDebug() {
835      return debug;
836   }
837
838   /**
839    * Locale.
840    *
841    * @see #CONTEXT_locale
842    * @return
843    *    The default locale for serializer and parser sessions.
844    */
845   protected final Locale getDefaultLocale() {
846      return locale;
847   }
848
849   /**
850    * Media type.
851    *
852    * @see #CONTEXT_mediaType
853    * @return
854    *    The default media type value for serializer and parser sessions.
855    */
856   protected final MediaType getDefaultMediaType() {
857      return mediaType;
858   }
859
860   /**
861    * Time zone.
862    *
863    * @see #CONTEXT_timeZone
864    * @return
865    *    The default timezone for serializer and parser sessions.
866    */
867   protected final TimeZone getDefaultTimeZone() {
868      return timeZone;
869   }
870
871   //-----------------------------------------------------------------------------------------------------------------
872   // Other methods
873   //-----------------------------------------------------------------------------------------------------------------
874
875   @Override /* Object */
876   public String toString() {
877      return SimpleJsonSerializer.DEFAULT_READABLE.toString(toMap());
878   }
879
880   /**
881    * Returns the properties defined on this bean context as a simple map for debugging purposes.
882    *
883    * @return A new map containing the properties defined on this context.
884    */
885   public OMap toMap() {
886      return new DefaultFilteringOMap()
887         .a("Context", new DefaultFilteringOMap()
888            .a("identityCode", identityCode)
889            .a("propertyStore", propertyStore)
890         );
891   }
892}