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 java.util.*;
020
021import org.apache.juneau.common.utils.*;
022import org.apache.juneau.cp.*;
023import org.apache.juneau.internal.*;
024
025/**
026 * Base class for bean builders.
027 *
028 * <h5 class='section'>See Also:</h5><ul>
029 * </ul>
030 *
031 * @param <T> The bean type that the builder creates.
032 */
033public class BeanBuilder<T> {
034
035   private Class<? extends T> type, defaultType;
036   private T impl;
037   private final BeanStore beanStore;
038
039   /**
040    * Constructor.
041    *
042    * @param beanStore The bean store to use for creating beans.
043    * @param defaultType The default bean type that this builder creates.
044    */
045   protected BeanBuilder(Class<? extends T> defaultType, BeanStore beanStore) {
046      this.defaultType = type = defaultType;
047      this.beanStore = beanStore;
048   }
049
050   /**
051    * Constructor.
052    *
053    * @param defaultType The type of bean being created.
054    */
055   protected BeanBuilder(Class<? extends T> defaultType) {
056      this(defaultType, BeanStore.INSTANCE);
057   }
058
059   /**
060    * Copy constructor.
061    *
062    * @param copyFrom The bean store to copy from.
063    */
064   protected BeanBuilder(BeanBuilder<T> copyFrom) {
065      type = copyFrom.type;
066      impl = copyFrom.impl;
067      beanStore = copyFrom.beanStore;
068   }
069
070   /**
071    * Creates the bean.
072    *
073    * @return A new bean.
074    */
075   public T build() {
076      if (impl != null)
077         return impl;
078      if (type == null || type == defaultType)
079         return buildDefault();
080      return creator().run();
081   }
082
083   /**
084    * Instantiates the creator for this bean.
085    *
086    * <p>
087    * Subclasses can override this to provide specialized handling.
088    *
089    * @return The creator for this bean.
090    */
091   protected BeanCreator<? extends T> creator() {
092      return beanStore
093         .createBean(type().orElseThrow(() -> new IllegalStateException("Type not specified.")))
094         .builder(BeanBuilder.class, this);
095   }
096
097   /**
098    * Creates the bean when the bean type is <jk>null</jk> or is the default value.
099    *
100    * @return A new bean.
101    */
102   protected T buildDefault() {
103      return beanStore
104         .createBean(type().orElseThrow(() -> new IllegalStateException("Type not specified.")))
105         .builder(BeanBuilder.class, this)
106         .run();
107   }
108
109   /**
110    * Overrides the bean type produced by the {@link #build()} method.
111    *
112    * <p>
113    * Use this method if you want to instantiated a bean subclass.
114    *
115    * @param value The setting value.
116    * @return  This object.
117    */
118   @SuppressWarnings("unchecked")
119   public BeanBuilder<T> type(Class<?> value) {
120      type = (Class<T>)value;
121      return this;
122   }
123
124   /**
125    * Returns the implementation type specified via {@link #type(Class)}.
126    *
127    * @return The implementation type specified via {@link #type(Class)}.
128    */
129   protected Optional<Class<? extends T>> type() {
130      return Utils.opt(type);
131   }
132
133   /**
134    * Overrides the bean returned by the {@link #build()} method.
135    *
136    * <p>
137    * Use this method if you want this builder to return an already-instantiated bean.
138    *
139    * @param value The setting value.
140    * @return  This object.
141    */
142   @SuppressWarnings("unchecked")
143   public BeanBuilder<T> impl(Object value) {
144      this.impl = (T)value;
145      return this;
146   }
147
148   /**
149    * Returns the override bean specified via {@link #impl(Object)}.
150    *
151    * @return The override bean specified via {@link #impl(Object)}.
152    */
153   protected Optional<T> impl() {
154      return Utils.opt(impl);
155   }
156
157   /**
158    * Returns the bean store passed in through the constructor.
159    *
160    * @return The bean store passed in through the constructor.
161    */
162   public BeanStore beanStore() {
163      return beanStore;
164   }
165}