This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new d260b12  Config API modernization.
d260b12 is described below

commit d260b127d39f70f36e008a3bcdf5c6fe055ace39
Author: JamesBognar <[email protected]>
AuthorDate: Mon Dec 27 10:35:49 2021 -0500

    Config API modernization.
---
 .../main/java/org/apache/juneau/config/Config.java | 165 +++++++--------------
 .../java/org/apache/juneau/config/ConfigMod.java   | 109 --------------
 .../main/java/org/apache/juneau/config/Entry.java  |  25 +---
 .../java/org/apache/juneau/config/Section.java     |  31 ++++
 .../apache/juneau/config/encode/ConfigEncoder.java |  54 -------
 .../apache/juneau/config/internal/ConfigMap.java   |   4 -
 .../juneau/config/internal/ConfigMapEntry.java     |  25 +---
 .../java/org/apache/juneau/config/mod/Mod.java     | 122 +++++++++++++++
 .../XorEncodeMod.java}                             |  50 ++++---
 .../config/{encode => mod}/package-info.java       |   2 +-
 juneau-doc/src/main/javadoc/overview.html          |  12 +-
 .../juneau/config/ConfigMapListenerTest.java       |   4 +-
 .../org/apache/juneau/config/ConfigMapTest.java    |  30 ++--
 .../java/org/apache/juneau/config/ConfigTest.java  |  65 ++++----
 14 files changed, 295 insertions(+), 403 deletions(-)

diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
index bd5a27b..bb6293d 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Config.java
@@ -13,11 +13,11 @@
 package org.apache.juneau.config;
 
 import static org.apache.juneau.assertions.Assertions.*;
-import static org.apache.juneau.config.ConfigMod.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.internal.SystemEnv.*;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 import static org.apache.juneau.internal.IOUtils.*;
+import static java.util.Collections.*;
 
 import java.io.*;
 import java.lang.annotation.*;
@@ -26,9 +26,9 @@ import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
-import org.apache.juneau.config.encode.*;
 import org.apache.juneau.config.event.*;
 import org.apache.juneau.config.internal.*;
+import org.apache.juneau.config.mod.*;
 import org.apache.juneau.config.store.*;
 import org.apache.juneau.config.vars.*;
 import org.apache.juneau.internal.*;
@@ -189,7 +189,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                ConfigStore store;
                WriterSerializer serializer;
                ReaderParser parser;
-               ConfigEncoder encoder;
+               Map<Character,Mod> mods;
                VarResolver varResolver;
                int binaryLineLength;
                BinaryFormat binaryFormat;
@@ -204,7 +204,8 @@ public final class Config extends Context implements 
ConfigEventListener {
                        store = ConfigFileStore.DEFAULT;
                        serializer = SimpleJsonSerializer.DEFAULT;
                        parser = JsonParser.DEFAULT;
-                       encoder = ConfigXorEncoder.INSTANCE;
+                       mods = new LinkedHashMap<>();
+                       mods(XorEncodeMod.INSTANCE);
                        varResolver = VarResolver.DEFAULT;
                        binaryLineLength = env("Config.binaryLineLength", -1);
                        binaryFormat = env("Config.binaryFormat", 
BinaryFormat.BASE64);
@@ -223,7 +224,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                        store = copyFrom.store;
                        serializer = copyFrom.serializer;
                        parser = copyFrom.parser;
-                       encoder = copyFrom.encoder;
+                       mods = new LinkedHashMap<>(copyFrom.mods);
                        varResolver = copyFrom.varResolver;
                        binaryLineLength = copyFrom.binaryLineLength;
                        binaryFormat = copyFrom.binaryFormat;
@@ -242,7 +243,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                        store = copyFrom.store;
                        serializer = copyFrom.serializer;
                        parser = copyFrom.parser;
-                       encoder = copyFrom.encoder;
+                       mods = new LinkedHashMap<>(copyFrom.mods);
                        varResolver = copyFrom.varResolver;
                        binaryLineLength = copyFrom.binaryLineLength;
                        binaryFormat = copyFrom.binaryFormat;
@@ -354,18 +355,18 @@ public final class Config extends Context implements 
ConfigEventListener {
                }
 
                /**
-                * Value encoder.
+                * Adds a value modifier.
                 *
                 * <p>
-                * The encoder to use for encoding encoded configuration values.
-                *
-                * @param value
-                *      The new value for this property.
-                *      <br>The default is {@link ConfigXorEncoder#INSTANCE}.
+                * Modifiers are used to modify entry value before being 
persisted.
+                * 
+                * @param values
+                *      The mods to apply to this config.
                 * @return This object.
                 */
-               public Builder encoder(ConfigEncoder value) {
-                       encoder = value;
+               public Builder mods(Mod...values) {
+                       for (Mod value : values)
+                               mods.put(value.getId(), value);
                        return this;
                }
 
@@ -539,7 +540,7 @@ public final class Config extends Context implements 
ConfigEventListener {
        final ConfigStore store;
        final WriterSerializer serializer;
        final ReaderParser parser;
-       final ConfigEncoder encoder;
+       final Map<Character,Mod> mods;
        final VarResolver varResolver;
        final int binaryLineLength;
        final BinaryFormat binaryFormat;
@@ -572,7 +573,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                serializer = builder.serializer;
                parser = builder.parser;
                beanSession = parser.getBeanContext().getSession();
-               encoder = builder.encoder;
+               mods = unmodifiableMap(new LinkedHashMap<>(builder.mods));
                varResolver = builder.varResolver;
                varSession = varResolver
                        .copy()
@@ -594,7 +595,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                configMap.register(this);
                serializer = copyFrom.serializer;
                parser = copyFrom.parser;
-               encoder = copyFrom.encoder;
+               mods = copyFrom.mods;
                varResolver = copyFrom.varResolver;
                this.varSession = varSession;
                binaryLineLength = copyFrom.binaryLineLength;
@@ -646,18 +647,28 @@ public final class Config extends Context implements 
ConfigEventListener {
 
                ConfigMapEntry ce = configMap.getEntry(sname, skey);
 
-               if (ce == null || ce.getValue() == null)
-                       return null;
+               if (ce == null) return null;
 
-               String val = ce.getValue();
-               for (ConfigMod m : ConfigMod.toReverse(ce.getModifiers())) {
-                       if (m == ENCODED) {
-                               if (encoder.isEncoded(val))
-                                       val = encoder.decode(key, val);
-                       }
-               }
+               return removeMods(ce.getModifiers(), ce.getValue());
+       }
 
-               return val;
+       String applyMods(String mods, String x) {
+               if (mods != null && x != null)
+                       for (int i = 0; i < mods.length(); i++)
+                               x = getMod(mods.charAt(i)).doApply(x);
+               return x;
+       }
+
+       String removeMods(String mods, String x) {
+               if (mods != null && x != null)
+                       for (int i = mods.length()-1; i > -1; i--)
+                               x = getMod(mods.charAt(i)).doRemove(x);
+               return x;
+       }
+
+       Mod getMod(char id) {
+               Mod x = mods.get(id);
+               return x == null ? Mod.NO_OP : x;
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -701,14 +712,7 @@ public final class Config extends Context implements 
ConfigEventListener {
                if (ce == null && value == null)
                        return this;
 
-               String mod = ce == null ? "" : ce.getModifiers();
-
-               String s = stringify(value);
-               for (ConfigMod m : ConfigMod.toModifiers(mod)) {
-                       if (m == ENCODED) {
-                               s = encoder.encode(key, s);
-                       }
-               }
+               String s = applyMods(ce == null ? null : ce.getModifiers(), 
stringify(value));
 
                configMap.setEntry(sname, skey, s, null, null, null);
                return this;
@@ -758,32 +762,6 @@ public final class Config extends Context implements 
ConfigEventListener {
         * @param serializer
         *      The serializer to use for serializing the object.
         *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
-        * @param modifier
-        *      Optional modifier to apply to the value.
-        *      <br>If <jk>null</jk>, then previous value will not be replaced.
-        * @param comment
-        *      Optional same-line comment to add to this value.
-        *      <br>If <jk>null</jk>, then previous value will not be replaced.
-        * @param preLines
-        *      Optional comment or blank lines to add before this entry.
-        *      <br>If <jk>null</jk>, then previous value will not be replaced.
-        * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
-        * @throws SerializeException
-        *      If serializer could not serialize the value or if a serializer 
is not registered with this config file.
-        * @throws UnsupportedOperationException If configuration is read only.
-        */
-       public Config set(String key, Object value, Serializer serializer, 
ConfigMod modifier, String comment, List<String> preLines) throws 
SerializeException {
-               return set(key, value, serializer, modifier == null ? null : 
new ConfigMod[]{modifier}, comment, preLines);
-       }
-
-       /**
-        * Same as {@link #set(String, Object)} but allows you to specify all 
aspects of a value.
-        *
-        * @param key The key.
-        * @param value The new value.
-        * @param serializer
-        *      The serializer to use for serializing the object.
-        *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
         * @param modifiers
         *      Optional modifiers to apply to the value.
         *      <br>If <jk>null</jk>, then previous value will not be replaced.
@@ -798,22 +776,16 @@ public final class Config extends Context implements 
ConfigEventListener {
         *      If serializer could not serialize the value or if a serializer 
is not registered with this config file.
         * @throws UnsupportedOperationException If configuration is read only.
         */
-       public Config set(String key, Object value, Serializer serializer, 
ConfigMod[] modifiers, String comment, List<String> preLines) throws 
SerializeException {
+       public Config set(String key, Object value, Serializer serializer, 
String modifiers, String comment, List<String> preLines) throws 
SerializeException {
                checkWrite();
                assertArgNotNull("key", key);
                String sname = sname(key);
                String skey = skey(key);
+               modifiers = nullIfEmpty(modifiers);
 
-               String s = serialize(value, serializer);
-               if (modifiers != null) {
-                       for (ConfigMod m : modifiers) {
-                               if (m == ENCODED) {
-                                       s = encoder.encode(key, s);
-                               }
-                       }
-               }
+               String s = applyMods(modifiers, serialize(value, serializer));
 
-               configMap.setEntry(sname, skey, s, modifiers == null ? null : 
ConfigMod.toModString(modifiers), comment, preLines);
+               configMap.setEntry(sname, skey, s, modifiers, comment, 
preLines);
                return this;
        }
 
@@ -842,13 +814,20 @@ public final class Config extends Context implements 
ConfigEventListener {
         * @return This object.
         * @throws UnsupportedOperationException If configuration is read only.
         */
-       public Config encodeEntries() {
+       public Config applyMods() {
                checkWrite();
                for (String section : configMap.getSections()) {
                        for (String key : configMap.getKeys(section)) {
                                ConfigMapEntry ce = configMap.getEntry(section, 
key);
-                               if (ce != null && ce.hasModifier('*') && ! 
encoder.isEncoded(ce.getValue())) {
-                                       configMap.setEntry(section, key, 
encoder.encode(section + '/' + key, ce.getValue()), null, null, null);
+                               if (ce.getModifiers() != null) {
+                                       String mods = ce.getModifiers();
+                                       String value = ce.getValue();
+                                       for (int i = 0; i < mods.length(); i++) 
{
+                                               Mod mod = 
getMod(mods.charAt(i));
+                                               if (! mod.isApplied(value)) {
+                                                       
configMap.setEntry(section, key, mod.apply(value), null, null, null);
+                                               }
+                                       }
                                }
                        }
                }
@@ -910,44 +889,6 @@ public final class Config extends Context implements 
ConfigEventListener {
        }
 
        /**
-        * Copies the entries in a section to the specified bean by calling the 
public setters on that bean.
-        *
-        * @param section
-        *      The section name to write from.
-        *      <br>If empty, refers to the default section.
-        *      <br>Must not be <jk>null</jk>.
-        * @param bean The bean to set the properties on.
-        * @param ignoreUnknownProperties
-        *      If <jk>true</jk>, don't throw an {@link 
IllegalArgumentException} if this section contains a key that doesn't
-        *      correspond to a setter method.
-        * @return An object map of the changes made to the bean.
-        * @throws ParseException If parser was not set on this config file or 
invalid properties were found in the section.
-        * @throws UnsupportedOperationException If configuration is read only.
-        */
-       public Config writeProperties(String section, Object bean, boolean 
ignoreUnknownProperties) throws ParseException {
-               checkWrite();
-               assertArgNotNull("bean", bean);
-               section = section(section);
-
-               Set<String> keys = configMap.getKeys(section);
-               if (keys == null)
-                       throw illegalArgumentException("Section ''{0}'' not 
found in configuration.", section);
-
-               BeanMap<?> bm = beanSession.toBeanMap(bean);
-               for (String k : keys) {
-                       BeanPropertyMeta bpm = bm.getPropertyMeta(k);
-                       if (bpm == null) {
-                               if (! ignoreUnknownProperties)
-                                       throw new ParseException("Unknown 
property ''{0}'' encountered in configuration section ''{1}''.", k, section);
-                       } else {
-                               bm.put(k, get(section + '/' + 
k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
-                       }
-               }
-
-               return this;
-       }
-
-       /**
         * Returns the section names defined in this config.
         *
         * @return The section names defined in this config.
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
deleted file mode 100644
index 716091b..0000000
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/ConfigMod.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.config;
-
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.util.*;
-
-import org.apache.juneau.config.encode.*;
-
-/**
- * Identifies the supported modification types for config entries.
- *
- * <ul class='seealso'>
- *     <li class='extlink'>{@source}
- * </ul>
- */
-public enum ConfigMod {
-
-       /**
-        * Encoded using the registered {@link ConfigEncoder}.
-        */
-       ENCODED("*");
-
-
-       private final String c;
-
-       private ConfigMod(String c) {
-               this.c = c;
-       }
-
-       /**
-        * Converts an array of modifiers to a modifier string.
-        *
-        * @param mods The modifiers.
-        * @return A modifier string, or an empty string if there are no 
modifiers.
-        */
-       public static String toModString(ConfigMod...mods) {
-               if (mods.length == 0)
-                       return "";
-               if (mods.length == 1)
-                       return mods[0].c;
-               StringBuilder sb = new StringBuilder(mods.length);
-               for (ConfigMod m : mods)
-                       sb.append(m.c);
-               return sb.toString();
-       }
-
-       private static ConfigMod fromChar(char c) {
-               if (c == '*')
-                       return ENCODED;
-               return null;
-       }
-
-       /**
-        * Converts a modifier string (e.g. <js>"^*"</js>) into a list of 
{@link ConfigMod Modifiers}
-        * in reverse order of how they appear in the string.
-        *
-        * @param s The modifier string.
-        * @return The list of modifiers, or an empty list if the string is 
empty or <jk>null</jk>.
-        */
-       public static List<ConfigMod> toReverse(String s) {
-               if (isEmpty(s))
-                       return Collections.emptyList();
-               if (s.length() == 1) {
-                       ConfigMod m = fromChar(s.charAt(0));
-                       return m == null ? Collections.<ConfigMod>emptyList() : 
Collections.singletonList(m);
-               }
-               List<ConfigMod> l = new ArrayList<>(s.length());
-               for (int i = s.length()-1; i >= 0; i--) {
-                       ConfigMod m = fromChar(s.charAt(i));
-                       if (m != null)
-                               l.add(m);
-               }
-               return l;
-       }
-
-       /**
-        * Converts a modifier string (e.g. <js>"^*"</js>) into a list of 
{@link ConfigMod Modifiers}.
-        *
-        * @param s The modifier string.
-        * @return The list of modifiers, or an empty list if the string is 
empty or <jk>null</jk>.
-        */
-       public static List<ConfigMod> toModifiers(String s) {
-               if (isEmpty(s))
-                       return Collections.emptyList();
-               if (s.length() == 1) {
-                       ConfigMod m = fromChar(s.charAt(0));
-                       return m == null ? Collections.<ConfigMod>emptyList() : 
Collections.singletonList(m);
-               }
-               List<ConfigMod> l = new ArrayList<>(s.length());
-               for (int i = 0; i < s.length(); i++) {
-                       ConfigMod m = fromChar(s.charAt(i));
-                       if (m != null)
-                               l.add(m);
-               }
-               return l;
-       }
-}
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Entry.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Entry.java
index 824afcc..c8d79ec 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Entry.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Entry.java
@@ -12,7 +12,6 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.config;
 
-import static org.apache.juneau.config.ConfigMod.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.BinaryFormat.*;
 import static java.util.Optional.*;
@@ -48,17 +47,7 @@ public class Entry {
        protected Entry(Config config, ConfigMap configMap, String sectionName, 
String entryName) {
                this.configEntry = configMap.getEntry(sectionName, entryName);
                this.config = config;
-
-               String v = null;
-               if (configEntry != null) {
-                       v = configEntry.getValue();
-                       if (! StringUtils.isEmpty(configEntry.getModifiers()))
-                               for (ConfigMod m : 
ConfigMod.toReverse(configEntry.getModifiers()))
-                                       if (m == ENCODED)
-                                               if (config.encoder.isEncoded(v))
-                                                       v = 
config.encoder.decode(configEntry.getKey(), v);
-               }
-               this.value = v;
+               this.value = configEntry == null ? null : 
config.removeMods(configEntry.getModifiers(), configEntry.getValue());
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
@@ -826,19 +815,9 @@ public class Entry {
        }
 
        /**
-        * Returns whether this entry has the specified modifier.
-        *
-        * @param m The modifier character.
-        * @return <jk>true</jk> if this entry is encoded.
-        */
-       public boolean hasModifier(char m) {
-               return configEntry.hasModifier(m);
-       }
-
-       /**
         * Returns the modifiers for this entry.
         *
-        * @return The modifiers for this entry, or an empty string if it has 
no modifiers.
+        * @return The modifiers for this entry, or <jk>null</jk> if it has no 
modifiers.
         */
        public String getModifiers() {
                return configEntry.getModifiers();
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java
index cfa98e0..9233415 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/Section.java
@@ -350,4 +350,35 @@ public class Section {
        public <T> Optional<T> asInterface(final Class<T> c) {
                return ofNullable(toInterface(c));
        }
+
+       /**
+        * Copies the entries in this section to the specified bean by calling 
the public setters on that bean.
+        *
+        * @param bean The bean to set the properties on.
+        * @param ignoreUnknownProperties
+        *      If <jk>true</jk>, don't throw an {@link 
IllegalArgumentException} if this section contains a key that doesn't
+        *      correspond to a setter method.
+        * @return An object map of the changes made to the bean.
+        * @throws ParseException If parser was not set on this config file or 
invalid properties were found in the section.
+        * @throws UnsupportedOperationException If configuration is read only.
+        */
+       public Section writeToBean(Object bean, boolean 
ignoreUnknownProperties) throws ParseException {
+               assertArgNotNull("bean", bean);
+               if (! isPresent()) throw illegalArgumentException("Section 
''{0}'' not found in configuration.", name);
+
+               Set<String> keys = configMap.getKeys(name);
+
+               BeanMap<?> bm = config.beanSession.toBeanMap(bean);
+               for (String k : keys) {
+                       BeanPropertyMeta bpm = bm.getPropertyMeta(k);
+                       if (bpm == null) {
+                               if (! ignoreUnknownProperties)
+                                       throw new ParseException("Unknown 
property ''{0}'' encountered in configuration section ''{1}''.", k, name);
+                       } else {
+                               bm.put(k, config.get(name + '/' + 
k).as(bpm.getClassMeta().getInnerClass()).orElse(null));
+                       }
+               }
+
+               return this;
+       }
 }
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
deleted file mode 100644
index 658d2e1..0000000
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigEncoder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// 
***************************************************************************************************************************
-// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
-// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
-// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
-// * with the License.  You may obtain a copy of the License at                
                                              *
-// *                                                                           
                                              *
-// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
-// *                                                                           
                                              *
-// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
-// * specific language governing permissions and limitations under the 
License.                                              *
-// 
***************************************************************************************************************************
-package org.apache.juneau.config.encode;
-
-import org.apache.juneau.config.*;
-
-/**
- * API for defining a string encoding/decoding mechanism for entries in {@link 
Config}.
- *
- * <ul class='seealso'>
- *     <li class='link'>{@doc jc.EncodedEntries}
- *     <li class='extlink'>{@source}
- * </ul>
- */
-public interface ConfigEncoder {
-
-       /**
-        * Encode a string.
-        *
-        * @param fieldName The field name being encoded.
-        * @param in The unencoded input string.
-        * @return The encoded output string.
-        */
-       public String encode(String fieldName, String in);
-
-       /**
-        * Decode a string.
-        *
-        * @param fieldName The field name being decoded.
-        * @param in The encoded input string.
-        * @return The decoded output string.
-        */
-       public String decode(String fieldName, String in);
-
-       /**
-        * Returns <jk>true</jk> if the specified string is encoded.
-        *
-        * @param in The input string.
-        * @return
-        *      <jk>true</jk> if the specified string is encoded.
-        *      <br>Returns <jk>false</jk> if the string is <jk>null</jk>.
-        */
-       public boolean isEncoded(String in);
-}
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
index b00eb69..918af7c 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMap.java
@@ -39,8 +39,6 @@ public class ConfigMap implements ConfigStoreListener {
        private volatile String contents;        // The original contents of 
this object.
        final String name;                       // The name  of this object.
 
-       private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
-
        // Changes that have been applied since the last load.
        private final List<ConfigEvent> changes = 
Collections.synchronizedList(new ConfigEvents());
 
@@ -371,8 +369,6 @@ public class ConfigMap implements ConfigStoreListener {
        public ConfigMap setEntry(String section, String key, String value, 
String modifiers, String comment, List<String> preLines) {
                checkSectionName(section);
                checkKeyName(key);
-               if (modifiers != null && ! MOD_CHARS.containsOnly(modifiers))
-                       throw new ConfigException("Invalid modifiers: {0}", 
modifiers);
                return applyChange(true, ConfigEvent.setEntry(name, section, 
key, value, modifiers, comment, preLines));
        }
 
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMapEntry.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMapEntry.java
index 32003e7..daf08d1 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMapEntry.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/internal/ConfigMapEntry.java
@@ -37,20 +37,17 @@ public class ConfigMapEntry {
 
        static final ConfigMapEntry NULL = new ConfigMapEntry(null, null, null, 
null, null);
 
-       private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
+//     private final static AsciiSet MOD_CHARS = AsciiSet.create("#$%&*+^@~");
 
        ConfigMapEntry(String line, List<String> preLines) {
                this.rawLine = line;
                int i = line.indexOf('=');
                String key = line.substring(0, i).trim();
 
-               int modIndex = key.length();
-               for (int j = key.length()-1; j > 0; j--)
-                       if (MOD_CHARS.contains(key.charAt(j)))
-                               modIndex--;
+               int m1 = key.indexOf('<'), m2 = key.indexOf('>');
+               modifiers = nullIfEmpty((m1 > -1 && m2 > m1) ? 
key.substring(m1+1, m2) : null);
 
-               this.modifiers = key.substring(modIndex);
-               this.key = key.substring(0, modIndex);
+               this.key = m1 == -1 ? key : key.substring(0, m1);
 
                line = line.substring(i+1);
 
@@ -117,19 +114,9 @@ public class ConfigMapEntry {
        }
 
        /**
-        * Returns whether this entry has the specified modifier.
-        *
-        * @param m The modifier character.
-        * @return <jk>true</jk> if this entry is encoded.
-        */
-       public boolean hasModifier(char m) {
-               return modifiers.indexOf(m) != -1;
-       }
-
-       /**
         * Returns the modifiers for this entry.
         *
-        * @return The modifiers for this entry, or an empty string if it has 
no modifiers.
+        * @return The modifiers for this entry, or <jk>null</jk> if it has no 
modifiers.
         */
        public String getModifiers() {
                return modifiers;
@@ -152,7 +139,7 @@ public class ConfigMapEntry {
                } else {
                        w.append(key);
                        if (modifiers != null)
-                               w.append(modifiers);
+                               w.append('<').append(new 
String(modifiers)).append('>');
                        w.append(" = ");
 
                        String val = value;
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/Mod.java 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/Mod.java
new file mode 100644
index 0000000..3374416
--- /dev/null
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/Mod.java
@@ -0,0 +1,122 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.config.mod;
+
+import java.util.function.*;
+
+/**
+ * Specifies an entry modifier that is used to encode during write and decode 
during read of config entries.
+ */
+public class Mod {
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Static
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /** A no-op modifier. */
+       public static final Mod NO_OP = new Mod(' ', x -> x, x -> x, x -> true);
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Instance
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       private final char id;
+       private final Function<String,String> removeFunction, applyFunction;
+       private final Function<String,Boolean> detectFunction;
+
+       /**
+        * Constructor.
+        *
+        * @param id The character identifier.
+        * @param applyFunction
+        *      The function to apply when writing an entry.
+        *      Can be <jk>null</jk> if you override the {@link #apply(String)} 
method.
+        * @param removeFunction
+        *      The function to apply when reading an entry.
+        *      Can be <jk>null</jk> if you override the {@link 
#remove(String)} method.
+        * @param detectFunction
+        *      The function to apply to detect whether the modification has 
been made.
+        *      Can be <jk>null</jk> if you override the {@link 
#isApplied(String)} method.
+        */
+       public Mod(char id, Function<String,String> applyFunction, 
Function<String,String> removeFunction, Function<String,Boolean> 
detectFunction) {
+               this.id = id;
+               this.applyFunction = applyFunction;
+               this.removeFunction = removeFunction;
+               this.detectFunction = detectFunction;
+       }
+
+       /**
+        * Returns the modifier identifier character.
+        *
+        * @return The modifier identifier character.
+        */
+       public char getId() {
+               return id;
+       }
+
+       /**
+        * Detects whether this modification has been applied.
+        *
+        * @param value The entry value being tested.  Will never be 
<jk>null</jk>.
+        * @return <jk>true</jk> if the modification has been made to the entry.
+        */
+       public boolean isApplied(String value) {
+               return detectFunction.apply(value);
+       }
+
+       /**
+        * Applies this modification to the specified entry value.
+        *
+        * <p>
+        * Will only be called if {@link #isApplied(String)} returns 
<jk>false</jk>.
+        *
+        * @param value The entry value being written.  Will never be 
<jk>null</jk>.
+        * @return The modified value.
+        */
+       public String apply(String value) {
+               return applyFunction.apply(value);
+       }
+
+       /**
+        * Removes this modification to the specified entry value.
+        *
+        * <p>
+        * Will only be called if {@link #isApplied(String)} returns 
<jk>true</jk>.
+        *
+        * @param value The entry value being read.  Will never be 
<jk>null</jk>.
+        * @return The unmodified value.
+        */
+       public String remove(String value) {
+               return removeFunction.apply(value);
+       }
+
+       /**
+        * Applies this modification to the specified entry value if it isn't 
already applied.
+        *
+        * @param value The entry value being written.  Will never be 
<jk>null</jk>.
+        * @return The modified value.
+        */
+       public final String doApply(String value) {
+               return isApplied(value) ? value : apply(value);
+       }
+
+       /**
+        * Removes this modification from the specified entry value if it is 
applied.
+        *
+        * @param value The entry value being written.  Will never be 
<jk>null</jk>.
+        * @return The modified value.
+        */
+       public final String doRemove(String value) {
+               return isApplied(value) ? remove(value) : value;
+       }
+}
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/XorEncodeMod.java
similarity index 69%
rename from 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
rename to 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/XorEncodeMod.java
index bc0d8a8..b586d51 100644
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/ConfigXorEncoder.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/XorEncodeMod.java
@@ -10,7 +10,7 @@
 // * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
 // * specific language governing permissions and limitations under the 
License.                                              *
 // 
***************************************************************************************************************************
-package org.apache.juneau.config.encode;
+package org.apache.juneau.config.mod;
 
 import static org.apache.juneau.internal.StringUtils.*;
 
@@ -27,39 +27,45 @@ import static org.apache.juneau.internal.IOUtils.*;
  *     <li class='extlink'>{@source}
  * </ul>
  */
-public final class ConfigXorEncoder implements ConfigEncoder {
+public class XorEncodeMod extends Mod {
 
        /** Reusable XOR-ConfigEncoder instance. */
-       public static final ConfigXorEncoder INSTANCE = new ConfigXorEncoder();
+       public static final XorEncodeMod INSTANCE = new XorEncodeMod();
 
-       private static final String key = 
System.getProperty("org.apache.juneau.config.XorEncoder.key",
+       private static final String KEY = 
System.getProperty("org.apache.juneau.config.XorEncoder.key",
                "nuy7og796Vh6G9O6bG230SHK0cc8QYkH");    // The 
super-duper-secret key
 
-       @Override /* ConfigEncoder */
-       public String encode(String fieldName, String in) {
-               byte[] b = in.getBytes(UTF8);
+       /**
+        * Constructor.
+        */
+       public XorEncodeMod() {
+               super('*', null, null, null);
+       }
+
+       @Override
+       public String apply(String value) {
+               byte[] b = value.getBytes(UTF8);
                for (int i = 0; i < b.length; i++) {
-                               int j = i % key.length();
-                       b[i] = (byte)(b[i] ^ key.charAt(j));
+                               int j = i % KEY.length();
+                       b[i] = (byte)(b[i] ^ KEY.charAt(j));
                }
-               return '{' + base64Encode(b) + '}';
+               return "{" + base64Encode(b) + "}";
        }
 
-       @Override /* ConfigEncoder */
-       public String decode(String fieldName, String in) {
-               if (! isEncoded(in))
-                       return in;
-               in = in.substring(1, in.length()-1);
-               byte[] b = base64Decode(in);
+       @Override
+       public String remove(String value) {
+               value = value.trim();
+               value = value.substring(1, value.length()-1);
+               byte[] b = base64Decode(value);
                for (int i = 0; i < b.length; i++) {
-                       int j = i % key.length();
-                       b[i] = (byte)(b[i] ^ key.charAt(j));
-       }
+                       int j = i % KEY.length();
+                       b[i] = (byte)(b[i] ^ KEY.charAt(j));
+               }
                return new String(b, UTF8);
        }
 
-       @Override /* ConfigEncoder */
-       public boolean isEncoded(String in) {
-               return in != null && in.length() > 1 && in.charAt(0) == '{' && 
in.charAt(in.length()-1) == '}';
+       @Override
+       public boolean isApplied(String value) {
+               return startsWith(value, '{') && endsWith(value, '}');
        }
 }
diff --git 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/package-info.java
 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/package-info.java
similarity index 96%
rename from 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/package-info.java
rename to 
juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/package-info.java
index bc9ae20..f5853e6 100755
--- 
a/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/encode/package-info.java
+++ 
b/juneau-core/juneau-config/src/main/java/org/apache/juneau/config/mod/package-info.java
@@ -14,5 +14,5 @@
 /**
  * Config Encoding Support
  */
-package org.apache.juneau.config.encode;
+package org.apache.juneau.config.mod;
 
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index 8a0b850..cc758e0 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -13385,17 +13385,17 @@
        <ck>password*</ck> = <cv>{AwwJVhwUQFZEMg==}</cv>
        </p>
        <p>
-               The default encoder is {@link 
org.apache.juneau.config.encode.ConfigXorEncoder} which is a simple XOR+Base64 
encoder.
+               The default encoder is {@link 
org.apache.juneau.config.mod.ConfigXorEncoder} which is a simple XOR+Base64 
encoder.
        </p>
        <p>
-               Custom encoders can be used to provide your own encoding 
support by implementing the {@link 
org.apache.juneau.config.encode.ConfigEncoder} interface.
+               Custom encoders can be used to provide your own encoding 
support by implementing the {@link org.apache.juneau.config.mod.ConfigEncoder} 
interface.
        </p>
        <ul class='javatree'>
-               <li class='jic'>{@link 
org.apache.juneau.config.encode.ConfigEncoder}
+               <li class='jic'>{@link 
org.apache.juneau.config.mod.ConfigEncoder}
                <ul>
-                       <li class='jm'>{@link 
org.apache.juneau.config.encode.ConfigEncoder#encode(String,String) 
encode(String,String)}
-                       <li class='jm'>{@link 
org.apache.juneau.config.encode.ConfigEncoder#decode(String,String) 
decode(String,String)}
-                       <li class='jm'>{@link 
org.apache.juneau.config.encode.ConfigEncoder#isEncoded(String) 
isEncoded(String)}
+                       <li class='jm'>{@link 
org.apache.juneau.config.mod.ConfigEncoder#encode(String,String) 
encode(String,String)}
+                       <li class='jm'>{@link 
org.apache.juneau.config.mod.ConfigEncoder#decode(String,String) 
decode(String,String)}
+                       <li class='jm'>{@link 
org.apache.juneau.config.mod.ConfigEncoder#isEncoded(String) isEncoded(String)}
                </ul>
        </ul>
        <p>
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
index a383404..9114ae3 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapListenerTest.java
@@ -137,7 +137,7 @@ public class ConfigMapListenerTest {
                assertNull(l.error);
                cm.unregister(l);
 
-               assertString(cm).replaceAll("\\r?\\n", "|").is("#k|k^* = kb # 
C|[S1]|#k1|k1^* = k1b # C1|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("#k|k<^*> = kb # 
C|[S1]|#k1|k1<^*> = k1b # C1|");
        }
 
        @Test
@@ -169,7 +169,7 @@ public class ConfigMapListenerTest {
                assertNull(l.error);
                cm.unregister(l);
 
-               assertString(cm).replaceAll("\\r?\\n", "|").is("#kb|k^* = kb # 
Cb|#S1|[S1]|#k1b|k1^* = k1b # Cb1|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("#kb|k<^*> = kb 
# Cb|#S1|[S1]|#k1b|k1<^*> = k1b # Cb1|");
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapTest.java 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapTest.java
index 06989b7..d98f2e1 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigMapTest.java
@@ -973,10 +973,10 @@ public class ConfigMapTest {
                assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|k1 = v2|");
 
                cm.setEntry("S1", "k1", "v3", ENCODED, "c3", 
Arrays.asList("#k1a"));
-               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1a|k1* = 
v3 # c3|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1a|k1<*> 
= v3 # c3|");
 
                cm.setEntry("S1", "k1", "v4", BASE64, "c4", 
Arrays.asList("#k1b"));
-               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1b|k1^ = 
v4 # c4|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1b|k1<^> 
= v4 # c4|");
        }
 
        @Test
@@ -992,10 +992,10 @@ public class ConfigMapTest {
                assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1|k1 = 
v2 # comment|");
 
                cm.setEntry("S1", "k1", "v3", ENCODED, "c3", 
Arrays.asList("#k1a"));
-               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1a|k1* = 
v3 # c3|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1a|k1<*> 
= v3 # c3|");
 
                cm.setEntry("S1", "k1", "v4", BASE64, "c4", 
Arrays.asList("#k1b"));
-               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1b|k1^ = 
v4 # c4|");
+               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|#k1b|k1<^> 
= v4 # c4|");
        }
 
        @Test
@@ -1096,22 +1096,19 @@ public class ConfigMapTest {
        public void testModifiers() throws Exception {
                ConfigStore s = initStore("Foo.cfg",
                        "[S1]",
-                       "k1^ = v1",
-                       "k2* = v2",
-                       "k3*^ = v3"
+                       "k1<^> = v1",
+                       "k2<*> = v2",
+                       "k3<*^> = v3"
                );
                ConfigMap cm = s.getMap("Foo.cfg");
 
-               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|k1^ = 
v1|k2* = v2|k3*^ = v3|");
-               assertTrue(cm.getEntry("S1", "k1").hasModifier('^'));
-               assertFalse(cm.getEntry("S1", "k1").hasModifier('*'));
-               assertFalse(cm.getEntry("S1", "k2").hasModifier('^'));
-               assertTrue(cm.getEntry("S1", "k2").hasModifier('*'));
-               assertTrue(cm.getEntry("S1", "k3").hasModifier('^'));
-               assertTrue(cm.getEntry("S1", "k3").hasModifier('*'));
+               assertString(cm).replaceAll("\\r?\\n", "|").is("[S1]|k1<^> = 
v1|k2<*> = v2|k3<*^> = v3|");
+               assertEquals("^", cm.getEntry("S1", "k1").getModifiers());
+               assertEquals("*", cm.getEntry("S1", "k2").getModifiers());
+               assertEquals("*^", cm.getEntry("S1", "k3").getModifiers());
 
                cm.setEntry("S1", "k1", "v1", "#$%&*+^@~", null, null);
-               assertString(cm).replaceAll("\\r?\\n", 
"|").is("[S1]|k1#$%&*+^@~ = v1|k2* = v2|k3*^ = v3|");
+               assertString(cm).replaceAll("\\r?\\n", 
"|").is("[S1]|k1<#$%&*+^@~> = v1|k2<*> = v2|k3<*^> = v3|");
        }
 
        @Test
@@ -1126,9 +1123,6 @@ public class ConfigMapTest {
 
                // This is okay.
                cm.setEntry("S1", "k1", "v1", "", null, null);
-
-               assertThrown(()->cm.setEntry("S1", "k1", "v1", "X", null, 
null)).message().is("Invalid modifiers: X");
-               assertThrown(()->cm.setEntry("S1", "k1", "v1", " ", null, 
null)).message().is("Invalid modifiers:  ");
        }
 
        private static ConfigStore initStore(String name, String...contents) {
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java 
b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
index 24e5c8f..6daebbb 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
@@ -13,7 +13,6 @@
 package org.apache.juneau.config;
 
 import static org.apache.juneau.assertions.Assertions.*;
-import static org.apache.juneau.config.ConfigMod.*;
 import static org.junit.Assert.*;
 import static org.apache.juneau.testutils.StreamUtils.*;
 
@@ -24,8 +23,8 @@ import java.util.concurrent.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.collections.*;
-import org.apache.juneau.config.encode.*;
 import org.apache.juneau.config.event.*;
+import org.apache.juneau.config.mod.*;
 import org.apache.juneau.config.store.*;
 import org.apache.juneau.svl.*;
 import org.apache.juneau.uon.*;
@@ -172,18 +171,18 @@ public class ConfigTest {
                Config c = init("a1=1", "[S]", "b1=1");
 
                ABean b = new ABean().init();
-               c.set("a1", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-               c.set("a2", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-               c.set("a3", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-               c.set("S/b1", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-               c.set("S/b2", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-               c.set("T/c1", b, UonSerializer.DEFAULT, ENCODED, "comment", 
Arrays.asList("#c1","#c2"));
-
-               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1* = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = 
{RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* 
= {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|");
+               c.set("a1", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+               c.set("a2", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+               c.set("a3", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+               c.set("S/b1", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+               c.set("S/b2", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+               c.set("T/c1", b, UonSerializer.DEFAULT, "*", "comment", 
Arrays.asList("#c1","#c2"));
+
+               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1<*> = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> 
= {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # 
comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = 
{RhMWWFIFVksf} # comment|");
                c.commit();
-               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1* = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = 
{RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* 
= {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|");
+               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1<*> = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> 
= {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # 
comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = 
{RhMWWFIFVksf} # comment|");
                c = cb.build();
-               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1* = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2* = {RhMWWFIFVksf} # comment|#c1|#c2|a3* = 
{RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1* = {RhMWWFIFVksf} # comment|#c1|#c2|b2* 
= {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1* = {RhMWWFIFVksf} # comment|");
+               assertString(c).replaceAll("\\r?\\n", "|").is("#c1|#c2|a1<*> = 
{RhMWWFIFVksf} # comment|#c1|#c2|a2<*> = {RhMWWFIFVksf} # comment|#c1|#c2|a3<*> 
= {RhMWWFIFVksf} # comment|[S]|#c1|#c2|b1<*> = {RhMWWFIFVksf} # 
comment|#c1|#c2|b2<*> = {RhMWWFIFVksf} # comment|[T]|#c1|#c2|c1<*> = 
{RhMWWFIFVksf} # comment|");
 
                assertEquals("(foo=bar)", c.get("a1").get());
                assertEquals("(foo=bar)", c.get("a2").get());
@@ -815,18 +814,18 @@ public class ConfigTest {
                BBean b = new BBean().init();
 
                Config c = init("foo=qux", "[S]", "foo=baz", "bar=baz");
-               c.writeProperties("S", a, true);
+               c.getSection("S").writeToBean(a, true);
                assertObject(a).asJson().is("{foo:'baz'}");
-               c.writeProperties("S", b, true);
+               c.getSection("S").writeToBean(b, true);
                assertObject(b).asJson().is("{foo:'baz'}");
-               assertThrown(()->c.writeProperties("S", a, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'S'.");
-               assertThrown(()->c.writeProperties("S", b, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'S'.");
-               c.writeProperties("", b, true);
+               assertThrown(()->c.getSection("S").writeToBean(a, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'S'.");
+               assertThrown(()->c.getSection("S").writeToBean(b, 
false)).message().is("Unknown property 'bar' encountered in configuration 
section 'S'.");
+               c.getSection("").writeToBean(b, true);
                assertObject(b).asJson().is("{foo:'qux'}");
-               c.writeProperties("", a, true);
+               c.getSection("").writeToBean(a, true);
+               assertObject(a).asJson().is("{foo:'qux'}");
+               c.getSection(null).writeToBean(a, true);
                assertObject(a).asJson().is("{foo:'qux'}");
-
-               assertThrown(()->c.writeProperties(null, a, 
true)).message().is("Argument 'section' cannot be null.");
        }
 
        
//====================================================================================================
@@ -1087,7 +1086,7 @@ public class ConfigTest {
        @Test
        public void testEncodedValues() throws Exception {
                Config cf = init(
-                       "[s1]", "", "foo* = "
+                       "[s1]", "", "foo<*> = "
                );
 
                cf.set("s1/foo", "mypassword");
@@ -1096,11 +1095,11 @@ public class ConfigTest {
 
                cf.commit();
 
-               assertString(cf).replaceAll("\\r?\\n", "|").is("[s1]||foo* = 
{AwwJVhwUQFZEMg==}|");
+               assertString(cf).replaceAll("\\r?\\n", "|").is("[s1]||foo<*> = 
{AwwJVhwUQFZEMg==}|");
 
                assertEquals("mypassword", cf.get("s1/foo").get());
 
-               cf.load(reader("[s1]\nfoo* = mypassword2\n"), true);
+               cf.load(reader("[s1]\nfoo<*> = mypassword2\n"), true);
 
                assertEquals("mypassword2", cf.get("s1/foo").get());
 
@@ -1109,7 +1108,7 @@ public class ConfigTest {
                // INI output should be encoded
                StringWriter sw = new StringWriter();
                cf.writeTo(new PrintWriter(sw));
-               assertString(sw).replaceAll("\\r?\\n", "|").is("[s1]|foo* = 
{AwwJVhwUQFZEMg==}|");
+               assertString(sw).replaceAll("\\r?\\n", "|").is("[s1]|foo<*> = 
{AwwJVhwUQFZEMg==}|");
        }
 
        
//====================================================================================================
@@ -1118,12 +1117,12 @@ public class ConfigTest {
        @Test
        public void testEncodeEntries() throws Exception {
                Config cf = init(
-                       "[s1]", "", "foo* = mypassword"
+                       "[s1]", "", "foo<*> = mypassword"
                );
 
-               cf.encodeEntries();
+               cf.applyMods();
                cf.commit();
-               
assertString(ConfigMemoryStore.DEFAULT.read("Test.cfg")).replaceAll("\\r?\\n", 
"|").is("[s1]||foo* = {AwwJVhwUQFZEMg==}|");
+               
assertString(ConfigMemoryStore.DEFAULT.read("Test.cfg")).replaceAll("\\r?\\n", 
"|").is("[s1]||foo<*> = {AwwJVhwUQFZEMg==}|");
        }
 
        
//====================================================================================================
@@ -1180,9 +1179,9 @@ public class ConfigTest {
        }
 
        private void testXor(String in) {
-               ConfigXorEncoder e = new ConfigXorEncoder();
-               String s = e.encode("", in);
-               String s2 = e.decode("", s);
+               XorEncodeMod e = new XorEncodeMod();
+               String s = e.apply(in);
+               String s2 = e.remove(s);
                assertEquals(in, s2);
        }
 
@@ -1308,8 +1307,8 @@ public class ConfigTest {
 
                // Encoded
                changes.clear();
-               cf.set("a4", "4", null, ConfigMod.ENCODED, null, null);
-               cf.set("B/b4", "4", null, ConfigMod.ENCODED, null, null);
+               cf.set("a4", "4", null, "*", null, null);
+               cf.set("B/b4", "4", null, "*", null, null);
                cf.commit();
                
assertObject(changes).asJson().is("['a4={Wg==}','B/b4={Wg==}']");
 
@@ -1542,7 +1541,7 @@ public class ConfigTest {
                assertEquals("a,#b,=c", cf.get("a").get());
                assertEquals("a,#b,=c", cf.get("A/a").get());
 
-               cf.set("a", "a,#b,=c", null, (ConfigMod)null, 
"comment#comment", null);
+               cf.set("a", "a,#b,=c", null, null, "comment#comment", null);
                assertString(cf).replaceAll("\\r?\\n", "|").is("a = 
a,\\u0023b,=c # comment#comment|[A]|a = a,\\u0023b,=c|");
                assertEquals("a,#b,=c", cf.get("a").get());
        }

Reply via email to