From a83bd814bf4f7aea7a6ca66c54002e862b0bfe4b Mon Sep 17 00:00:00 2001
From: Stuart Halloway <stu@thinkrelevance.com>
Date: Fri, 2 Dec 2011 11:04:11 -0500
Subject: [PATCH] CLJ-733 data conveying exception

---
 src/clj/clojure/core.clj                |   20 ++++++++++++++
 src/clj/clojure/repl.clj                |    4 ++-
 src/jvm/clojure/lang/ExceptionInfo.java |   42 +++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 1 deletions(-)
 create mode 100644 src/jvm/clojure/lang/ExceptionInfo.java

diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index b0a25fc..6860576 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -4220,6 +4220,26 @@
     (with-out-str
      (apply println xs)))
 
+(import clojure.lang.ExceptionInfo)
+(defn ex-info
+  "Alpha - subject to change.
+   Create an instance of ExceptionInfo, a RuntimeException subclass
+   that carries a map of additional data."
+  {:added "1.4"}
+  ([msg map]
+     (ExceptionInfo. msg map))
+  ([msg map cause]
+     (ExceptionInfo. msg map cause)))
+
+(defn ex-data
+  "Alpha - subject to change.
+   Returns exception data (a map) if ex is an ExceptionInfo.
+   Otherwise returns nil."
+  {:added "1.4"}
+  [ex]
+  (when (instance? ExceptionInfo ex)
+    (.getData ^ExceptionInfo ex)))
+
 (defmacro assert
   "Evaluates expr and throws an exception if it does not evaluate to
   logical true."
diff --git a/src/clj/clojure/repl.clj b/src/clj/clojure/repl.clj
index 3218180..e6ea66f 100644
--- a/src/clj/clojure/repl.clj
+++ b/src/clj/clojure/repl.clj
@@ -251,7 +251,9 @@ str-or-pattern."
          (pst (root-cause e) e-or-depth))))
   ([^Throwable e depth]
      (binding [*out* *err*]
-       (println (str (-> e class .getSimpleName) " " (.getMessage e)))
+       (println (str (-> e class .getSimpleName) " "
+                     (.getMessage e)
+                     (when-let [info (ex-data e)] (str " " (pr-str info)))))
        (let [st (.getStackTrace e)
              cause (.getCause e)]
          (doseq [el (take depth
diff --git a/src/jvm/clojure/lang/ExceptionInfo.java b/src/jvm/clojure/lang/ExceptionInfo.java
new file mode 100644
index 0000000..1ebc83c
--- /dev/null
+++ b/src/jvm/clojure/lang/ExceptionInfo.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ */
+
+package clojure.lang;
+
+/**
+ * Exception that carries data (a map) as additional payload. Clojure programs that need
+ * richer semantics for exceptions should use this in lieu of defining project-specific
+ * exception classes.
+ */
+public class ExceptionInfo extends RuntimeException{
+    public final IPersistentMap data;
+
+    public ExceptionInfo(String s, IPersistentMap data) {
+        super(s);
+        if (data instanceof IPersistentMap) {
+            this.data = data;
+        }  else {
+            throw new IllegalArgumentException("Additional data must be a persistent map: " + data);
+        }
+    }
+
+    public ExceptionInfo(String s, IPersistentMap data, Throwable throwable) {
+        super(s, throwable);
+        this.data = data;
+    }
+
+    public IPersistentMap getData() {
+        return data;
+    }
+
+    public String toString() {
+        return "clojure.lang.ExceptionInfo: " + getMessage() + " " + data.toString();
+    }
+}
-- 
1.7.4.1

