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