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.config;
018
019
020import java.util.*;
021
022import org.apache.juneau.marshall.serializer.*;
023import static org.apache.juneau.commons.utils.Shorts.*;
024
025/**
026 * A fluent builder for adding or updating a single {@link Config} entry.
027 *
028 * <p>
029 * This is an ergonomic, self-documenting alternative to the multi-argument
030 * {@link Config#set(String, Object, Serializer, String, String, List)} overload.  That overload packs several distinct
031 * "leave-unchanged versus clear" sentinel conventions into one call (a <jk>null</jk> comment leaves the existing comment
032 * untouched while a blank comment clears it; a <jk>null</jk> pre-lines list leaves the existing pre-lines untouched while
033 * an empty list clears them).  This builder replaces those sentinels with explicit methods:
034 * <ul>
035 *    <li><b>Not calling</b> a facet method leaves that facet untouched (no sentinel needed).
036 *    <li>An explicit setter ({@link #comment(String)}, {@link #preLines(List)}, {@link #serializer(Serializer)},
037 *       {@link #modifiers(String)}) sets that facet.
038 *    <li>An explicit clear ({@link #clearComment()}, {@link #clearPreLines()}) intentionally clears that facet rather than
039 *       relying on a magic blank string or empty list.
040 * </ul>
041 *
042 * <p>
043 * The write is performed by the terminal {@link #set(Object)} or {@link #set()} method.  The result is identical to the
044 * equivalent {@link Config#set(String, Object, Serializer, String, String, List)} call.
045 *
046 * <h5 class='section'>Example:</h5>
047 * <p class='bjava'>
048 *    <jc>// Add an entry with a comment and pre-lines.</jc>
049 *    <jv>config</jv>.entry(<js>"MySection/myKey"</js>)
050 *       .comment(<js>"My comment"</js>)
051 *       .preLines(List.<jsm>of</jsm>(<js>"# A pre-line"</js>))
052 *       .set(<js>"My value"</js>);
053 *
054 *    <jc>// Update only the comment on an existing entry, leaving the value and pre-lines untouched.</jc>
055 *    <jv>config</jv>.entry(<js>"MySection/myKey"</js>)
056 *       .comment(<js>"Updated comment"</js>)
057 *       .set();
058 *
059 *    <jc>// Clear the comment on an existing entry.</jc>
060 *    <jv>config</jv>.entry(<js>"MySection/myKey"</js>)
061 *       .clearComment()
062 *       .set();
063 * </p>
064 *
065 * <h5 class='section'>Notes:</h5><ul>
066 *    <li class='note'>This builder is not thread safe and is intended to be used and discarded within a single statement.
067 * </ul>
068 *
069 * <h5 class='section'>See Also:</h5><ul>
070 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauConfig">juneau-config</a>
071 * </ul>
072 */
073public class EntryBuilder {
074
075   private final Config config;
076   private final String key;
077
078   private Object value;
079   private Serializer serializer;
080   private String modifiers;
081   private String comment;
082   private List<String> preLines;
083
084   /**
085    * Constructor.
086    *
087    * @param config The config that the entry belongs to.
088    * @param key
089    *    The key of the entry to add or update.
090    *    <br>Must not be <jk>null</jk>.
091    */
092   protected EntryBuilder(Config config, String key) {
093      reqnn("key", key);
094      this.config = config;
095      this.key = key;
096   }
097
098   /**
099    * Specifies the value to store on the entry.
100    *
101    * <p>
102    * Equivalent to passing the value to the terminal {@link #set(Object)} method.
103    *
104    * @param value
105    *    The new value POJO.
106    *    <br>Serialized to a string using the registered (or {@link #serializer(Serializer) specified}) serializer.
107    * @return This object.
108    */
109   public EntryBuilder value(Object value) {
110      this.value = value;
111      return this;
112   }
113
114   /**
115    * Specifies the serializer to use to serialize the value.
116    *
117    * <p>
118    * If not called, the serializer registered on the config is used.
119    *
120    * @param value The serializer to use for serializing the value.
121    * @return This object.
122    */
123   public EntryBuilder serializer(Serializer value) {
124      this.serializer = value;
125      return this;
126   }
127
128   /**
129    * Specifies the modifiers to apply to the value (e.g. <js>"*"</js> for encoded values).
130    *
131    * <p>
132    * If not called, the modifiers on any existing entry are left untouched.
133    *
134    * @param value The modifiers to apply to the value.
135    * @return This object.
136    */
137   public EntryBuilder modifiers(String value) {
138      this.modifiers = value;
139      return this;
140   }
141
142   /**
143    * Specifies the same-line comment to add to the entry.
144    *
145    * <p>
146    * If not called, the comment on any existing entry is left untouched.
147    * <br>Use {@link #clearComment()} to explicitly remove an existing comment.
148    *
149    * @param value The same-line comment to add to the entry.  Must not be <jk>null</jk> (use {@link #clearComment()} instead).
150    * @return This object.
151    */
152   public EntryBuilder comment(String value) {
153      reqnn("value", value);
154      this.comment = value;
155      return this;
156   }
157
158   /**
159    * Explicitly clears the same-line comment on the entry.
160    *
161    * @return This object.
162    */
163   public EntryBuilder clearComment() {
164      this.comment = "";
165      return this;
166   }
167
168   /**
169    * Specifies the comment or blank lines to add before the entry.
170    *
171    * <p>
172    * If not called, the pre-lines on any existing entry are left untouched.
173    * <br>Use {@link #clearPreLines()} to explicitly remove existing pre-lines.
174    *
175    * @param value The comment or blank lines to add before the entry.  Must not be <jk>null</jk> (use {@link #clearPreLines()} instead).
176    * @return This object.
177    */
178   public EntryBuilder preLines(List<String> value) {
179      reqnn("value", value);
180      this.preLines = value;
181      return this;
182   }
183
184   /**
185    * Explicitly clears the pre-lines on the entry.
186    *
187    * @return This object.
188    */
189   public EntryBuilder clearPreLines() {
190      this.preLines = Collections.emptyList();
191      return this;
192   }
193
194   /**
195    * Adds or updates the entry using the value specified via {@link #value(Object)}.
196    *
197    * @return The config that this builder belongs to.
198    * @throws SerializeException
199    *    If serializer could not serialize the value or if a serializer is not registered with this config file.
200    * @throws UnsupportedOperationException If configuration is read only.
201    */
202   public Config set() throws SerializeException {
203      return config.set(key, value, serializer, modifiers, comment, preLines);
204   }
205
206   /**
207    * Adds or updates the entry using the specified value.
208    *
209    * @param value
210    *    The new value POJO.
211    *    <br>Serialized to a string using the registered (or {@link #serializer(Serializer) specified}) serializer.
212    * @return The config that this builder belongs to.
213    * @throws SerializeException
214    *    If serializer could not serialize the value or if a serializer is not registered with this config file.
215    * @throws UnsupportedOperationException If configuration is read only.
216    */
217   public Config set(Object value) throws SerializeException {
218      this.value = value;
219      return set();
220   }
221}