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