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}