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