Repository: incubator-juneau
Updated Branches:
  refs/heads/master 4eb151d1a -> 257776b98


Fix parse exceptions on partial ISO8601 timestamps.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/257776b9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/257776b9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/257776b9

Branch: refs/heads/master
Commit: 257776b98d04ece5b45efbaa04b33d6b368eea5f
Parents: 4eb151d
Author: JamesBognar <[email protected]>
Authored: Mon Jun 12 15:33:44 2017 -0400
Committer: JamesBognar <[email protected]>
Committed: Mon Jun 12 15:33:44 2017 -0400

----------------------------------------------------------------------
 .../juneau/ini/ConfigFileInterfaceTest.java     |   4 +-
 .../java/org/apache/juneau/ini/ConfigFile.java  | 171 +++++++++++++++++--
 .../apache/juneau/ini/ConfigFileContext.java    |  60 +++++++
 .../org/apache/juneau/ini/ConfigFileImpl.java   |  48 ++++--
 .../apache/juneau/ini/ConfigFileWrapped.java    |  12 +-
 .../org/apache/juneau/internal/ArrayUtils.java  |  21 +++
 .../org/apache/juneau/internal/DateUtils.java   |  60 +++++++
 .../org/apache/juneau/internal/StringUtils.java |  11 +-
 .../apache/juneau/transforms/CalendarSwap.java  |   5 +-
 .../org/apache/juneau/transforms/DateSwap.java  |   5 +-
 juneau-core/src/main/javadoc/overview.html      |   7 +
 11 files changed, 361 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
index 4d34568..7bc4163 100644
--- 
a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
@@ -182,7 +182,7 @@ public class ConfigFileInterfaceTest {
        public void testBeanList() throws Exception {
                proxy.setBeanList(Arrays.asList(new ABean().init()));
                assertObjectEquals("[{a:1,b:'foo'}]", proxy.getBeanList());
-               assertEquals("[{a:1,b:'foo'}]", cf.get("A", "beanList"));
+               assertEquals("\n[\n\t{a:1,b:'foo'}\n]", cf.get("A", 
"beanList"));
                assertType(ABean.class, proxy.getBeanList().get(0));
        }
 
@@ -248,7 +248,7 @@ public class ConfigFileInterfaceTest {
        public void testTypedBeanList() throws Exception {
                proxy.setTypedBeanList(Arrays.asList((TypedBean)new 
TypedBeanImpl().init()));
                assertObjectEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", 
proxy.getTypedBeanList());
-               assertEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", 
cf.get("A", "typedBeanList"));
+               assertEquals("\n[\n\t{_type:'TypedBeanImpl',a:1,b:'foo'}\n]", 
cf.get("A", "typedBeanList"));
                assertType(TypedBeanImpl.class, 
proxy.getTypedBeanList().get(0));
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
index d2a7e6a..172d3d9 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
@@ -57,15 +57,17 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @param sectionName The section name.  Must not be <jk>null</jk>.
         * @param sectionKey The section key.  Must not be <jk>null</jk>.
         * @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 encoded
         * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
         * @throws SerializeException If the object value could not be 
converted to a JSON string for some reason.
         * @throws UnsupportedOperationException If config file is read only.
         */
-       public abstract String put(String sectionName, String sectionKey, 
Object value, boolean encoded) throws SerializeException;
+       public abstract String put(String sectionName, String sectionKey, 
Object value, Serializer serializer, boolean encoded) throws SerializeException;
 
        /**
-        * Identical to {@link #put(String, String, Object, boolean)} except 
used when the value is a simple string
+        * Identical to {@link #put(String, String, Object, Serializer, 
boolean)} except used when the value is a simple string
         * to avoid having to catch a {@link SerializeException}.
         *
         * @param sectionName The section name.  Must not be <jk>null</jk>.
@@ -192,21 +194,25 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * </ul>
         *
         * @param o The object to serialize.
+        * @param serializer The serializer to use for serializing the object.
+        *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
         * @return The serialized object.
         * @throws SerializeException
         */
-       protected abstract String serialize(Object o) throws SerializeException;
+       protected abstract String serialize(Object o, Serializer serializer) 
throws SerializeException;
 
        /**
         * Converts the specified string to an object of the specified type.
         *
         * @param s The string to parse.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
         * @param type The data type to create.
         * @param args The generic type arguments if the type is a {@link 
Collection} or {@link Map}
         * @return The parsed object.
         * @throws ParseException
         */
-       protected abstract <T> T parse(String s, Type type, Type...args) throws 
ParseException;
+       protected abstract <T> T parse(String s, Parser parser, Type type, 
Type...args) throws ParseException;
 
        /**
         * Places a read lock on this config file.
@@ -301,9 +307,28 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
        public final <T> T getObject(String key, Type type, Type...args) throws 
ParseException {
+               return getObject(key, (Parser)null, type, args);
+       }
+
+       /**
+        * Same as {@link #getObject(String, Type, Type...)} but allows you to 
specify the parser to use to parse the value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObject(String key, Parser parser, Type type, 
Type...args) throws ParseException {
                assertFieldNotNull(key, "key");
                assertFieldNotNull(type, "type");
-               return parse(getString(key), type, args);
+               return parse(getString(key), parser, type, args);
        }
 
        /**
@@ -338,12 +363,27 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for 
maps and collections.
         */
        public final <T> T getObject(String key, Class<T> type) throws 
ParseException {
+               return getObject(key, (Parser)null, type);
+       }
+
+       /**
+        * Same as {@link #getObject(String, Class)} but allows you to specify 
the parser to use to parse the value.
+        *
+        * @param <T> The class type of the object being created.
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
+        * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for 
maps and collections.
+        */
+       public final <T> T getObject(String key, Parser parser, Class<T> type) 
throws ParseException {
                assertFieldNotNull(key, "key");
                assertFieldNotNull(type, "c");
-               return parse(getString(key), type);
+               return parse(getString(key), parser, type);
        }
 
-
        /**
         * Gets the entry with the specified key and converts it to the 
specified value.
         * <p>
@@ -357,9 +397,25 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
        public final <T> T getObjectWithDefault(String key, T def, Class<T> 
type) throws ParseException {
+               return getObjectWithDefault(key, null, def, type);
+       }
+
+       /**
+        * Same as {@link #getObjectWithDefault(String, Object, Class)} but 
allows you to specify the parser to use to parse the value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param def The default value if section or key does not exist.
+        * @param type The class to convert the value to.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObjectWithDefault(String key, Parser parser, T 
def, Class<T> type) throws ParseException {
                assertFieldNotNull(key, "key");
                assertFieldNotNull(type, "c");
-               T t = parse(getString(key), type);
+               T t = parse(getString(key), parser, type);
                return (t == null ? def : t);
        }
 
@@ -380,9 +436,29 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
        public final <T> T getObjectWithDefault(String key, T def, Type type, 
Type...args) throws ParseException {
+               return getObjectWithDefault(key, null, def, type, args);
+       }
+
+       /**
+        * Same as {@link #getObjectWithDefault(String, Object, Type, Type...)} 
but allows you to specify the parser to use to parse the value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param def The default value if section or key does not exist.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObjectWithDefault(String key, Parser parser, T 
def, Type type, Type...args) throws ParseException {
                assertFieldNotNull(key, "key");
                assertFieldNotNull(type, "type");
-               T t = parse(getString(key), type, args);
+               T t = parse(getString(key), parser, type, args);
                return (t == null ? def : t);
        }
 
@@ -399,9 +475,25 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @return The value, or the default value if the section or value 
doesn't exist.
         */
        public final <T> T getObject(String sectionName, String sectionKey, 
Class<T> c) throws ParseException {
+               return getObject(sectionName, sectionKey, null, c);
+       }
+
+       /**
+        * Same as {@link #getObject(String, String, Class)} but allows you to 
specify the parser to use to parse the value.
+        *
+        * @param sectionName The section name.  Must not be <jk>null</jk>.
+        * @param sectionKey The section key.  Must not be <jk>null</jk>.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param c The class to convert the value to.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or the default value if the section or value 
doesn't exist.
+        */
+       public final <T> T getObject(String sectionName, String sectionKey, 
Parser parser, Class<T> c) throws ParseException {
                assertFieldNotNull(sectionName, "sectionName");
                assertFieldNotNull(sectionKey, "sectionKey");
-               return parse(get(sectionName, sectionKey), c);
+               return parse(get(sectionName, sectionKey), parser, c);
        }
 
        /**
@@ -421,9 +513,29 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @return The value, or <jk>null</jk> if the section or key does not 
exist.
         */
        public final <T> T getObject(String sectionName, String sectionKey, 
Type type, Type...args) throws ParseException {
+               return getObject(sectionName, sectionKey, null, type, args);
+       }
+
+       /**
+        * Same as {@link #getObject(String, String, Type, Type...)} but allows 
you to specify the parser to use to parse the value.
+        *
+        * @param sectionName The section name.  Must not be <jk>null</jk>.
+        * @param sectionKey The section key.  Must not be <jk>null</jk>.
+        * @param parser The parser to use for parsing the object.
+        *      If <jk>null</jk>, then uses the predefined parser on the config 
file.
+        * @param type The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args The type arguments of the class if it's a collection or 
map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        *
+        * @throws ParseException If parser could not parse the value or if a 
parser is not registered with this config file.
+        * @return The value, or <jk>null</jk> if the section or key does not 
exist.
+        */
+       public final <T> T getObject(String sectionName, String sectionKey, 
Parser parser, Type type, Type...args) throws ParseException {
                assertFieldNotNull(sectionName, "sectionName");
                assertFieldNotNull(sectionKey, "sectionKey");
-               return parse(get(sectionName, sectionKey), type, args);
+               return parse(get(sectionName, sectionKey), parser, type, args);
        }
 
        /**
@@ -533,7 +645,22 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @throws UnsupportedOperationException If config file is read only.
         */
        public final String put(String key, Object value) throws 
SerializeException {
-               return put(key, value, isEncoded(key));
+               return put(key, value, null, isEncoded(key));
+       }
+
+       /**
+        * Same as {@link #put(String, Object)} but allows you to specify the 
serializer to use to serialize the value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of the key.
+        * @param value The new value POJO.
+        * @param serializer The serializer to use for serializing the object.
+        *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
+        * @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 config file is read only.
+        */
+       public final String put(String key, Object value, Serializer 
serializer) throws SerializeException {
+               return put(key, value, serializer, isEncoded(key));
        }
 
        /**
@@ -556,8 +683,24 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @throws UnsupportedOperationException If config file is read only.
         */
        public final String put(String key, Object value, boolean encoded) 
throws SerializeException {
+               return put(key, value, null, encoded);
+       }
+
+       /**
+        * Same as {@link #put(String, Object, boolean)} but allows you to 
specify the serializer to use to serialize the value.
+        *
+        * @param key The key.  See {@link #getString(String)} for a 
description of 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 encoded If <jk>true</jk>, value is encoded by the registered 
encoder when the config file is persisted to disk.
+        * @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 config file is read only.
+        */
+       public final String put(String key, Object value, Serializer 
serializer, boolean encoded) throws SerializeException {
                assertFieldNotNull(key, "key");
-               return put(getSectionName(key), getSectionKey(key), 
serialize(value), encoded);
+               return put(getSectionName(key), getSectionKey(key), 
serialize(value, serializer), encoded);
        }
 
        /**
@@ -770,7 +913,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
                                        if (method.equals(rm))
                                                return 
ConfigFile.this.getObject(sectionName, pd.getName(), rm.getGenericReturnType());
                                        if (method.equals(wm))
-                                               return 
ConfigFile.this.put(sectionName, pd.getName(), args[0], false);
+                                               return 
ConfigFile.this.put(sectionName, pd.getName(), args[0], null, false);
                                }
                                throw new 
UnsupportedOperationException("Unsupported interface method.  method=[ " + 
method + " ]");
                        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileContext.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileContext.java
new file mode 100644
index 0000000..593a7e9
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileContext.java
@@ -0,0 +1,60 @@
+// 
***************************************************************************************************************************
+// * 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.ini;
+
+import org.apache.juneau.*;
+
+/**
+ * TODO
+ */
+public class ConfigFileContext extends Context {
+
+       /**
+        * TODO
+        *
+        * @param propertyStore
+        */
+       public ConfigFileContext(PropertyStore propertyStore) {
+               super(propertyStore);
+       }
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_serializer = 
"ConfigFile.serializer";
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_parser = "ConfigFile.parser";
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_encoder = "ConfigFile.encoder";
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_readonly = "ConfigFile.readonly";
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_createIfNotExists = 
"ConfigFile.createIfNotExists";
+
+       /**
+        * TODO
+        */
+       public static final String CONFIGFILE_wsDepth = "ConfigFile.wsDepth";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
index 0e08db2..44353f3 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
@@ -197,22 +197,46 @@ public final class ConfigFileImpl extends ConfigFile {
                return this;
        }
 
+       @SuppressWarnings("hiding")
        @Override /* ConfigFile */
-       protected String serialize(Object value) throws SerializeException {
+       protected String serialize(Object value, Serializer serializer) throws 
SerializeException {
                if (value == null)
                        return "";
+               if (serializer == null)
+                       serializer = this.serializer;
                Class<?> c = value.getClass();
                if (isSimpleType(c))
                        return value.toString();
-               String s = serializer.toString(value);
-               if (s.startsWith("'"))
-                       return s.substring(1, s.length()-1);
-               return s;
+
+               BeanContext bc = serializer.getBeanContext();
+               ClassMeta<?> cm = bc.getClassMetaForObject(value);
+               String r = null;
+
+               // For arrays of bean/collections, we have special formatting.
+               if (cm.isCollectionOrArray()) {
+                       Object v1 = ArrayUtils.getFirst(value);
+                       if (bc.getClassMetaForObject(v1).isMapOrBean()) {
+                               List<Object> l = new ArrayList<Object>();
+                               if (cm.isCollection()) {
+                                       for (Object o : (Collection<?>)value)
+                                               l.add(serializer.serialize(o));
+                               } else {
+                                       for (int i = 0; i < 
Array.getLength(value); i++)
+                                               
l.add(serializer.serialize(Array.get(value, i)));
+                               }
+                               return "\n[\n\t" + StringUtils.join(l, ",\n\t") 
+ "\n]";
+                       }
+               }
+
+               r = (String)serializer.serialize(value);
+               if (r.startsWith("'"))
+                       return r.substring(1, r.length()-1);
+               return r;
        }
 
        @Override /* ConfigFile */
-       @SuppressWarnings({ "unchecked" })
-       protected <T> T parse(String s, Type type, Type...args) throws 
ParseException {
+       @SuppressWarnings({ "unchecked", "hiding" })
+       protected <T> T parse(String s, Parser parser, Type type, Type...args) 
throws ParseException {
 
                if (StringUtils.isEmpty(s))
                        return null;
@@ -220,10 +244,13 @@ public final class ConfigFileImpl extends ConfigFile {
                if (isSimpleType(type))
                        return (T)pBeanSession.convertToType(s, (Class<?>)type);
 
-               char s1 = charAt(s, 0);
+               char s1 = firstNonWhitespaceChar(s);
                if (s1 != '[' && s1 != '{' && ! "null".equals(s))
                        s = '\'' + s + '\'';
 
+               if (parser == null)
+                       parser = this.parser;
+
                return parser.parse(s, type, args);
        }
 
@@ -469,11 +496,12 @@ public final class ConfigFileImpl extends ConfigFile {
                return (s2 == null ? null : s2.toString());
        }
 
+       @SuppressWarnings("hiding")
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) throws SerializeException {
+       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded) throws SerializeException {
                assertFieldNotNull(sectionKey, "sectionKey");
                Section s = getSection(sectionName, true);
-               return s.put(sectionKey, serialize(value), encoded);
+               return s.put(sectionKey, serialize(value, serializer), encoded);
        }
 
        @Override /* ConfigFile */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
index 5407c0f..dd8e2ee 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
@@ -252,8 +252,8 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
boolean encoded) throws SerializeException {
-               return cf.put(sectionName, sectionKey, value, encoded);
+       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded) throws SerializeException {
+               return cf.put(sectionName, sectionKey, value, serializer, 
encoded);
        }
 
        @Override /* ConfigFile */
@@ -277,12 +277,12 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       protected String serialize(Object o) throws SerializeException {
-               return cf.serialize(o);
+       protected String serialize(Object o, Serializer s) throws 
SerializeException {
+               return cf.serialize(o, s);
        }
 
        @Override /* ConfigFile */
-       protected <T> T parse(String s, Type type, Type... args) throws 
ParseException {
-               return cf.parse(s, type, args);
+       protected <T> T parse(String s, Parser parser, Type type, Type... args) 
throws ParseException {
+               return cf.parse(s, parser, type, args);
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
index 3fbaeaa..4f1a7d8 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/ArrayUtils.java
@@ -363,4 +363,25 @@ public final class ArrayUtils {
                        l.add(i2.next());
                return l;
        }
+
+       /**
+        * Returns the first object in the specified collection or array.
+        *
+        * @param val The collection or array object.
+        * @return The first object, or <jk>null</jk> if the collection or 
array is empty or <jk>null</jk> or the value
+        *      isn't a collection or array.
+        */
+       public static Object getFirst(Object val) {
+               if (val != null) {
+                       if (val instanceof Collection) {
+                               Collection<?> c = (Collection<?>)val;
+                               if (c.isEmpty())
+                                       return null;
+                               return c.iterator().next();
+                       }
+                       if (val.getClass().isArray())
+                               return Array.getLength(val) == 0 ? null : 
Array.get(val, 0);
+               }
+               return null;
+       }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/internal/DateUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/internal/DateUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/DateUtils.java
index 48565c4..9ca1b5e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/DateUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/DateUtils.java
@@ -16,6 +16,8 @@ import java.lang.ref.*;
 import java.text.*;
 import java.util.*;
 
+import javax.xml.bind.*;
+
 /**
  * A utility class for parsing and formatting HTTP dates as used in cookies and
  * other headers.  This class handles dates as defined by RFC 2616 section
@@ -183,4 +185,62 @@ public final class DateUtils {
                        THREADLOCAL_FORMATS.remove();
                }
        }
+
+       /**
+        * Pads out an ISO8601 string so that it can be parsed using {@link 
DatatypeConverter#parseDateTime(String)}.
+        * <ul>
+        *      <li><js>"2001-07-04T15:30:45-05:00"</js> --&gt; 
<js>"2001-07-04T15:30:45-05:00"</js>
+        *      <li><js>"2001-07-04T15:30:45Z"</js> --&gt; 
<js>"2001-07-04T15:30:45Z"</js>
+        *      <li><js>"2001-07-04T15:30:45.1Z"</js> --&gt; 
<js>"2001-07-04T15:30:45.1Z"</js>
+        *      <li><js>"2001-07-04T15:30Z"</js> --&gt; 
<js>"2001-07-04T15:30:00Z"</js>
+        *      <li><js>"2001-07-04T15:30"</js> --&gt; 
<js>"2001-07-04T15:30:00"</js>
+        *      <li><js>"2001-07-04"</js> --&gt; 
<li><js>"2001-07-04T00:00:00"</js>
+        *      <li><js>"2001-07"</js> --&gt; <js>"2001-07-01T00:00:00"</js>
+        *      <li><js>"2001"</js> --&gt; <js>"2001-01-01T00:00:00"</js>
+        * </ul>
+        *
+        * @param in The string to pad.
+        * @return The padded string.
+        */
+       public static final String toValidISO8601DT(String in) {
+
+               // "2001-07-04T15:30:45Z"
+               final int
+                       S1 = 1, // Looking for -
+                       S2 = 2, // Found -, looking for -
+                       S3 = 3, // Found -, looking for T
+                       S4 = 4, // Found T, looking for :
+                       S5 = 5, // Found :, looking for :
+                       S6 = 6; // Found :
+
+               int state = 1;
+               for (int i = 0; i < in.length(); i++) {
+                       char c = in.charAt(i);
+                       if (state == S1) {
+                               if (c == '-')
+                                       state = S2;
+                       } else if (state == S2) {
+                               if (c == '-')
+                                       state = S3;
+                       } else if (state == S3) {
+                               if (c == 'T')
+                                       state = S4;
+                       } else if (state == S4) {
+                               if (c == ':')
+                                       state = S5;
+                       } else if (state == S5) {
+                               if (c == ':')
+                                       state = S6;
+                       }
+               }
+
+               switch(state) {
+                       case S1: return in + "-01-01T00:00:00";
+                       case S2: return in + "-01T00:00:00";
+                       case S3: return in + "T00:00:00";
+                       case S4: return in + ":00:00";
+                       case S5: return in + ":00";
+                       default: return in;
+               }
+       }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java 
b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
index 22b444b..3baf951 100644
--- a/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -1319,13 +1319,10 @@ public final class StringUtils {
         * @return The first non-whitespace character, or <code>0</code> if the 
string is <jk>null</jk>, empty, or composed of only whitespace.
         */
        public static char firstNonWhitespaceChar(String s) {
-               if (s != null) {
-                       for (int i = 0; i < s.length(); i++) {
-                               char c = s.charAt(i);
-                               if (! Character.isWhitespace(c))
-                                       return c;
-                       }
-               }
+               if (s != null)
+                       for (int i = 0; i < s.length(); i++)
+                               if (! Character.isWhitespace(s.charAt(i)))
+                                       return s.charAt(i);
                return 0;
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/transforms/CalendarSwap.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/transforms/CalendarSwap.java 
b/juneau-core/src/main/java/org/apache/juneau/transforms/CalendarSwap.java
index 2490c99..9f45498 100644
--- a/juneau-core/src/main/java/org/apache/juneau/transforms/CalendarSwap.java
+++ b/juneau-core/src/main/java/org/apache/juneau/transforms/CalendarSwap.java
@@ -13,6 +13,7 @@
 package org.apache.juneau.transforms;
 
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.internal.DateUtils.*;
 
 import java.text.*;
 import java.util.*;
@@ -133,7 +134,7 @@ public class CalendarSwap extends StringSwap<Calendar> {
                        try {
                                if (isEmpty(o))
                                        return null;
-                               return 
convert(DatatypeConverter.parseDateTime(o), hint);
+                               return 
convert(DatatypeConverter.parseDateTime(toValidISO8601DT(o)), hint);
                        } catch (Exception e) {
                                throw new ParseException(e);
                        }
@@ -164,7 +165,7 @@ public class CalendarSwap extends StringSwap<Calendar> {
                        try {
                                if (isEmpty(o))
                                        return null;
-                               return 
convert(DatatypeConverter.parseDateTime(o), hint);
+                               return 
convert(DatatypeConverter.parseDateTime(toValidISO8601DT(o)), hint);
                        } catch (Exception e) {
                                throw new ParseException(e);
                        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/java/org/apache/juneau/transforms/DateSwap.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/transforms/DateSwap.java 
b/juneau-core/src/main/java/org/apache/juneau/transforms/DateSwap.java
index 642ba1b..b2fb0b0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/transforms/DateSwap.java
+++ b/juneau-core/src/main/java/org/apache/juneau/transforms/DateSwap.java
@@ -12,6 +12,7 @@
 // 
***************************************************************************************************************************
 package org.apache.juneau.transforms;
 
+import static org.apache.juneau.internal.DateUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
 import java.text.*;
@@ -132,7 +133,7 @@ public class DateSwap extends StringSwap<Date> {
                        try {
                                if (isEmpty(o))
                                        return null;
-                               return 
convert(DatatypeConverter.parseDateTime(o).getTime(), hint);
+                               return 
convert(DatatypeConverter.parseDateTime(toValidISO8601DT(o)).getTime(), hint);
                        } catch (Exception e) {
                                throw new ParseException(e);
                        }
@@ -165,7 +166,7 @@ public class DateSwap extends StringSwap<Date> {
                        try {
                                if (isEmpty(o))
                                        return null;
-                               return 
convert(DatatypeConverter.parseDateTime(o).getTime(), hint);
+                               return 
convert(DatatypeConverter.parseDateTime(toValidISO8601DT(o)).getTime(), hint);
                        } catch (Exception e) {
                                throw new ParseException(e);
                        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/257776b9/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html 
b/juneau-core/src/main/javadoc/overview.html
index 458a70f..81a3f33 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6192,12 +6192,19 @@
                        <li>New/modified methods on {@link 
org.apache.juneau.ini.ConfigFile}:
                                <ul>
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#put(String,String,Object,boolean) 
put(String,String,Object,boolean)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#put(String,String,Object,Serializer,boolean) 
put(String,String,Object,boolean)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Type,Type...) 
getObject(String,Type,Type...)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Parser,Type,Type...) 
getObject(String,Parser,Type,Type...)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Class) 
getObject(String,Class)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,Parser,Class) 
getObject(String,Parser,Class)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Type,Type...) 
getObject(String,String,Type,Type...)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Parser,Type,Type...) 
getObject(String,String,Parser,Type,Type...)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Class) 
getObject(String,String,Class)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObject(String,String,Parser,Class) 
getObject(String,String,Parser,Class)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Type,Type...)
 getObjectWithDefault(String,Object,Type,Type)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Parser,Type,Type...)
 getObjectWithDefault(String,Object,Type,Type)} 
                                        <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Class) 
getObjectWithDefault(String,Object,Class)} 
+                                       <li>{@link 
org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Parser,Class)
 getObjectWithDefault(String,Object,Parser,Class)} 
                                </ul>
                        <li>New ability to interact with config file sections 
with proxy interfaces with new method {@link 
org.apache.juneau.ini.ConfigFile#getSectionAsInterface(String,Class)}.
                        <li>{@link org.apache.juneau.annotation.BeanProperty 
@BeanProperty} annotation can now be applied to getters


Reply via email to