001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau;
018
019import static java.util.Collections.*;
020import static org.apache.juneau.collections.JsonMap.*;
021import static org.apache.juneau.common.utils.StringUtils.*;
022
023import java.text.*;
024import java.util.*;
025import java.util.function.*;
026
027import org.apache.juneau.collections.*;
028import org.apache.juneau.common.utils.*;
029import org.apache.juneau.internal.*;
030
031/**
032 * A one-time-use non-thread-safe object that's meant to be used once and then thrown away.
033 *
034 * <h5 class='section'>Notes:</h5><ul>
035 *    <li class='warn'>This class is not typically thread safe.
036 * </ul>
037 *
038 * <h5 class='section'>See Also:</h5><ul>
039 * </ul>
040 */
041public abstract class ContextSession {
042
043   //-----------------------------------------------------------------------------------------------------------------
044   // Builder
045   //-----------------------------------------------------------------------------------------------------------------
046
047   /**
048    * Builder class.
049    */
050   public static abstract class Builder {
051      Context ctx;
052      JsonMap properties;
053      boolean unmodifiable;
054      Boolean debug;
055
056      /**
057       * Constructor.
058       *
059       * @param ctx The context creating this session.
060       */
061      protected Builder(Context ctx) {
062         this.ctx = ctx;
063         debug = ctx.debug;
064      }
065
066      /**
067       * Build the object.
068       *
069       * @return The built object.
070       */
071      public abstract ContextSession build();
072
073      /**
074       * Debug mode.
075       *
076       * <p>
077       * Enables the following additional information during parsing:
078       * <ul>
079       *    <li> When bean setters throws exceptions, the exception includes the object stack information in order to determine how that method was invoked.
080       * </ul>
081       *
082       * <p>
083       * If not specified, defaults to {@link Context.Builder#debug()}.
084       *
085       * <h5 class='section'>See Also:</h5><ul>
086       *    <li class='ja'>{@link org.apache.juneau.annotation.BeanConfig#debug()}
087       *    <li class='jm'>{@link org.apache.juneau.Context.Builder#debug()}
088       *
089       * @param value
090       *    The new value for this property.
091       *    <br>Can be <jk>null</jk>.  Value will be ignored.
092       * @return This object.
093       */
094      public Builder debug(Boolean value) {
095         if (value != null)
096            debug = value;
097         return this;
098      }
099
100      /**
101       * Create an unmodifiable session.
102       *
103       * <p>
104       * The created ContextSession object will be unmodifiable which makes it suitable for caching and reuse.
105       *
106       * @return This object.
107       */
108      public Builder unmodifiable() {
109         unmodifiable = true;
110         return this;
111      }
112
113      /**
114       * Session properties.
115       *
116       * <p>
117       * Session properties are generic key-value pairs that can be passed through the session and made
118       * available to any customized serializers/parsers or swaps.
119       *
120       * @param value
121       *    The new value for this property.
122       *    <br>Can be <jk>null</jk>.
123       * @return This object.
124       */
125      public Builder properties(Map<String,Object> value) {
126         properties = JsonMap.of(value);
127         return this;
128      }
129
130      /**
131       * Adds a property to this session.
132       *
133       * @param key The property key.
134       * @param value The property value.
135       * @return This object.
136       */
137      public Builder property(String key, Object value) {
138         if (properties == null)
139            properties = JsonMap.create();
140         if (value == null) {
141            properties.remove(key);
142         } else {
143            properties.put(key, value);
144         }
145         return this;
146      }
147
148      /**
149       * Applies a consumer to this builder if it's the specified type.
150       *
151       * @param <T> The expected type.
152       * @param type The expected type.
153       * @param apply   The consumer to apply.
154       * @return This object.
155       */
156      public <T> Builder apply(Class<T> type, Consumer<T> apply) {
157         if (type.isInstance(this))
158            apply.accept(type.cast(this));
159         return this;
160      }
161   }
162
163   //-----------------------------------------------------------------------------------------------------------------
164   // Instance
165   //-----------------------------------------------------------------------------------------------------------------
166
167   private final JsonMap properties;
168   private List<String> warnings;   // Any warnings encountered.
169
170   private final Context ctx;
171   private final boolean debug;
172   private final boolean unmodifiable;
173
174   /**
175    * Default constructor.
176    *
177    * @param builder The builder for this object
178    */
179   protected ContextSession(Builder builder) {
180      ctx = builder.ctx;
181      unmodifiable = builder.unmodifiable;
182      JsonMap sp = builder.properties == null ? JsonMap.EMPTY_MAP : builder.properties;
183      if (unmodifiable)
184         sp = sp.unmodifiable();
185      properties = sp;
186      debug = builder.debug;
187   }
188
189   /**
190    * Returns the session properties on this session.
191    *
192    * @return The session properties on this session.  Never <jk>null</jk>.
193    */
194   public final JsonMap getSessionProperties() {
195      return properties;
196   }
197
198   /**
199    * Returns the context that created this session.
200    *
201    * @return The context that created this session.
202    */
203   public Context getContext() {
204      return ctx;
205   }
206
207   /**
208    * Logs a warning message.
209    *
210    * @param msg The warning message.
211    * @param args Optional {@link MessageFormat}-style arguments.
212    */
213   public void addWarning(String msg, Object... args) {
214      if (unmodifiable)
215         return;
216      if (warnings == null)
217         warnings = new LinkedList<>();
218      warnings.add((warnings.size() + 1) + ": " + format(msg, args));
219   }
220
221   /**
222    * Returns the warnings that occurred in this session.
223    *
224    * @return The warnings that occurred in this session, or <jk>null</jk> if no warnings occurred.
225    */
226   public final List<String> getWarnings() {
227      return warnings == null ? emptyList() : warnings;
228   }
229
230   /**
231    * Throws a {@link BeanRuntimeException} if any warnings occurred in this session and debug is enabled.
232    */
233   public void checkForWarnings() {
234      if (debug && ! getWarnings().isEmpty())
235         throw new BeanRuntimeException("Warnings occurred in session: \n" + Utils.join(getWarnings(), "\n"));
236   }
237
238   //-----------------------------------------------------------------------------------------------------------------
239   // Configuration properties
240   //-----------------------------------------------------------------------------------------------------------------
241
242   /**
243    * Debug mode enabled.
244    *
245    * @see Context.Builder#debug()
246    * @return
247    *    <jk>true</jk> if debug mode is enabled.
248    */
249   public boolean isDebug() {
250      return debug;
251   }
252
253   //-----------------------------------------------------------------------------------------------------------------
254   // Other methods
255   //-----------------------------------------------------------------------------------------------------------------
256
257   /**
258    * Returns the properties on this bean as a map for debugging.
259    *
260    * @return The properties on this bean as a map for debugging.
261    */
262   protected JsonMap properties() {
263      return filteredMap("debug", debug);
264   }
265
266   @Override /* Object */
267   public String toString() {
268      return Utils2.toPropertyMap(this).asReadableString();
269   }
270}