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.marshall.cp;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.marshall.*;
025
026/**
027 * Utility class for instantiating a Context bean.
028 *
029 * <p>
030 * Contains either a pre-existing Context bean, or a builder for that bean.
031 * If it's a builder, then annotations can be applied to it.
032 *
033 *
034 * @param <T> The bean type.
035 */
036@SuppressWarnings({
037   "unchecked" // Casts of builder.build() and the supplied value to T, and of the type to Class<? extends Context>, are guaranteed by the creator's bean type.
038})
039public class ContextBeanCreator<T> {
040
041   /**
042    * Creator.
043    *
044    * @param <T> The bean type.
045    * @param type The bean type.
046    * @return A new creator object.
047    */
048   public static <T> ContextBeanCreator<T> create(Class<T> type) {
049      return new ContextBeanCreator<>(type);
050   }
051
052   private final Class<T> type;
053   private T impl;
054   private Context.Builder<?> builder;
055
056   /**
057    * Constructor.
058    *
059    * @param type The bean type.
060    */
061   protected ContextBeanCreator(Class<T> type) {
062      this.type = type;
063   }
064
065   /**
066    * Copy constructor.
067    *
068    * @param copyFrom The creator to copy from.
069    */
070   protected ContextBeanCreator(ContextBeanCreator<T> copyFrom) {
071      this.type = copyFrom.type;
072      this.impl = copyFrom.impl;
073      this.builder = Context.Builder.copyOrNull(copyFrom.builder);
074   }
075
076   /**
077    * Applies the specified annotations to all applicable serializer builders in this group.
078    *
079    * @param work The annotations to apply.
080    * @return This object.
081    */
082   public ContextBeanCreator<T> apply(AnnotationWorkList work) {
083      if (nn(builder))
084         builder.apply(work);
085      return this;
086   }
087
088   /**
089    * Returns access to the inner builder if the builder exists and is of the specified type.
090    *
091    * @param <B> The builder class type.
092    * @param c The builder class type.
093    * @return An optional containing the builder if it exists.
094    */
095   public <B extends Context.Builder<?>> Optional<B> builder(Class<B> c) {
096      return o(c.isInstance(builder) ? c.cast(builder) : null);
097   }
098
099   /**
100    * Applies an operation to the builder in this creator object.
101    *
102    * <p>
103    * Typically used to allow you to execute operations without breaking the fluent flow of the client builder.
104    * The operation is ignored if the builder isn't the specified type.
105    *
106    * @param <B> The builder class type.
107    * @param c The builder class type.
108    * @param operation The operation to apply.
109    * @return This object.
110    */
111   public <B extends Context.Builder<?>> ContextBeanCreator<T> builder(Class<B> c, Consumer<B> operation) {
112      if (c.isInstance(builder))
113         operation.accept(c.cast(builder));
114      return this;
115   }
116
117   /**
118    * Returns true if any of the annotations/appliers can be applied to the inner builder (if it has one).
119    *
120    * @param work The work to check.
121    * @return This object.
122    */
123   public boolean canApply(AnnotationWorkList work) {
124      if (nn(builder))
125         return (builder.canApply(work));
126      return false;
127   }
128
129   /**
130    * Creates a new copy of this creator.
131    *
132    * @return A new copy of this creator.
133    */
134   public ContextBeanCreator<T> copy() {
135      return new ContextBeanCreator<>(this);
136   }
137
138   /**
139    * Returns the built bean.
140    *
141    * @return The built bean, or <jk>null</jk> if neither an implementation nor a builder has been set on this creator.
142    */
143   public T create() {
144      if (nn(impl))
145         return impl;
146      if (nn(builder))
147         return (T)builder.build();
148      return null;
149   }
150
151   /**
152    * Sets an already instantiated object on this creator.
153    *
154    * @param value The bean to set.
155    * @return This object.
156    */
157   public ContextBeanCreator<T> impl(Object value) {
158      this.impl = (T)value;
159      return this;
160   }
161
162   /**
163    * Sets the implementation type of the bean.
164    *
165    * <p>
166    * The class type must extend from {@link Context} and have a builder create method.
167    *
168    * @param value The bean type.
169    * @return This object.
170    */
171   public ContextBeanCreator<T> type(Class<? extends T> value) {
172      builder = Context.createBuilder((Class<? extends Context>)value);
173      if (builder == null)
174         throw iaex("Creator for class %s not found.", cn(value));
175      return this;
176   }
177}