diff --git a/examples/network/github_timeline.own b/examples/network/github_timeline.own
new file mode 100644
index 0000000..2363f3b
--- /dev/null
+++ b/examples/network/github_timeline.own
@@ -0,0 +1,68 @@
+use "std"
+use "http"
+use "json"
+use "functional"
+
+header = "* Prints current GitHub timeline *"
+println "*" * length(header)
+println header
+println "*" * length(header)
+
+// Executes in main thread
+//http("https://api.github.com/events", def(r) {
+// foreach(jsondecode(r), ::show_github_events)
+//})
+
+// Executes in new thread
+thread(::http, "https://api.github.com/events", def(r) {
+ foreach(jsondecode(r), ::show_github_events)
+})
+
+def show_github_events(event) {
+ println event["created_at"]
+ actor = event["actor"]
+ println "User: https://github.com/" + actor["login"]
+ println github_event_type(event)
+ println "-" * 50
+}
+
+def github_event_type(event) {
+ type = event["type"]
+ repo = event["repo"]
+ repo = "https://github.com/" + repo["name"]
+ payload = event["payload"]
+
+ if (type == "CommitCommentEvent") {
+ comment = payload["comment"]
+ return "commented commit in " + repo + "\n" + comment["body"]
+ }
+ if (type == "CreateEvent") {
+ return "created " + payload["ref_type"] + " on " + repo
+ }
+ if (type == "DeleteEvent") {
+ return "deleted " + payload["ref_type"] + " on " + repo
+ }
+ if (type == "ForkEvent") {
+ return "forked repository " + repo
+ }
+ if (type == "IssueCommentEvent") {
+ comment = payload["comment"]
+ issue = payload["issue"]
+ return "commented issue " + issue["title"] + " on " + repo + "\n" + comment["body"]
+ }
+ if (type == "IssuesEvent") {
+ issue = payload["issue"]
+ return payload["action"] + " issue '" + issue["title"] + "' on " + repo
+ }
+ if (type == "PullRequestEvent") {
+ pr = payload["pull_request"]
+ return payload["action"] + " pull request #" + payload["number"] + " '" + pr["title"] + "' on " + repo
+ }
+ if (type == "PushEvent") {
+ return "pushed " + length(payload["commits"]) + " commits to " + repo
+ }
+ if (type == "WatchEvent") {
+ return "start watching repository " + repo
+ }
+ return type + " on " + repo
+}
diff --git a/libs/json-20151123.jar b/libs/json-20151123.jar
new file mode 100644
index 0000000..472b253
Binary files /dev/null and b/libs/json-20151123.jar differ
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
index b7e2500..6d4a345 100644
--- a/nbproject/build-impl.xml
+++ b/nbproject/build-impl.xml
@@ -222,6 +222,7 @@ is divided into following sections:
+
@@ -698,7 +699,7 @@ is divided into following sections:
-
+
@@ -773,7 +774,7 @@ is divided into following sections:
-
+
@@ -800,7 +801,7 @@ is divided into following sections:
-
+
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
index 1037409..1cc3671 100644
--- a/nbproject/genfiles.properties
+++ b/nbproject/genfiles.properties
@@ -4,5 +4,5 @@ build.xml.stylesheet.CRC32=8064a381@1.78.0.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=48c1b1a1
-nbproject/build-impl.xml.script.CRC32=e3eeb3f1
-nbproject/build-impl.xml.stylesheet.CRC32=2d327b5d@1.78.0.48
+nbproject/build-impl.xml.script.CRC32=b8cd7788
+nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.0.48
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 9de6066..f4950c5 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -1,9 +1,10 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
-annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
+application.title=OwnLang
+application.vendor=aNNiMON
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
@@ -26,10 +27,21 @@ dist.archive.excludes=
dist.dir=dist
dist.jar=${dist.dir}/OwnLang.jar
dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
excludes=
+file.reference.commons-codec-1.6.jar=libs/commons-codec-1.6.jar
+file.reference.commons-logging-1.1.3.jar=libs/commons-logging-1.1.3.jar
+file.reference.httpclient-4.3.5.jar=libs/httpclient-4.3.5.jar
+file.reference.httpcore-4.3.2.jar=libs/httpcore-4.3.2.jar
+file.reference.json-20151123.jar=libs/json-20151123.jar
includes=**
jar.compress=false
-javac.classpath=
+javac.classpath=\
+ ${file.reference.commons-codec-1.6.jar}:\
+ ${file.reference.commons-logging-1.1.3.jar}:\
+ ${file.reference.httpclient-4.3.5.jar}:\
+ ${file.reference.httpcore-4.3.2.jar}:\
+ ${file.reference.json-20151123.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
diff --git a/program.own b/program.own
index 4cc1cfd..7ac6f02 100644
--- a/program.own
+++ b/program.own
@@ -128,11 +128,25 @@ println "Sum: " + reduce(squares, 0, def(x, y) = x + y)
use "http"
-http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) {
+/*http("http://jsonplaceholder.typicode.com/users", "POST", {"name": "OwnLang", "versionCode": 10}, def(v) {
println "Added: " + v
http("http://jsonplaceholder.typicode.com/users/2", "PATCH", {"name": "Patched Name"}, ::http_get_demo)
})
def http_get_demo(v) {
println "Updated: " + v
http("http://jsonplaceholder.typicode.com/users", ::echo)
-}
+}*/
+
+use "json"
+println "json"
+println jsonencode({
+ "name": "JSON Example",
+ "version": 1,
+ "arrayData": [
+ 1, 2, 3, 4
+ ],
+ "objectData": {
+ "key": "value",
+ 10: "1000"
+ }
+})
\ No newline at end of file
diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_decode.java b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java
new file mode 100644
index 0000000..97fb9d5
--- /dev/null
+++ b/src/com/annimon/ownlang/lib/modules/functions/json_decode.java
@@ -0,0 +1,61 @@
+package com.annimon.ownlang.lib.modules.functions;
+
+import com.annimon.ownlang.lib.*;
+import java.util.Iterator;
+import org.json.*;
+
+public final class json_decode implements Function {
+
+ @Override
+ public Value execute(Value... args) {
+ if (args.length != 1) throw new RuntimeException("One argument expected");
+ try {
+ final String jsonRaw = args[0].asString();
+ final Object root = new JSONTokener(jsonRaw).nextValue();
+ return process(root);
+ } catch (JSONException ex) {
+ throw new RuntimeException("Error while parsing json", ex);
+ }
+ }
+
+ private Value process(Object obj) {
+ if (obj instanceof JSONObject) {
+ return process((JSONObject) obj);
+ }
+ if (obj instanceof JSONArray) {
+ return process((JSONArray) obj);
+ }
+ if (obj instanceof String) {
+ return new StringValue((String) obj);
+ }
+ if (obj instanceof Number) {
+ return new NumberValue(((Number) obj).doubleValue());
+ }
+ if (obj instanceof Boolean) {
+ return NumberValue.fromBoolean((Boolean) obj);
+ }
+ // NULL or other
+ return NumberValue.ZERO;
+ }
+
+ private MapValue process(JSONObject json) {
+ final MapValue result = new MapValue(json.length());
+ final Iterator it = json.keys();
+ while(it.hasNext()) {
+ final String key = it.next();
+ final Value value = process(json.get(key));
+ result.set(new StringValue(key), value);
+ }
+ return result;
+ }
+
+ private ArrayValue process(JSONArray json) {
+ final int length = json.length();
+ final ArrayValue result = new ArrayValue(length);
+ for (int i = 0; i < length; i++) {
+ final Value value = process(json.get(i));
+ result.set(i, value);
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/com/annimon/ownlang/lib/modules/functions/json_encode.java b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java
new file mode 100644
index 0000000..8263ac1
--- /dev/null
+++ b/src/com/annimon/ownlang/lib/modules/functions/json_encode.java
@@ -0,0 +1,53 @@
+package com.annimon.ownlang.lib.modules.functions;
+
+import com.annimon.ownlang.lib.*;
+import java.util.Map;
+import org.json.*;
+
+public final class json_encode implements Function {
+
+ @Override
+ public Value execute(Value... args) {
+ if (args.length != 1) throw new RuntimeException("One argument expected");
+ try {
+ final Object root = process(args[0]);
+ final String jsonRaw = JSONObject.valueToString(root);
+ return new StringValue(jsonRaw);
+ } catch (JSONException ex) {
+ throw new RuntimeException("Error while creating json", ex);
+ }
+ }
+
+ private Object process(Value val) {
+ switch (val.type()) {
+ case Types.ARRAY:
+ return process((ArrayValue) val);
+ case Types.MAP:
+ return process((MapValue) val);
+ case Types.NUMBER:
+ return val.asNumber();
+ case Types.STRING:
+ return val.asString();
+ default:
+ return JSONObject.NULL;
+ }
+ }
+
+ private Object process(MapValue map) {
+ final JSONObject result = new JSONObject();
+ for (Map.Entry entry : map) {
+ final String key = entry.getKey().asString();
+ final Object value = process(entry.getValue());
+ result.put(key, value);
+ }
+ return result;
+ }
+
+ private Object process(ArrayValue array) {
+ final JSONArray result = new JSONArray();
+ for (Value value : array) {
+ result.put(process(value));
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/com/annimon/ownlang/lib/modules/json.java b/src/com/annimon/ownlang/lib/modules/json.java
new file mode 100644
index 0000000..6af5997
--- /dev/null
+++ b/src/com/annimon/ownlang/lib/modules/json.java
@@ -0,0 +1,17 @@
+package com.annimon.ownlang.lib.modules;
+
+import com.annimon.ownlang.lib.*;
+import com.annimon.ownlang.lib.modules.functions.*;
+
+/**
+ *
+ * @author aNNiMON
+ */
+public final class json implements Module {
+
+ @Override
+ public void init() {
+ Functions.set("jsonencode", new json_encode());
+ Functions.set("jsondecode", new json_decode());
+ }
+}