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.internal.*;
019import org.apache.juneau.json.*;
020
021/**
022 * A reusable stateless thread-safe read-only configuration, typically used for creating one-time use {@link Session}
023 * objects.
024 *
025 * <p>
026 * Contexts are created through the {@link ContextBuilder#build()} method (and subclasses of {@link ContextBuilder}).
027 *
028 * <p>
029 * Subclasses MUST implement the following constructor:
030 *
031 * <p class='bcode w800'>
032 *    <jk>public</jk> T(PropertyStore);
033 * </p>
034 *
035 * <p>
036 * Besides that restriction, a context object can do anything you desire.
037 * <br>However, it MUST be thread-safe and all fields should be declared final to prevent modification.
038 * <br>It should NOT be used for storing temporary or state information.
039 *
040 * @see PropertyStore
041 */
042public abstract class Context {
043
044   private final PropertyStore propertyStore;
045   private final int identityCode;
046
047   /**
048    * Constructor for this class.
049    *
050    * <p>
051    * Subclasses MUST implement the same public constructor.
052    *
053    * @param ps The read-only configuration for this context object.
054    * @param allowReuse If <jk>true</jk>, subclasses that share the same property store values can be reused.
055    */
056   public Context(PropertyStore ps, boolean allowReuse) {
057      this.propertyStore = ps == null ? PropertyStore.DEFAULT : ps;
058      this.identityCode = allowReuse ? new HashCode().add(getClass().getName()).add(ps).get() : System.identityHashCode(this);
059   }
060
061   /**
062    * Returns the raw property value with the specified name.
063    *
064    * @param key The property name.
065    * @return The property value, or <jk>null</jk> if it doesn't exist.
066    */
067   public Object getProperty(String key) {
068      return propertyStore.getProperty(key);
069   }
070
071   /**
072    * Returns the property value with the specified name.
073    *
074    * @param key The property name.
075    * @param c The class to cast or convert the value to.
076    * @param def The default value.
077    * @return The property value, or the default value if it doesn't exist.
078    */
079   public final <T> T getProperty(String key, Class<T> c, T def) {
080      return propertyStore.getProperty(key, c, def);
081   }
082
083   /**
084    * Shortcut for calling <code>getProperty(key, Boolean.<jk>class</jk>, def)</code>.
085    *
086    * @param key The property name.
087    * @param def The default value.
088    * @return The property value, or the default value if it doesn't exist.
089    */
090   public final Boolean getBooleanProperty(String key, Boolean def) {
091      return getProperty(key, Boolean.class, def);
092   }
093
094   /**
095    * Shortcut for calling <code>getProperty(key, Integer.<jk>class</jk>, def)</code>.
096    *
097    * @param key The property name.
098    * @param def The default value.
099    * @return The property value, or the default value if it doesn't exist.
100    */
101   public final Integer getIntegerProperty(String key, Integer def) {
102      return getProperty(key, Integer.class, def);
103   }
104
105   /**
106    * Shortcut for calling <code>getProperty(key, Long.<jk>class</jk>, def)</code>.
107    *
108    * @param key The property name.
109    * @param def The default value.
110    * @return The property value, or the default value if it doesn't exist.
111    */
112   public final Long getLongProperty(String key, Long def) {
113      return getProperty(key, Long.class, def);
114   }
115
116   /**
117    * Shortcut for calling <code>getProperty(key, String.<jk>class</jk>, def)</code>.
118    *
119    * @param key The property name.
120    * @param def The default value.
121    * @return The property value, or the default value if it doesn't exist.
122    */
123   public final String getStringProperty(String key, String def) {
124      return getProperty(key, String.class, def);
125   }
126
127   /**
128    * Returns a property as a parsed comma-delimited list of strings.
129    *
130    * @param key The property name.
131    * @param def The default value.
132    * @return The property value, or the default value if it doesn't exist.
133    */
134   public final String[] getCdlProperty(String key, String def) {
135      return StringUtils.split(StringUtils.emptyIfNull(getProperty(key, String.class, def)));
136   }
137
138   /**
139    * Same as {@link #getStringProperty(String, String)} but returns a blank instead of the default value if it resolves to <js>"NONE"</js>.
140    *
141    * @param key The property name.
142    * @param def The default value.
143    * @return The property value, or the default value if it doesn't exist.
144    */
145   public final String getStringPropertyWithNone(String key, String def) {
146      String s = getProperty(key, String.class, def);
147      return "NONE".equalsIgnoreCase(s) ? "" : s;
148   }
149
150   /**
151    * Returns the class property with the specified name.
152    *
153    * @param key The property name.
154    * @param type The class type of the property.
155    * @param def The default value.
156    * @return The property value, or the default value if it doesn't exist.
157    */
158   public final <T> Class<? extends T> getClassProperty(String key, Class<T> type, Class<? extends T> def) {
159      return propertyStore.getClassProperty(key, type, def);
160   }
161
162   /**
163    * Returns the array property value with the specified name.
164    *
165    * @param key The property name.
166    * @param eType The class type of the elements in the property.
167    * @return The property value, or the default value if it doesn't exist.
168    */
169   public final <T> T[] getArrayProperty(String key, Class<T> eType) {
170      return propertyStore.getArrayProperty(key, eType);
171   }
172
173   /**
174    * Returns the array property value with the specified name.
175    *
176    * @param key The property name.
177    * @param eType The class type of the elements in the property.
178    * @param def The default value.
179    * @return The property value, or the default value if it doesn't exist.
180    */
181   public final <T> T[] getArrayProperty(String key, Class<T> eType, T[] def) {
182      return propertyStore.getArrayProperty(key, eType, def);
183   }
184
185   /**
186    * Returns the class array property with the specified name.
187    *
188    * @param key The property name.
189    * @return The property value, or an empty array if it doesn't exist.
190    */
191   public final Class<?>[] getClassArrayProperty(String key) {
192      return propertyStore.getClassArrayProperty(key);
193   }
194
195   /**
196    * Returns the class array property with the specified name.
197    *
198    * @param key The property name.
199    * @param def The default value.
200    * @return The property value, or an empty array if it doesn't exist.
201    */
202   public final Class<?>[] getClassArrayProperty(String key, Class<?>[] def) {
203      return propertyStore.getClassArrayProperty(key, def);
204   }
205
206   /**
207    * Returns the class array property with the specified name.
208    *
209    * @param key The property name.
210    * @param eType The class type of the elements in the property.
211    * @return The property value, or an empty array if it doesn't exist.
212    */
213   public final <T> Class<T>[] getClassArrayProperty(String key, Class<T> eType) {
214      return propertyStore.getClassArrayProperty(key, eType);
215   }
216
217   /**
218    * Returns the set property with the specified name.
219    *
220    * @param key The property name.
221    * @param eType The class type of the elements in the property.
222    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
223    */
224   public final <T> Set<T> getSetProperty(String key, Class<T> eType) {
225      return propertyStore.getSetProperty(key, eType);
226   }
227
228   /**
229    * Returns the set property with the specified name.
230    *
231    * @param key The property name.
232    * @param eType The class type of the elements in the property.
233    * @param def The default value if the property doesn't exist or is empty.
234    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or the default value if it doesn't exist or is empty.
235    */
236   public final <T> Set<T> getSetProperty(String key, Class<T> eType, Set<T> def) {
237      return propertyStore.getSetProperty(key, eType, def);
238   }
239
240   /**
241    * Returns the class set property with the specified name.
242    *
243    * @param key The property name.
244    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
245    */
246   public final Set<Class<?>> getClassSetProperty(String key) {
247      return propertyStore.getClassSetProperty(key);
248   }
249
250   /**
251    * Returns the class set property with the specified name.
252    *
253    * @param key The property name.
254    * @param eType The class type of the elements in the property.
255    * @return The property value as an unmodifiable <c>LinkedHashSet</c>, or an empty set if it doesn't exist.
256    */
257   public final <T> Set<Class<T>> getClassSetProperty(String key, Class<T> eType) {
258      return propertyStore.getClassSetProperty(key, eType);
259   }
260
261   /**
262    * Returns the list property with the specified name.
263    *
264    * @param key The property name.
265    * @param eType The class type of the elements in the property.
266    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
267    */
268   public final <T> List<T> getListProperty(String key, Class<T> eType) {
269      return propertyStore.getListProperty(key, eType);
270   }
271
272   /**
273    * Returns the list property with the specified name.
274    *
275    * @param key The property name.
276    * @param eType The class type of the elements in the property.
277    * @param def The default value if the property doesn't exist or is empty.
278    * @return The property value as an unmodifiable <c>ArrayList</c>, or the default value if it doesn't exist or is empty.
279    */
280   public final <T> List<T> getListProperty(String key, Class<T> eType, List<T> def) {
281      return propertyStore.getListProperty(key, eType, def);
282   }
283
284   /**
285    * Returns the class list property with the specified name.
286    *
287    * @param key The property name.
288    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
289    */
290   public final List<Class<?>> getClassListProperty(String key) {
291      return propertyStore.getClassListProperty(key);
292   }
293
294   /**
295    * Returns the class list property with the specified name.
296    *
297    * @param key The property name.
298    * @param eType The class type of the elements in the property.
299    * @return The property value as an unmodifiable <c>ArrayList</c>, or an empty list if it doesn't exist.
300    */
301   public final <T> List<Class<T>> getClassListProperty(String key, Class<T> eType) {
302      return propertyStore.getClassListProperty(key, eType);
303   }
304
305   /**
306    * Returns the map property with the specified name.
307    *
308    * @param key The property name.
309    * @param eType The class type of the elements in the property.
310    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
311    */
312   public final <T> Map<String,T> getMapProperty(String key, Class<T> eType) {
313      return propertyStore.getMapProperty(key, eType);
314   }
315
316   /**
317    * Returns the class map property with the specified name.
318    *
319    * @param key The property name.
320    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
321    */
322   public final Map<String,Class<?>> getClassMapProperty(String key) {
323      return propertyStore.getClassMapProperty(key);
324   }
325
326   /**
327    * Returns the class map property with the specified name.
328    *
329    * @param key The property name.
330    * @param eType The class type of the elements in the property.
331    * @return The property value as an unmodifiable <c>LinkedHashMap</c>, or an empty map if it doesn't exist.
332    */
333   public final <T> Map<String,Class<T>> getClassMapProperty(String key, Class<T> eType) {
334      return propertyStore.getClassMapProperty(key, eType);
335   }
336
337   /**
338    * Returns an instance of the specified class, string, or object property.
339    *
340    * <p>
341    * If instantiating a class, assumes the class has a no-arg constructor.
342    * Otherwise, throws a runtime exception.
343    *
344    * @param key The property name.
345    * @param type The class type of the property.
346    * @param def
347    *    The default value if the property doesn't exist.
348    *    <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>.
349    * @return A new property instance.
350    */
351   public <T> T getInstanceProperty(String key, Class<T> type, Object def) {
352      return propertyStore.getInstanceProperty(key, type, def);
353   }
354
355   /**
356    * Returns an instance of the specified class, string, or object property.
357    *
358    * @param key The property name.
359    * @param type The class type of the property.
360    * @param def
361    *    The default value if the property doesn't exist.
362    *    <br>Can either be an instance of <c>T</c>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
363    * @param resolver
364    *    The resolver to use for instantiating objects.
365    * @param args
366    *    Arguments to pass to the constructor.
367    *    Constructors matching the arguments are always used before no-arg constructors.
368    * @return A new property instance.
369    */
370   public <T> T getInstanceProperty(String key, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
371      return propertyStore.getInstanceProperty(key, type, def, resolver, args);
372   }
373
374   /**
375    * Returns an instance of the specified class, string, or object property.
376    *
377    * @param key The property name.
378    * @param outer The outer object if the class we're instantiating is an inner class.
379    * @param type The class type of the property.
380    * @param def
381    *    The default value if the property doesn't exist.
382    *    <br>Can either be an instance of <c>T</c>, or a <code>Class&lt;? <jk>extends</jk> T&gt;</code>.
383    * @param resolver
384    *    The resolver to use for instantiating objects.
385    * @param args
386    *    Arguments to pass to the constructor.
387    *    Constructors matching the arguments are always used before no-arg constructors.
388    * @return A new property instance.
389    */
390   public <T> T getInstanceProperty(String key, Object outer, Class<T> type, Object def, ResourceResolver resolver, Object...args) {
391      return propertyStore.getInstanceProperty(key, outer, type, def, resolver, args);
392   }
393
394   /**
395    * Returns the specified property as an array of instantiated objects.
396    *
397    * @param key The property name.
398    * @param type The class type of the property.
399    * @param def The default object to return if the property doesn't exist.
400    * @return A new property instance.
401    */
402   public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def) {
403      return propertyStore.getInstanceArrayProperty(key, type, def);
404   }
405
406   /**
407    * Returns the specified property as an array of instantiated objects.
408    *
409    * @param key The property name.
410    * @param type The class type of the property.
411    * @param def The default object to return if the property doesn't exist.
412    * @param resolver
413    *    The resolver to use for instantiating objects.
414    * @param args
415    *    Arguments to pass to the constructor.
416    *    Constructors matching the arguments are always used before no-arg constructors.
417    * @return A new property instance.
418    */
419   public <T> T[] getInstanceArrayProperty(String key, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
420      return propertyStore.getInstanceArrayProperty(key, type, def, resolver, args);
421   }
422
423   /**
424    * Returns the specified property as an array of instantiated objects.
425    *
426    * @param key The property name.
427    * @param outer The outer object if the class we're instantiating is an inner class.
428    * @param type The class type of the property.
429    * @param def The default object to return if the property doesn't exist.
430    * @param resolver
431    *    The resolver to use for instantiating objects.
432    * @param args
433    *    Arguments to pass to the constructor.
434    *    Constructors matching the arguments are always used before no-arg constructors.
435    * @return A new property instance.
436    */
437   public <T> T[] getInstanceArrayProperty(String key, Object outer, Class<T> type, T[] def, ResourceResolver resolver, Object...args) {
438      return propertyStore.getInstanceArrayProperty(key, outer, type, def, resolver, args);
439   }
440
441   /**
442    * Returns the keys found in the specified property group.
443    *
444    * <p>
445    * The keys are NOT prefixed with group names.
446    *
447    * @param group The group name.
448    * @return The set of property keys, or an empty set if the group was not found.
449    */
450   public Set<String> getPropertyKeys(String group) {
451      return propertyStore.getPropertyKeys(group);
452   }
453
454   /**
455    * Returns the property store associated with this context.
456    *
457    * @return The property store associated with this context.
458    */
459   @BeanIgnore
460   public final PropertyStore getPropertyStore() {
461      return propertyStore;
462   }
463
464   /**
465    * Creates a builder from this context object.
466    *
467    * <p>
468    * Builders are used to define new contexts (e.g. serializers, parsers) based on existing configurations.
469    *
470    * @return A new ContextBuilder object.
471    */
472   public ContextBuilder builder() {
473      return null;
474   }
475
476   /**
477    * Create a new bean session based on the properties defined on this context.
478    *
479    * <p>
480    * Use this method for creating sessions if you don't need to override any
481    * properties or locale/timezone currently set on this context.
482    *
483    * @return A new session object.
484    */
485   public Session createSession() {
486      return createSession(createDefaultSessionArgs());
487   }
488
489   /**
490    * Create a new session based on the properties defined on this context combined with the specified
491    * runtime args.
492    *
493    * <p>
494    * Use this method for creating sessions if you don't need to override any
495    * properties or locale/timezone currently set on this context.
496    *
497    * @param args
498    *    The session arguments.
499    * @return A new session object.
500    */
501   public abstract Session createSession(SessionArgs args);
502
503   /**
504    * Defines default session arguments used when calling the {@link #createSession()} method.
505    *
506    * @return A SessionArgs object, possibly a read-only reusable instance.
507    */
508   public abstract SessionArgs createDefaultSessionArgs();
509
510   @Override /* Object */
511   public int hashCode() {
512      return identityCode;
513   }
514
515   /**
516    * Returns a uniqueness identity code for this context.
517    *
518    * @return A uniqueness identity code.
519    */
520   public int identityCode() {
521      return identityCode;
522   }
523
524   @Override /* Object */
525   public boolean equals(Object o) {
526      // Context objects are considered equal if they're the same class and have the same set of properties.
527      if (o == null)
528         return false;
529      if (o.getClass() != this.getClass())
530         return false;
531      Context c = (Context)o;
532      return (c.propertyStore.equals(propertyStore));
533   }
534
535   //-----------------------------------------------------------------------------------------------------------------
536   // Other methods
537   //-----------------------------------------------------------------------------------------------------------------
538
539   @Override /* Object */
540   public String toString() {
541      return SimpleJsonSerializer.DEFAULT_READABLE.toString(toMap());
542   }
543
544   /**
545    * Returns the properties defined on this bean context as a simple map for debugging purposes.
546    *
547    * @return A new map containing the properties defined on this context.
548    */
549   public ObjectMap toMap() {
550      return new DefaultFilteringObjectMap()
551         .append("Context", new DefaultFilteringObjectMap()
552            .append("identityCode", identityCode)
553            .append("propertyStore", propertyStore)
554         );
555   }
556}