dart: add dlog library

dlog is a dart library for pretty-printing structured data into
console. This will be used in baku/mdtest project.

Change-Id: Ib241552f679951b3fe7dd8679500b53960b5502f
diff --git a/dart/dlog/.gitignore b/dart/dlog/.gitignore
new file mode 100644
index 0000000..e8a0eda
--- /dev/null
+++ b/dart/dlog/.gitignore
@@ -0,0 +1,7 @@
+.buildlog
+.DS_Store
+.idea
+.pub/
+build/
+packages
+pubspec.lock
diff --git a/dart/dlog/CHANGELOG.md b/dart/dlog/CHANGELOG.md
new file mode 100644
index 0000000..a483e1e
--- /dev/null
+++ b/dart/dlog/CHANGELOG.md
@@ -0,0 +1,25 @@
+# Changelog
+
+## 0.0.1
+
+- Initial version, created by Stagehand
+- Implements output information as table
+
+## 0.0.2
+
+- Some document fixes
+
+## 0.0.3
+
+- Fix condition for cell separator
+- Implements output information as tree
+
+## 0.0.4
+
+- Implements output information as json
+- Some fixes
+
+## 0.0.5
+
+- Added flush buffer before each output
+- Implements Timer
\ No newline at end of file
diff --git a/dart/dlog/LICENSE b/dart/dlog/LICENSE
new file mode 100644
index 0000000..f1a13c6
--- /dev/null
+++ b/dart/dlog/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2015, Viktor Dakalov.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the <organization> nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/dart/dlog/README.google b/dart/dlog/README.google
new file mode 100644
index 0000000..9623e5a
--- /dev/null
+++ b/dart/dlog/README.google
@@ -0,0 +1,10 @@
+URL: https://github.com/vdakalov/dlog/archive/60479aed7047a68ba2b95b2234ee06ad6b25bb62.zip
+Version: 60479aed7047a68ba2b95b2234ee06ad6b25bb62
+License: BSD
+License File: LICENSE
+
+Description:
+dlog is a dart library for pretty-printing structured data into console.
+
+Local Modifications:
+No Modifications
diff --git a/dart/dlog/README.md b/dart/dlog/README.md
new file mode 100644
index 0000000..5b7bbc6
--- /dev/null
+++ b/dart/dlog/README.md
@@ -0,0 +1,288 @@
+# dlog
+
+A useful library for output structured information of debugging into console
+
+## Usage
+
+The information can be structured as a table, tree or json. So it is possible to measure the performance of a function or piece of code.
+
+### Table
+
+    import "dart:math" as Math;
+    import 'package:dlog/dlog.dart' as dlog;
+    
+    double rad(num deg) =>
+      deg * (Math.PI / 180);
+    
+    main() {
+    
+        // create new table and specify the column number
+        var table = new dlog.Table(1);
+        
+        // you can add header names (optional)
+        // in this case the number of columns is changed to 3
+        table.columns.add("deg°");
+        table.columns.addAll(["radian", "vector (x, y)"]);
+        
+        for (int angle = 0; angle < 360; angle++) {
+            
+            String x = (1 * Math.sin(angle)).toStringAsFixed(4),
+                   y = (1 * Math.cos(angle)).toStringAsFixed(4);
+            
+            // add row (number of cell equal columns number)
+            table.data.addAll([
+              angle,       // deg°
+              rad(angle),  // radian
+              [x, y]       // vector (x, y)
+            ]);
+        }
+        
+        // cut part of table
+        var pi = table.clone().crop(0, 180),
+            pi2 = table.clone().crop(180, 180);
+        
+        // output to console
+        print(table);
+        
+        // output range 0-179
+        print(pi);
+        
+        // output range 180-359
+        print(pi2);
+    }
+
+Result:
+
+    ┌──────┬──────────────────────┬────────────────────┐
+    │ DEG° │ RADIAN               │ VECTOR (X, Y)      │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 0    │ 0.0                  │ [0.0000, 1.0000]   │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 1    │ 0.017453292519943295 │ [0.8415, 0.5403]   │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 2    │ 0.03490658503988659  │ [0.9093, -0.4161]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 3    │ 0.05235987755982989  │ [0.1411, -0.9900]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 4    │ 0.06981317007977318  │ [-0.7568, -0.6536] │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 5    │ 0.08726646259971647  │ [-0.9589, 0.2837]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 6    │ 0.10471975511965978  │ [-0.2794, 0.9602]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 7    │ 0.12217304763960307  │ [0.6570, 0.7539]   │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 8    │ 0.13962634015954636  │ [0.9894, -0.1455]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 354  │ 6.178465552059927    │ [0.8415, -0.5403]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 355  │ 6.19591884457987     │ [-0.0000, -1.0000] │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 356  │ 6.213372137099813    │ [-0.8415, -0.5403] │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 357  │ 6.230825429619756    │ [-0.9093, 0.4162]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 358  │ 6.2482787221397      │ [-0.1411, 0.9900]  │
+    ├──────┼──────────────────────┼────────────────────┤
+    │ 359  │ 6.265732014659643    │ [0.7568, 0.6536]   │
+    └──────┴──────────────────────┴────────────────────┘
+
+### Tree
+
+    import 'package:dlog/dlog.dart' as dlog;
+    
+    List<Map> users = [
+      {
+        "name": "Dmitry",
+        "age": 23,
+        "city": "Yekaterinburg"
+      },
+      {
+        "name": "Alexandr",
+        "age": 28,
+        "city": "Moskow"
+      }
+    ];
+    
+    main() {
+      
+      var tree = new dlog.Tree("Users");
+      
+      // required opening root group
+      tree.openGroup();
+      
+      for (int i = 0; i < users.length; i++) {
+          tree
+              // add name
+              ..add(users[i]["name"])
+              
+              // open user group
+              // (previous element is the name of the group following)
+              ..openGroup()
+              
+              // subitems
+              ..add("age: ${users[i]["age"]}")
+              ..add("city: ${users[i]["city"]}")
+              
+              // close user group
+              ..closeGroup()
+              ;
+      }
+      
+      // close root group and output in console
+      print(tree..closeGroup());
+    }
+
+Result
+
+    Users
+    │ ├ Dmitry
+    │ │ ├ age: 23
+    │ │ └ city: Yekaterinburg
+    │ ├ Alexandr
+    │ │ ├ age: 28
+    │ │ └ city: Moskow
+
+### Json
+    
+    import "package:dlog/dlog.dart" as dlog;
+    import "dart:convert";
+    
+    main() {
+    
+      // create Json object and
+      var debug = new dlog.Json(
+          title: "My",
+          data: new List()
+      );
+    
+      debug.data = getJSON();
+      debug.title += " json";
+    
+      // max length for string (set null for output full string)
+      debug.maxStringLen = 50;
+    
+      // custom data parsers (custom parsers for List, Map and String types will be ignored)
+      debug.parsers["int"] = (int num) => "$num <-- int";
+
+      // no clear buffer [by default: true]
+      debug.flush = false;
+    
+      // output
+      print(debug);
+    
+    }
+    
+
+Result
+
+    My json
+    [
+      0: {
+        _id: "54bde2ec6d0c45fe2aad89a1",
+        index: 0 <-- int,
+        guid: "391ad3b0-e003-44fe-8f52-9a53b0d2ce52",
+        isActive: true,
+        balance: "$3,385.54",
+        picture: "http://placehold.it/32x32",
+        age: 25 <-- int,
+        eyeColor: "blue",
+        name: "Burns Avery",
+        gender: "male",
+        company: "COMTRAIL",
+        email: "burnsavery@comtrail.com",
+        phone: "+1 (829) 415-3400",
+        address: "496 Hemlock Street, Hegins, New Mexico, 4698",
+        about: "Qui ex labore irure proident aute veniam sit minim...",
+        registered: "2014-09-19T00:05:13 -05:00",
+        latitude: -2.302439,
+        longitude: 92.194414,
+        tags: [
+          0: "commodo",
+          1: "eu",
+          2: "deserunt",
+          3: "quis",
+          4: "dolor",
+          5: "nulla",
+          6: "ad"
+        ],
+        friends: [
+          0: {
+            id: 0 <-- int,
+            name: "Katheryn Rogers"
+          },
+          1: {
+            id: 1 <-- int,
+            name: "Corine Smith"
+          },
+          2: {
+            id: 2 <-- int,
+            name: "Jacobson Christensen"
+          }
+        ],
+        greeting: "Hello, Burns Avery! You have 3 unread messages.",
+        favoriteFruit: "strawberry"
+      }
+    ]
+
+### Additional fetature
+
+Opportunity to detect run-time function
+
+    import "dart:math";
+    import "package:dlog/dlog.dart" as dlog;
+    
+    sleep() {
+      DateTime init = new DateTime.now();
+      int ms = 1500;
+      while (ms > new DateTime.now().difference(init).inMilliseconds);
+    }
+    
+    main() {
+    
+      // create Time object and specity descrption
+      var debug = new dlog.Time("Time test for power function");
+    
+      // call check before a separate logical operation
+      debug.checkBegin("complex random");
+    
+      // some logic
+      int len = 1000;
+      while (len-- > 0) {
+        sqrt(pow(new Random().nextDouble(), new Random().nextDouble()) * new Random().nextDouble());
+      }
+    
+      // and after completion
+      debug.checkEnd("complex random");
+    
+      debug.checkFunc("sleep function", sleep);
+    
+      debug.checkFunc("check power speed", (){
+        for (int i = 0; i < 100000; i++) {
+          pow(i, 100);
+        }
+      });
+    
+      // output
+      print(debug);
+    
+    }
+
+Result
+
+    ────────────────────────────────────────────────
+     Time test for power function
+    ─────┬────────────────┬─────────────────────────
+     1   │ 0:00:00.014000 │ complex random
+     2   │ 0:00:01.500000 │ sleep function
+     3   │ 0:00:00.414000 │ check power speed
+    ─────┴────────────────┴─────────────────────────
+
+
+## Features and bugs
+
+Please file feature requests and bugs at the [issue tracker][tracker].
+
+[tracker]: https://github.com/vdakalov/DLog
\ No newline at end of file
diff --git a/dart/dlog/example/json.dart b/dart/dlog/example/json.dart
new file mode 100644
index 0000000..7a2115d
--- /dev/null
+++ b/dart/dlog/example/json.dart
@@ -0,0 +1,87 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+library dlog.example.json;
+
+import "package:dlog/dlog.dart" as dlog;
+import "dart:convert";
+
+getJSON() {
+  var data = '''
+[
+  {
+    "_id": "54bde2ec6d0c45fe2aad89a1",
+    "index": 0,
+    "guid": "391ad3b0-e003-44fe-8f52-9a53b0d2ce52",
+    "isActive": true,
+    "balance": "\$3,385.54",
+    "picture": "http://placehold.it/32x32",
+    "age": 25,
+    "eyeColor": "blue",
+    "name": "Burns Avery",
+    "gender": "male",
+    "company": "COMTRAIL",
+    "email": "burnsavery@comtrail.com",
+    "phone": "+1 (829) 415-3400",
+    "address": "496 Hemlock Street, Hegins, New Mexico, 4698",
+    "about": "Qui ex labore irure proident aute veniam sit minim Lorem irure. Est officia quis amet dolor duis velit pariatur culpa elit in aliqua aute magna. Occaecat et proident ut ea sit dolore aliquip. Ipsum minim esse ad et deserunt. Ad sit amet occaecat sint. Ipsum anim commodo ex eiusmod reprehenderit exercitation sit mollit proident aliqua. Occaecat reprehenderit mollit voluptate tempor nostrud qui anim veniam est laboris qui pariatur.\r\n",
+    "registered": "2014-09-19T00:05:13 -05:00",
+    "latitude": -2.302439,
+    "longitude": 92.194414,
+    "tags": [
+      "commodo",
+      "eu",
+      "deserunt",
+      "quis",
+      "dolor",
+      "nulla",
+      "ad"
+    ],
+    "friends": [
+      {
+        "id": 0,
+        "name": "Katheryn Rogers"
+      },
+      {
+        "id": 1,
+        "name": "Corine Smith"
+      },
+      {
+        "id": 2,
+        "name": "Jacobson Christensen"
+      }
+    ],
+    "greeting": "Hello, Burns Avery! You have 3 unread messages.",
+    "favoriteFruit": "strawberry"
+  }
+]
+'''.replaceAll(new RegExp("\n|\r"), "");
+  return JSON.decode(data);
+}
+
+main() {
+
+  // create Json object and
+  var debug = new dlog.Json(
+      title: "My",
+      data: new List()
+  );
+
+  debug.data = getJSON();
+  debug.title += " json";
+
+  // max length for string (set null for output full string)
+  debug.maxStringLen = 50;
+
+  // custom data parsers (custom parsers for List, Map and String types will be ignored)
+  debug.parsers["int"] = (int num) => "$num <-- int";
+
+  // output
+  print(debug);
+
+  // no clear buffer [by default: true]
+  debug.flush = false;
+  debug.maxStringLen = 10;
+  print(debug);
+
+}
diff --git a/dart/dlog/example/table.dart b/dart/dlog/example/table.dart
new file mode 100644
index 0000000..7209107
--- /dev/null
+++ b/dart/dlog/example/table.dart
@@ -0,0 +1,48 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+library dlog.example.table;
+
+import "dart:math" as Math;
+import "package:dlog/dlog.dart" as dlog;
+
+double rad(num deg) =>
+  deg * (Math.PI / 180);
+
+main() {
+
+  // create new table and specify the column number
+  var table = new dlog.Table(1);
+
+  // you can add header names (optional)
+  // in this case the number of columns is changed to 3
+  table.columns.add("deg°");
+  table.columns.addAll(["radian", "vector (x, y)"]);
+
+  for (int angle = 0; angle < 360; angle++) {
+
+    String x = (1 * Math.sin(angle)).toStringAsFixed(4),
+           y = (1 * Math.cos(angle)).toStringAsFixed(4);
+
+    // add row (number of cell equal columns number)
+    table.data.addAll([
+      angle,       // deg°
+      rad(angle),  // radian
+      [x, y]       // vector (x, y)
+    ]);
+  }
+
+  // cut part of table
+  var pi = table.clone().crop(0, 180),
+      pi2 = table.clone().crop(180, 180);
+
+  // output to console
+  print(table);
+
+  // output range 0-179
+  print(pi);
+
+  // output range 180-359
+  print(pi2);
+
+}
diff --git a/dart/dlog/example/time.dart b/dart/dlog/example/time.dart
new file mode 100644
index 0000000..15f0f6c
--- /dev/null
+++ b/dart/dlog/example/time.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+library dlog.example.time;
+
+import "dart:math";
+import "package:dlog/dlog.dart" as dlog;
+
+sleep() {
+  DateTime init = new DateTime.now();
+  int ms = 1500;
+  while (ms > new DateTime.now().difference(init).inMilliseconds);
+}
+
+main() {
+
+  // create Time object and specity descrption
+  // object can be used throughout the application
+  var debug = new dlog.Time("Time test for power function");
+
+  // call check before a separate logical operation
+  debug.checkBegin("complex random");
+
+  // some logic
+  int len = 1000;
+  while (len-- > 0) {
+    sqrt(pow(new Random().nextDouble(), new Random().nextDouble()) * new Random().nextDouble());
+  }
+
+  // and after completion
+  debug.checkEnd("complex random");
+
+  // same as above
+  debug.checkFunc("sleep function", sleep);
+
+  debug.checkFunc("check power speed", (){
+    for (int i = 0; i < 100000; i++) {
+      pow(i, 100);
+    }
+  });
+
+  // output
+  print(debug);
+
+}
diff --git a/dart/dlog/example/tree.dart b/dart/dlog/example/tree.dart
new file mode 100644
index 0000000..e8081b5
--- /dev/null
+++ b/dart/dlog/example/tree.dart
@@ -0,0 +1,37 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+library dlog.example.tree;
+
+import "package:dlog/dlog.dart" as dlog;
+
+List<Map> users = [
+  {
+    "name": "Dmitry",
+    "age": 23,
+    "city": "Yekaterinburg"
+  },
+  {
+    "name": "Vasya",
+    "age": 28,
+    "city": "Moskow"
+  }
+];
+
+main() {
+
+  var debug = new dlog.Tree("Users");
+  debug.openGroup();
+
+  for (int i = 0; i < users.length; i++) {
+    debug..add(users[i]["name"])
+              ..openGroup()
+              ..add("age: ${users[i]["age"]}")
+              ..add("city: ${users[i]["city"]}")
+              ..closeGroup()
+              ;
+  }
+
+  print(debug..closeGroup());
+
+}
diff --git a/dart/dlog/lib/dlog.dart b/dart/dlog/lib/dlog.dart
new file mode 100644
index 0000000..aec0970
--- /dev/null
+++ b/dart/dlog/lib/dlog.dart
@@ -0,0 +1,11 @@
+library dlog;
+
+import "dart:mirrors";
+
+part "src/Symbols.dart";
+
+part "src/Display.dart";
+part "src/displays/Table.dart";
+part "src/displays/Tree.dart";
+part "src/displays/Json.dart";
+part "src/displays/Time.dart";
\ No newline at end of file
diff --git a/dart/dlog/lib/src/Display.dart b/dart/dlog/lib/src/Display.dart
new file mode 100644
index 0000000..30b05de
--- /dev/null
+++ b/dart/dlog/lib/src/Display.dart
@@ -0,0 +1,35 @@
+part of dlog;
+
+abstract class _Display {
+
+  final List<String> _outputBuffer = new List<String>();
+
+  bool flush = true;
+
+  String endOfLineUnicode = Symbols.LF;
+
+  _outputBufferWrite(String char, [num]) {
+    num = num is int ? num : 1;
+    for (; num > 0; num--) {
+      _outputBuffer.add(char);
+    }
+  }
+
+  _outputBufferWriteLn([String char, num]) {
+    char = char is String ? char : "";
+    _outputBufferWrite(char, num);
+    _outputBufferWrite(endOfLineUnicode);
+  }
+
+  _initBuffer();
+
+  toString() {
+
+    if (flush) {
+      _outputBuffer.clear();
+    }
+
+    _initBuffer();
+    return _outputBuffer.join();
+  }
+}
\ No newline at end of file
diff --git a/dart/dlog/lib/src/Symbols.dart b/dart/dlog/lib/src/Symbols.dart
new file mode 100644
index 0000000..0b24ad0
--- /dev/null
+++ b/dart/dlog/lib/src/Symbols.dart
@@ -0,0 +1,64 @@
+part of dlog;
+
+class Symbols {
+  static const String LF = "\u000A";
+  static const String CL = "\u000D";
+  static const String space = "\u0020";
+
+  static const String colon = "\u003A";
+  static const String semicolon = "\u003B";
+  static const String comma = "\u002C";
+  static const String bracketSquareLeft = "\u005B";
+  static const String bracketSquareRight = "\u005D";
+  static const String bracketCurlyLeft = "\u007B";
+  static const String bracketCurlyRight = "\u007D";
+  static const String quotationMark = "\u0022";
+  static const String apostrophe = "\u0027";
+
+  static const String doubleLineVertical = "\u2551";
+  static const String doubleLineHorizontal = "\u2550";
+  static const String doubleLineAngleTopLeft = "\u2554";
+  static const String doubleLineAngleTopRight = "\u2557";
+  static const String doubleLineAngleBottomLeft = "\u255A";
+  static const String doubleLineAngleBottomRight = "\u255D";
+  static const String doubleLineFromTop = "\u2569";
+  static const String doubleLineFromLeft = "\u2563";
+  static const String doubleLineFromRight = "\u2560";
+  static const String doubleLineFromBottom = "\u2566";
+  static const String doubleLineCross = "\u256C";
+  static const String doubleLineSingleTop = "\u2567";
+  static const String doubleLineSingleLeft = "\u2562";
+  static const String doubleLineSingleRight = "\u255F";
+  static const String doubleLineSingleBottom = "\u2564";
+
+  static const String singleLineVertical = "\u2502";
+  static const String singleLineHorizontal = "\u2500";
+  static const String singleLineCross = "\u253C";
+  static const String singleLineAngleTopLeft = "\u250C";
+  static const String singleLineAngleTopRight = "\u2510";
+  static const String singleLineAngleBottomLeft = "\u2514";
+  static const String singleLineAngleBottomRight = "\u2518";
+  static const String singleLineFromTop = "\u2534";
+  static const String singleLineFromLeft = "\u2524";
+  static const String singleLineFromRight = "\u251C";
+  static const String singleLineFromBottom = "\u252C";
+
+  static const String singleLineHeavyHorizontal = "\u2501";
+  static const String singleLineHeavyVertical = "\u2503";
+  static const String singleLineHeavyAngleTopLeft = "\u250F";
+  static const String singleLineHeavyAngleTopRight = "\u2513";
+  static const String singleLineHeavyAngleBottomLeft = "\u2517";
+  static const String singleLineHeavyAngleBottomRight = "\u251B";
+  static const String singleLineHeavyFromTop = "\u253B";
+  static const String singleLineHeavyFromLeft = "\u252B";
+  static const String singleLineHeavyFromRight = "\u2523";
+  static const String singleLineHeavyFromBottom = "\u2533";
+  static const String singleLineHeavyFromTopLight = "\u2537";
+  static const String singleLineHeavyFromLeftLight = "\u2528";
+  static const String singleLineHeavyFromRightLight = "\u2520";
+  static const String singleLineHeavyFromBottomLight = "\u252F";
+
+  static const String dashLineHorizontal = "\u2508";
+  static const String dashLineVertical = "\u250A";
+
+}
\ No newline at end of file
diff --git a/dart/dlog/lib/src/displays/Json.dart b/dart/dlog/lib/src/displays/Json.dart
new file mode 100644
index 0000000..cfc8853
--- /dev/null
+++ b/dart/dlog/lib/src/displays/Json.dart
@@ -0,0 +1,122 @@
+part of dlog;
+
+class Json extends _Display {
+
+  final Map<String, Function> parsers = new Map<String, Function>();
+
+  String title;
+  dynamic data;
+
+  int _depth = 0;
+
+  int indent = 2;
+  int maxStringLen = 45;
+
+  Json({this.data, this.title});
+
+  _depthDown() =>
+      _depth += indent;
+
+  _depthUp() =>
+      _depth -= indent;
+
+  _typeof(dynamic some) =>
+    reflect(some).type.reflectedType.toString();
+
+  _outputDepth() {
+    _outputBufferWrite(Symbols.space, _depth);
+  }
+
+  _outputItem(dynamic item) {
+
+    if (item is List) {
+      _outputBufferWriteLn(Symbols.bracketSquareLeft);
+      _depthDown();
+      _outputList(item);
+      _depthUp();
+      _outputDepth();
+      _outputBufferWrite(Symbols.bracketSquareRight);
+
+    } else if (item is Map) {
+      _outputBufferWriteLn(Symbols.bracketCurlyLeft);
+      _depthDown();
+      _outputMap(item);
+      _depthUp();
+      _outputDepth();
+      _outputBufferWrite(Symbols.bracketCurlyRight);
+
+    } else if (item is String) {
+      _outputString(item);
+
+    } else {
+
+      var type = _typeof(item),
+          parser = parsers.containsKey(type) ? parsers[type] : (dynamic some) => some.toString();
+
+      _outputBufferWrite(parser(item));
+    }
+  }
+
+  _outputString(String text) {
+
+    if (maxStringLen is int) {
+      text = text.length > maxStringLen ? text.substring(0, maxStringLen) + "..." : text;
+    }
+
+    _outputBufferWrite(Symbols.quotationMark);
+    _outputBufferWrite(text);
+    _outputBufferWrite(Symbols.quotationMark);
+  }
+
+  _outputList(List list) {
+
+    for (int i = 0; i < list.length; i++) {
+
+      _outputDepth();
+      _outputBufferWrite(i.toString());
+      _outputBufferWrite(Symbols.colon);
+      _outputBufferWrite(Symbols.space);
+
+      _outputItem(list[i]);
+
+      if (i < list.length - 1) {
+        _outputBufferWrite(Symbols.comma);
+      }
+
+      _outputBufferWriteLn("");
+    }
+
+  }
+
+  _outputMap(Map map) {
+
+    List keys = map.keys.toList();
+
+    for (int i = 0; i < keys.length; i++) {
+
+      _outputDepth();
+      _outputBufferWrite(keys[i].toString());
+      _outputBufferWrite(Symbols.colon);
+      _outputBufferWrite(Symbols.space);
+
+      _outputItem(map[keys[i]]);
+
+      if (i < keys.length - 1) {
+        _outputBufferWrite(Symbols.comma);
+      }
+
+      _outputBufferWriteLn("");
+    }
+  }
+
+
+  @override
+  _initBuffer() {
+
+    _outputBufferWriteLn(title);
+    _outputItem(data);
+    _outputBufferWriteLn("");
+
+  }
+}
+
diff --git a/dart/dlog/lib/src/displays/Table.dart b/dart/dlog/lib/src/displays/Table.dart
new file mode 100644
index 0000000..145c948
--- /dev/null
+++ b/dart/dlog/lib/src/displays/Table.dart
@@ -0,0 +1,240 @@
+part of dlog;
+
+class Table<C, R> extends _Display {
+
+  List<int> _columnsWidth = new List<int>();
+  int _tableWidth = 2;
+  int _currentRow = 3;
+  int _currentColumn = 4;
+  int _size = 0;
+
+  final List<C> columns = new List<C>();
+  final List<R> data = new List<R>();
+
+  int get size => columns.length > 0 ? columns.length : _size;
+  void set size(int num) {
+    _size = num;
+    if (columns.length > num) {
+      columns.length = num;
+    }
+  }
+
+  Table(this._size);
+
+  Table.fromHeader(List<C> columns_) {
+    if (columns_ is List<C>) {
+      columns.addAll(columns_);
+      size = columns.length;
+    }
+  }
+
+  Table.fromData(this._size, List<R> rows_) {
+    if (rows_ is List<R>) {
+      data.addAll(rows_);
+    }
+  }
+
+  _initBuffer() {
+
+    List<int> sizes = new List<int>();
+    int totalWidth = 0;
+
+    if (columns.length > 0) {
+      size = columns.length;
+    }
+
+    if (size == null) {
+      return "Log (Table): Specify the number of columns (property size)";
+    }
+
+    for (int i = 0; i < columns.length; i++) {
+      String value = columns[i].toString();
+
+      if (i >= sizes.length) {
+        sizes.add(0);
+      }
+
+      if (value.length > sizes[i]) {
+        sizes[i] = value.length;
+      }
+    }
+
+    for (int i = 0; i < data.length; i++) {
+      int _i = i % size;
+      String value = data[i].toString();
+
+      if (_i >= sizes.length) {
+        sizes.add(0);
+      }
+
+      if (value.length > sizes[_i]) {
+        sizes[_i] = value.length;
+      }
+    }
+
+    sizes.forEach((size){ totalWidth += size; });
+
+    _columnsWidth = sizes;
+    _tableWidth = totalWidth;
+
+    _tableBegin();
+    _tableHeader();
+
+    int len = data.length;
+    data.length = data.length - (data.length % size);
+
+    for (int i = 0; i < data.length; i += size) {
+      if (data.length >= i + size) {
+        _currentRow = i;
+        _tableRow(data.sublist(i, i + size));
+        if (i + size < data.length) {
+          _tableRowSeparator();
+        }
+      }
+    }
+
+    _tableEnd();
+
+  }
+
+  void _tableCell(String cell) {
+    int width = _columnsWidth[_currentColumn];
+
+    _outputBufferWrite(cell);
+    _outputBufferWrite(Symbols.space, width - cell.length);
+  }
+
+  void _tableBegin() {
+
+    _outputBufferWrite(Symbols.singleLineAngleTopLeft);
+
+    for (int i = 0; i < _columnsWidth.length; i++) {
+      if (i > 0) {
+        _outputBufferWrite(Symbols.singleLineFromBottom);
+      }
+      _outputBufferWrite(
+          Symbols.singleLineHorizontal,
+          _columnsWidth[i] + 2);
+    }
+    _outputBufferWriteLn(Symbols.singleLineAngleTopRight);
+  }
+
+  void _tableEnd() {
+
+    _outputBufferWrite(Symbols.singleLineAngleBottomLeft);
+
+    for (int i = 0; i < _columnsWidth.length; i++) {
+      if (i > 0) {
+        _outputBufferWrite(Symbols.singleLineFromTop);
+      }
+      _outputBufferWrite(
+          Symbols.singleLineHorizontal,
+          _columnsWidth[i] + 2);
+    }
+    _outputBufferWriteLn(Symbols.singleLineAngleBottomRight);
+  }
+
+  void _tableHeader() {
+
+    if (columns.length == 0) {
+      return ;
+    }
+
+    _outputBufferWrite(Symbols.singleLineVertical);
+    _outputBufferWrite(Symbols.space);
+
+    for (int i = 0; i < columns.length; i++) {
+      _currentColumn = i;
+      _tableCell(columns[i].toString().toUpperCase());
+      if (columns.last != columns[i]) {
+        _tableHeaderCellSeparator();
+      }
+    }
+
+    _outputBufferWrite(Symbols.space);
+    _outputBufferWriteLn(Symbols.singleLineVertical);
+
+    _tableHeaderSeparator();
+  }
+
+  void _tableHeaderSeparator() {
+
+    _outputBufferWrite(Symbols.singleLineFromRight);
+
+    for (int i = 0; i < _columnsWidth.length; i++) {
+      if (i > 0) {
+        _outputBufferWrite(Symbols.singleLineCross);
+      }
+      _outputBufferWrite(
+          Symbols.singleLineHorizontal,
+          _columnsWidth[i] + 2);
+    }
+
+    _outputBufferWriteLn(Symbols.singleLineFromLeft);
+  }
+
+  void _tableHeaderCellSeparator() {
+    _outputBufferWrite(Symbols.space);
+    _outputBufferWrite(Symbols.singleLineVertical);
+    _outputBufferWrite(Symbols.space);
+  }
+
+  void _tableCellSeparator() {
+    _outputBufferWrite(Symbols.space);
+    _outputBufferWrite(Symbols.singleLineVertical);
+    _outputBufferWrite(Symbols.space);
+  }
+
+  void _tableRow(List<R> row) {
+    _outputBufferWrite(Symbols.singleLineVertical);
+    _outputBufferWrite(Symbols.space);
+
+    for (int i = 0; i < row.length; i++) {
+      _currentColumn = i;
+      _tableCell(row[i].toString());
+      if (i < row.length - 1) {
+        _tableCellSeparator();
+      }
+    }
+
+    _outputBufferWrite(Symbols.space);
+    _outputBufferWriteLn(Symbols.singleLineVertical);
+  }
+
+  void _tableRowSeparator() {
+
+    _outputBufferWrite(Symbols.singleLineFromRight);
+
+    for (int i = 0; i < _columnsWidth.length; i++) {
+      if (i > 0) {
+        _outputBufferWrite(Symbols.singleLineCross);
+      }
+      _outputBufferWrite(
+          Symbols.singleLineHorizontal,
+          _columnsWidth[i] + 2);
+    }
+
+    _outputBufferWriteLn(Symbols.singleLineFromLeft);
+  }
+
+  Table crop(int startRow, int numRow) {
+
+    startRow *= size;
+    numRow *= size;
+
+    List sublist = data.sublist(startRow, startRow + numRow);
+
+    data.clear();
+    data.addAll(sublist);
+
+    return this;
+  }
+
+  Table clone() {
+    var clone = new Table(size);
+    clone.columns.addAll(columns);
+    clone.data.addAll(data);
+    return clone;
+  }
+
+}
\ No newline at end of file
diff --git a/dart/dlog/lib/src/displays/Time.dart b/dart/dlog/lib/src/displays/Time.dart
new file mode 100644
index 0000000..0008a06
--- /dev/null
+++ b/dart/dlog/lib/src/displays/Time.dart
@@ -0,0 +1,84 @@
+part of dlog;
+
+class Time extends _Display {
+
+  final int _DESCRIPTION = 0;
+  final int _INIT = 1;
+  final int _FINAL = 2;
+
+  String description;
+
+  List<String> _indexes = new List<String>();
+  List<List> _checks = new List<List>();
+
+  Time([this.description]);
+
+  checkBegin(String description) {
+
+    if (_indexes.contains(description)) {
+      throw new Exception("dlog.Time: check with description \"$description\" has been initialized");
+    }
+
+    _indexes.add(description);
+    _checks.add([description, new DateTime.now(), null]);
+
+  }
+
+  checkEnd(String description) {
+
+    int index = _indexes.indexOf(description);
+
+    if (index == -1) {
+      throw new Exception("dlog.Time: begin check with description \"$description\" is not found");
+    }
+
+    if (_checks[index][_FINAL] is DateTime) {
+      throw new Exception("dlog.Time: check with description \"$description\" has been completed");
+    }
+
+    _checks[index][_FINAL] = new DateTime.now();
+  }
+
+  checkFunc(String description, Function checkit) {
+    checkBegin(description);
+    checkit();
+    checkEnd(description);
+  }
+
+  @override
+  _initBuffer() {
+
+    if (description is String) {
+      _outputBufferWriteLn(Symbols.singleLineHorizontal, 48);
+      _outputBufferWrite(Symbols.space);
+      _outputBufferWriteLn(this.description);
+    }
+
+    _outputBufferWrite(Symbols.singleLineHorizontal, 5);
+    _outputBufferWrite(Symbols.singleLineFromBottom);
+    _outputBufferWrite(Symbols.singleLineHorizontal, 16);
+    _outputBufferWrite(Symbols.singleLineFromBottom);
+    _outputBufferWriteLn(Symbols.singleLineHorizontal, 25);
+
+    for (int i = 0; i < _indexes.length; i++) {
+      Duration diff = _checks[i][_FINAL].difference(_checks[i][_INIT]);
+      String num = (i + 1).toString();
+      _outputBufferWrite(Symbols.space);
+      _outputBufferWrite(num);
+      _outputBufferWrite(Symbols.space, 4 - num.length);
+      _outputBufferWrite(Symbols.singleLineVertical);
+      _outputBufferWrite(Symbols.space);
+      _outputBufferWrite(diff.toString());
+      _outputBufferWrite(Symbols.space);
+      _outputBufferWrite(Symbols.singleLineVertical);
+      _outputBufferWrite(Symbols.space);
+      _outputBufferWriteLn(_checks[i][_DESCRIPTION]);
+    }
+
+    _outputBufferWrite(Symbols.singleLineHorizontal, 5);
+    _outputBufferWrite(Symbols.singleLineFromTop);
+    _outputBufferWrite(Symbols.singleLineHorizontal, 16);
+    _outputBufferWrite(Symbols.singleLineFromTop);
+    _outputBufferWriteLn(Symbols.singleLineHorizontal, 25);
+  }
+}
\ No newline at end of file
diff --git a/dart/dlog/lib/src/displays/Tree.dart b/dart/dlog/lib/src/displays/Tree.dart
new file mode 100644
index 0000000..8544b41
--- /dev/null
+++ b/dart/dlog/lib/src/displays/Tree.dart
@@ -0,0 +1,78 @@
+part of dlog;
+
+class Tree<E> extends _Display {
+
+  List<E> _items = new List<E>();
+  List _stack = new List();
+
+  int _depth = -1;
+
+  String title;
+
+  Tree([this.title]);
+
+  add(E text) {
+    _stack.last.add(_items.length);
+    _items.add(text);
+  }
+
+  openGroup() {
+
+    if (_stack.length == 0) {
+      _stack.add(_stack);
+    }
+
+    List group = new List();
+    _stack.last.add(group);
+    _stack.add(group);
+  }
+
+  closeGroup() {
+    _stack.removeLast();
+  }
+
+  _group(List group) {
+
+    _depth++;
+
+    group.forEach((item){
+
+      bool first = group.first == item,
+           last = group.last == item,
+           isGroup = item is List;
+
+      if (isGroup) {
+        _group(item);
+
+      } else {
+
+        _outputBufferWrite(Symbols.singleLineVertical + Symbols.space, _depth);
+
+        if (last) {
+          _outputBufferWrite(Symbols.singleLineAngleBottomLeft);
+        } else {
+          _outputBufferWrite(Symbols.singleLineFromRight);
+        }
+
+        _outputBufferWrite(Symbols.space);
+        _outputBufferWriteLn(_items[item].toString());
+      }
+    });
+
+    _depth--;
+  }
+
+  _item(E item) {
+
+  }
+
+  @override
+  _initBuffer() {
+
+    if (title is String) {
+      _outputBufferWriteLn(title);
+    }
+    _group(_stack.sublist(1));
+
+  }
+}
\ No newline at end of file
diff --git a/dart/dlog/pubspec.yaml b/dart/dlog/pubspec.yaml
new file mode 100644
index 0000000..793f6f4
--- /dev/null
+++ b/dart/dlog/pubspec.yaml
@@ -0,0 +1,10 @@
+name: dlog
+version: 0.0.5
+author: Viktor Dakalov <atlantidu@gmail.com>
+description: A useful library for output structured information of debugging into console
+homepage: https://github.com/vdakalov/dlog
+environment:
+  sdk: '>=1.0.0 <2.0.0'
+documentation: https://github.com/vdakalov/dlog
+dev_dependencies:
+  unittest: any
diff --git a/dart/dlog/test/json_test.dart b/dart/dlog/test/json_test.dart
new file mode 100644
index 0000000..ae9bee0
--- /dev/null
+++ b/dart/dlog/test/json_test.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+import 'package:unittest/unittest.dart';
+import 'package:dlog/dlog.dart';
+
+main() {
+
+  var json = JSON.decode('{"name": "Nikolay", "age": 21, "roles": ["admin", "developer"]}');
+
+  test("Output", (){
+
+    var debug = new Json(data: json, title: "Json simple"),
+        output = [
+          'Json simple',
+          '{',
+          '  name: "Nikolay",',
+          '  age: 21,',
+          '  roles: [',
+          '    0: "admin",',
+          '    1: "developer"',
+          '  ]',
+          '}', ''];
+
+    expect(debug.toString(), output.join(debug.endOfLineUnicode));
+
+  });
+
+  test("Max string length", (){
+
+    var debug = new Json(data: json, title: "Json simple"),
+        output = [
+          'Json simple',
+          '{',
+          '  name: "Nikol...",',
+          '  age: 21,',
+          '  roles: [',
+          '    0: "admin",',
+          '    1: "devel..."',
+          '  ]',
+          '}', ''];
+
+    debug.maxStringLen = 5;
+
+    expect(debug.toString(), output.join(debug.endOfLineUnicode));
+
+  });
+
+  test("Custom parser", (){
+
+    var debug = new Json(data: json, title: "Json simple"),
+        output = [
+          'Json simple',
+          '{',
+          '  name: "Nikolay",',
+          '  age: <int> 21,',
+          '  roles: [',
+          '    0: "admin",',
+          '    1: "developer"',
+          '  ]',
+          '}', ''];
+
+    debug.parsers["int"] = (int num) => "<int> $num" ;
+
+    expect(debug.toString(), output.join(debug.endOfLineUnicode));
+
+  });
+
+}
\ No newline at end of file
diff --git a/dart/dlog/test/table_test.dart b/dart/dlog/test/table_test.dart
new file mode 100644
index 0000000..582b58d
--- /dev/null
+++ b/dart/dlog/test/table_test.dart
@@ -0,0 +1,108 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+import 'package:unittest/unittest.dart';
+import 'package:dlog/dlog.dart';
+
+gen(int rows, int cols, List target) {
+  cols = rows * cols;
+  while (rows-- > 0) {
+    while (cols-- > 0) {
+      target.add("$rows-$cols");
+    }
+  }
+}
+
+main() {
+
+  test("Table 3x10", (){
+
+    List<String> output = [
+      '┌──────┬──────┬──────┐',
+      '│ 9-29 │ 9-28 │ 9-27 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-26 │ 9-25 │ 9-24 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-23 │ 9-22 │ 9-21 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-20 │ 9-19 │ 9-18 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-17 │ 9-16 │ 9-15 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-14 │ 9-13 │ 9-12 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-11 │ 9-10 │ 9-9  │',
+      '├──────┼──────┼──────┤',
+      '│ 9-8  │ 9-7  │ 9-6  │',
+      '├──────┼──────┼──────┤',
+      '│ 9-5  │ 9-4  │ 9-3  │',
+      '├──────┼──────┼──────┤',
+      '│ 9-2  │ 9-1  │ 9-0  │',
+      '└──────┴──────┴──────┘',
+      ''
+    ];
+
+    Table table = new Table(3);
+    gen(10, table.size, table.data);
+    expect(table.toString(), output.join(table.endOfLineUnicode));
+
+  });
+
+  test("types", (){
+
+    Table table = new Table<String, int>(3);
+
+    // wrong type for columns
+
+    table.columns.add("Number");
+    expect(table.columns[0], "Number");
+
+    bool wrongColumnType = false;
+    try { table.columns.add(10); } catch (e) { wrongColumnType = true; }
+    expect(wrongColumnType, isTrue);
+
+    // wrong type for data
+
+    table.data.add(10);
+    expect(table.data[0], 10);
+
+    bool wrongDataType = false;
+    try { table.data.add(true); } catch (e) { wrongDataType = true; }
+    expect(wrongDataType, isTrue);
+
+  });
+
+  test("crop method", (){
+
+    List<String> output = [
+      '┌──────┬──────┬──────┐',
+      '│ 9-23 │ 9-22 │ 9-21 │',
+      '├──────┼──────┼──────┤',
+      '│ 9-20 │ 9-19 │ 9-18 │',
+      '└──────┴──────┴──────┘',
+      ''
+    ];
+
+    Table table = new Table(3);
+    gen(10, table.size, table.data);
+    expect(table.crop(2, 2).toString(), output.join(table.endOfLineUnicode));
+
+  });
+
+  test("clone method", (){
+
+    Table table = new Table(1);
+    table.data.add(1);
+    table.data.add(2);
+
+    Table clone = table.clone();
+
+    expect(clone, isNot(table));
+    expect(clone, new isInstanceOf<Table>());
+    expect(clone.size, table.size);
+    expect(clone.columns, equals(table.columns));
+    expect(clone.data, equals(table.data));
+
+  });
+
+}
diff --git a/dart/dlog/test/time_test.dart b/dart/dlog/test/time_test.dart
new file mode 100644
index 0000000..6cc2482
--- /dev/null
+++ b/dart/dlog/test/time_test.dart
@@ -0,0 +1,11 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+import 'package:unittest/unittest.dart';
+import 'package:dlog/dlog.dart';
+
+main() {
+
+  // coming soon...
+
+}
\ No newline at end of file
diff --git a/dart/dlog/test/tree_test.dart b/dart/dlog/test/tree_test.dart
new file mode 100644
index 0000000..a3bf64b
--- /dev/null
+++ b/dart/dlog/test/tree_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2015, Viktor Dakalov. All rights reserved. Use of this source code
+// is governed by a BSD-style license that can be found in the LICENSE file.
+
+import 'package:unittest/unittest.dart';
+import 'package:dlog/dlog.dart';
+
+main() {
+
+  test("Tree flat", (){
+
+    var tree = new Tree("Tree flat");
+    var output = [
+      'Tree flat',
+      '│ ├ 0',
+      '│ ├ 1',
+      '│ ├ 2',
+      '│ ├ 3',
+      '│ ├ 4',
+      '│ ├ 5',
+      '│ ├ 6',
+      '│ ├ 7',
+      '│ ├ 8',
+      '│ └ 9',
+      ''
+    ];
+
+    tree.openGroup();
+    for (int i = 0; i < 10; i++) {
+      tree.add(i);
+    }
+    tree.closeGroup();
+
+    expect(tree.toString(), output.join(tree.endOfLineUnicode));
+
+  });
+
+  test("Tree 3x3", (){
+
+    var tree = new Tree("Tree 3x3");
+    var output = [
+      'Tree 3x3\n'
+      '│ ├ 0\n'
+      '│ │ ├ 0x0\n'
+      '│ │ ├ 0x1\n'
+      '│ │ └ 0x2\n'
+      '│ ├ 1\n'
+      '│ │ ├ 1x0\n'
+      '│ │ ├ 1x1\n'
+      '│ │ └ 1x2\n'
+      '│ ├ 2\n'
+      '│ │ ├ 2x0\n'
+      '│ │ ├ 2x1\n'
+      '│ │ └ 2x2\n'
+      ''
+    ];
+
+    tree.openGroup();
+
+    for (int i = 0; i < 3; i++) {
+      tree.add(i);
+      tree.openGroup();
+      for (int j = 0; j < 3; j++) {
+        tree.add("${i}x${j}");
+      }
+      tree.closeGroup();
+    }
+
+    tree.closeGroup();
+
+    // TODO join separator is not working on unknown reason
+    expect(tree.toString(), output.join());
+
+  });
+
+}
\ No newline at end of file