From e1f3ce778683a77baea60547b89d0d9ee252fc1e Mon Sep 17 00:00:00 2001
From: Alexey Pomogaev <fororer@gmail.com>
Date: Wed, 10 Oct 2012 17:30:17 +0700
Subject: [PATCH] Added support for ".hocon" extension #45

---
 .../java/com/typesafe/config/ConfigFactory.java    | 10 ++++++----
 .../java/com/typesafe/config/ConfigSyntax.java     |  2 +-
 .../java/com/typesafe/config/impl/Parseable.java   |  2 +-
 .../com/typesafe/config/impl/SimpleIncluder.java   | 14 +++++++++++++-
 .../com/typesafe/config/impl/EquivalentsTest.scala |  3 ++-
 5 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/config/src/main/java/com/typesafe/config/ConfigFactory.java b/config/src/main/java/com/typesafe/config/ConfigFactory.java
index e713a4bb..82abe2d4 100644
--- a/config/src/main/java/com/typesafe/config/ConfigFactory.java
+++ b/config/src/main/java/com/typesafe/config/ConfigFactory.java
@@ -555,10 +555,11 @@ public final class ConfigFactory {
      * with all known extensions and merges whatever is found.
      *
      * <p>
-     * In the current implementation, the extension ".conf" forces
+     * In the current implementation, the extension ".conf" or ".hocon" forces
      * {@link ConfigSyntax#CONF}, ".json" forces {@link ConfigSyntax#JSON}, and
      * ".properties" forces {@link ConfigSyntax#PROPERTIES}. When merging files,
-     * ".conf" falls back to ".json" falls back to ".properties".
+     * ".conf" falls back to ".hocon" fall back to ".json" falls back to 
+     * ".properties".
      *
      * <p>
      * Future versions of the implementation may add additional syntaxes or
@@ -644,8 +645,9 @@ public final class ConfigFactory {
      * classpath order. To keep it simple, the lists are simply concatenated,
      * with the same syntax priorities as
      * {@link #parseFileAnySyntax(File,ConfigParseOptions) parseFileAnySyntax()}
-     * - all ".conf" resources are ahead of all ".json" resources which are
-     * ahead of all ".properties" resources.
+     * - all ".conf" resources are ahead of all ".hocon" resources which are
+     * ahead of all ".json" resources which are ahead of all ".properties" 
+     * resources.
      *
      * @param klass
      *            class which determines the <code>ClassLoader</code> and the
diff --git a/config/src/main/java/com/typesafe/config/ConfigSyntax.java b/config/src/main/java/com/typesafe/config/ConfigSyntax.java
index 54529fad..bb886872 100644
--- a/config/src/main/java/com/typesafe/config/ConfigSyntax.java
+++ b/config/src/main/java/com/typesafe/config/ConfigSyntax.java
@@ -6,7 +6,7 @@ package com.typesafe.config;
 /**
  * The syntax of a character stream, <a href="http://json.org">JSON</a>, <a
  * href="https://github.com/typesafehub/config/blob/master/HOCON.md">HOCON</a>
- * aka ".conf", or <a href=
+ * aka ".conf" and ".hocon", or <a href=
  * "http://download.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29"
  * >Java properties</a>.
  *
diff --git a/config/src/main/java/com/typesafe/config/impl/Parseable.java b/config/src/main/java/com/typesafe/config/impl/Parseable.java
index 16f3cb96..4f28254b 100644
--- a/config/src/main/java/com/typesafe/config/impl/Parseable.java
+++ b/config/src/main/java/com/typesafe/config/impl/Parseable.java
@@ -228,7 +228,7 @@ public abstract class Parseable implements ConfigParseable {
     private static ConfigSyntax syntaxFromExtension(String name) {
         if (name.endsWith(".json"))
             return ConfigSyntax.JSON;
-        else if (name.endsWith(".conf"))
+        else if (name.endsWith(".conf") || name.endsWith(".hocon"))
             return ConfigSyntax.CONF;
         else if (name.endsWith(".properties"))
             return ConfigSyntax.PROPERTIES;
diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleIncluder.java b/config/src/main/java/com/typesafe/config/impl/SimpleIncluder.java
index e3cd2513..5987b79e 100644
--- a/config/src/main/java/com/typesafe/config/impl/SimpleIncluder.java
+++ b/config/src/main/java/com/typesafe/config/impl/SimpleIncluder.java
@@ -166,12 +166,14 @@ class SimpleIncluder implements FullIncluder {
     // loading app.{conf,json,properties} from the filesystem.
     static ConfigObject fromBasename(NameSource source, String name, ConfigParseOptions options) {
         ConfigObject obj;
-        if (name.endsWith(".conf") || name.endsWith(".json") || name.endsWith(".properties")) {
+        if (name.endsWith(".conf") || name.endsWith(".hocon") || name.endsWith(".json") 
+                 || name.endsWith(".properties")) {
             ConfigParseable p = source.nameToParseable(name, options);
 
             obj = p.parse(p.options().setAllowMissing(options.getAllowMissing()));
         } else {
             ConfigParseable confHandle = source.nameToParseable(name + ".conf", options);
+            ConfigParseable hoconHandle = source.nameToParseable(name + ".hocon", options);
             ConfigParseable jsonHandle = source.nameToParseable(name + ".json", options);
             ConfigParseable propsHandle = source.nameToParseable(name + ".properties", options);
             boolean gotSomething = false;
@@ -190,6 +192,16 @@ class SimpleIncluder implements FullIncluder {
                 }
             }
 
+            if (syntax == null || syntax == ConfigSyntax.CONF) {
+                try {
+                    obj = hoconHandle.parse(hoconHandle.options().setAllowMissing(false)
+                            .setSyntax(ConfigSyntax.CONF));
+                    gotSomething = true;
+                } catch (ConfigException.IO e) {
+                    failMessages.add(e.getMessage());
+                }
+            }
+
             if (syntax == null || syntax == ConfigSyntax.JSON) {
                 try {
                     ConfigObject parsed = jsonHandle.parse(jsonHandle.options()
diff --git a/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala b/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
index caff1593..8094e4a6 100644
--- a/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
+++ b/config/src/test/scala/com/typesafe/config/impl/EquivalentsTest.scala
@@ -24,7 +24,8 @@ class EquivalentsTest extends TestUtils {
 
     private def filesForEquiv(equiv: File) = {
         val rawFiles = equiv.listFiles()
-        val files = rawFiles.filter({ f => f.getName().endsWith(".json") || f.getName().endsWith(".conf") })
+        val files = rawFiles.filter({ f => f.getName().endsWith(".json") || f.getName().endsWith(".conf")
+                                               || f.getName().endsWith(".hocon") })
         files
     }