Skip to content

Commit 685357f

Browse files
committed
doodle2code
0 parents  commit 685357f

37 files changed

+1088
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

compiler/Sketch2Code_v2_Test_1.gui

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
header{
2+
btn-inactive,btn-inactive,btn-inactive,btn-inactive
3+
}
4+
row{
5+
quadruple{
6+
small-title,text,btn-orange
7+
}
8+
quadruple{
9+
small-title,text,btn-orange
10+
}
11+
quadruple{
12+
small-title,text,btn-orange
13+
}
14+
quadruple{
15+
small-title,text,btn-orange
16+
}
17+
}
18+
row{
19+
double{
20+
small-title,text,btn-orange
21+
}
22+
double{
23+
small-title,text,btn-orange
24+
}
25+
}
26+
row{
27+
quadruple{
28+
small-title,text,btn-orange
29+
}
30+
quadruple{
31+
small-title,text,btn-orange
32+
}
33+
quadruple{
34+
small-title,text,btn-orange
35+
}
36+
quadruple{
37+
small-title,text,btn-orange
38+
}
39+
}

compiler/Sketch2Code_v3_Test_1.gui

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
header{
2+
quadruple{
3+
btn-active
4+
}
5+
double{
6+
btn-inactive
7+
}
8+
quadruple{
9+
btn-active
10+
}
11+
}
12+
container{
13+
row{
14+
single{
15+
empty
16+
}
17+
}
18+
row{
19+
single{
20+
big-text,text
21+
}
22+
}
23+
}

compiler/android-compiler.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env python
2+
from __future__ import print_function
3+
__author__ = 'Tony Beltramelli - www.tonybeltramelli.com'
4+
5+
import sys
6+
7+
from os.path import basename
8+
from classes.Utils import *
9+
from classes.Compiler import *
10+
11+
if __name__ == "__main__":
12+
argv = sys.argv[1:]
13+
length = len(argv)
14+
if length != 0:
15+
input_file = argv[0]
16+
else:
17+
print("Error: not enough argument supplied:")
18+
print("android-compiler.py <input file>")
19+
exit(0)
20+
21+
TEXT_PLACE_HOLDER = "[TEXT]"
22+
ID_PLACE_HOLDER = "[ID]"
23+
24+
dsl_path = "assets/android-dsl-mapping.json"
25+
compiler = Compiler(dsl_path)
26+
27+
28+
def render_content_with_text(key, value):
29+
value = value.replace(TEXT_PLACE_HOLDER, Utils.get_random_text(length_text=5, space_number=0))
30+
while value.find(ID_PLACE_HOLDER) != -1:
31+
value = value.replace(ID_PLACE_HOLDER, Utils.get_android_id(), 1)
32+
return value
33+
34+
file_uid = basename(input_file)[:basename(input_file).find(".")]
35+
path = input_file[:input_file.find(file_uid)]
36+
37+
input_file_path = "{}{}.gui".format(path, file_uid)
38+
output_file_path = "{}{}.xml".format(path, file_uid)
39+
40+
compiler.compile(input_file_path, output_file_path, rendering_function=render_content_with_text)
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"opening-tag": "{",
3+
"closing-tag": "}",
4+
"body": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n android:id=\"@+id/container\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:orientation=\"vertical\"\n tools:context=\"com.tonybeltramelli.android_gui.MainActivity\">\n {}\n</LinearLayout>\n",
5+
"stack": "<FrameLayout android:id=\"@+id/content\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" android:layout_weight=\"1\" android:padding=\"10dp\">\n <LinearLayout android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" android:orientation=\"vertical\">\n {}\n </LinearLayout>\n</FrameLayout>",
6+
"row": "<LinearLayout android:layout_width=\"match_parent\" android:layout_height=\"wrap_content\" android:orientation=\"horizontal\" android:paddingTop=\"10dp\" android:paddingBottom=\"10dp\" android:weightSum=\"1\">\n{}\n</LinearLayout>",
7+
"label": "<TextView android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:text=\"[TEXT]\" android:textAppearance=\"@style/TextAppearance.AppCompat.Body2\"/>\n",
8+
"btn": "<Button android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:text=\"[TEXT]\"/>",
9+
"slider": "<SeekBar android:id=\"@+id/[ID]\" style=\"@style/Widget.AppCompat.SeekBar.Discrete\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:layout_weight=\"0.9\" android:max=\"10\" android:progress=\"5\"/>",
10+
"check": "<CheckBox android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:paddingRight=\"10dp\" android:text=\"[TEXT]\"/>",
11+
"radio": "<RadioButton android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:paddingRight=\"10dp\" android:text=\"[TEXT]\"/>",
12+
"switch": "<Switch android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:paddingRight=\"10dp\" android:text=\"[TEXT]\"/>",
13+
"footer": "<LinearLayout android:layout_width=\"match_parent\" android:layout_height=\"wrap_content\" android:orientation=\"horizontal\" android:weightSum=\"1\">\n {}\n</LinearLayout>",
14+
"btn-home": "<Button android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:background=\"#0ffffff\" android:layout_weight=\"1\" android:drawableBottom=\"@drawable/ic_home_black_24dp\" android:text=\"\"/>",
15+
"btn-dashboard": "<Button android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:background=\"#0ffffff\" android:layout_weight=\"1\" android:drawableBottom=\"@drawable/ic_dashboard_black_24dp\" android:text=\"\"/>",
16+
"btn-notifications": "<Button android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:background=\"#0ffffff\" android:layout_weight=\"1\" android:drawableBottom=\"@drawable/ic_notifications_black_24dp\" android:text=\"\"/>",
17+
"btn-search": "<Button android:id=\"@+id/[ID]\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:background=\"#0ffffff\" android:layout_weight=\"1\" android:drawableBottom=\"?android:attr/actionModeWebSearchDrawable\" android:text=\"\"/>"
18+
}

compiler/assets/ios-dsl-mapping.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"opening-tag": "{",
3+
"closing-tag": "}",
4+
"body": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"11201\" systemVersion=\"15G1217\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\">\n <dependencies>\n <deployment identifier=\"iOS\"/>\n <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"11161\"/>\n <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n </dependencies>\n <scenes>\n <!--View Controller-->\n <scene sceneID=\"qAw-JF-viq\">\n <objects>\n <viewController id=\"[ID]\" sceneMemberID=\"viewController\">\n <layoutGuides>\n <viewControllerLayoutGuide type=\"top\" id=\"[ID]\"/>\n <viewControllerLayoutGuide type=\"bottom\" id=\"[ID]\"/>\n </layoutGuides>\n <view key=\"view\" contentMode=\"center\" id=\"[ID]\">\n <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n <subviews>\n {}\n </subviews>\n <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n </view>\n </viewController>\n <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"[ID]\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n </objects>\n <point key=\"canvasLocation\" x=\"20\" y=\"95.802098950524751\"/>\n </scene>\n </scenes>\n</document>\n",
5+
"stack": "<stackView opaque=\"NO\" contentMode=\"center\" fixedFrame=\"YES\" axis=\"vertical\" alignment=\"center\" spacing=\"10\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" minX=\"16\" minY=\"20\" width=\"343\" height=\"440\"/>\n <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n <subviews>\n {}\n </subviews>\n <color key=\"backgroundColor\" red=\"0.80000001190000003\" green=\"0.80000001190000003\" blue=\"0.80000001190000003\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n</stackView>",
6+
"row": "<view contentMode=\"center\" ambiguous=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"343\" height=\"65\"/>\n <subviews>\n <stackView opaque=\"NO\" contentMode=\"center\" fixedFrame=\"YES\" spacing=\"30\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" minX=\"8\" minY=\"6\" width=\"337\" height=\"52\"/>\n <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMaxY=\"YES\"/>\n <subviews>\n {}\n </subviews>\n </stackView>\n </subviews>\n <color key=\"backgroundColor\" red=\"0.9\" green=\"0.9\" blue=\"0.9\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n</view>",
7+
"img": "<imageView userInteractionEnabled=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" ambiguous=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"36\" height=\"36\"/>\n <color key=\"backgroundColor\" red=\"0.40000000600000002\" green=\"0.40000000600000002\" blue=\"1\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n</imageView>",
8+
"label": "<label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" ambiguous=\"YES\" text=\"[TEXT]\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"255\" height=\"52\"/>\n <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n <nil key=\"textColor\"/>\n <nil key=\"highlightedColor\"/>\n</label>",
9+
"switch": "<switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" ambiguous=\"YES\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"51\" height=\"31\"/>\n</switch>",
10+
"slider": "<slider opaque=\"NO\" contentMode=\"scaleToFill\" ambiguous=\"YES\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" value=\"0.5\" minValue=\"0.0\" maxValue=\"1\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"142\" height=\"31\"/>\n</slider>",
11+
"btn-add": "<button opaque=\"NO\" contentMode=\"scaleToFill\" ambiguous=\"YES\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" buttonType=\"contactAdd\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" width=\"22\" height=\"22\"/>\n</button>",
12+
"footer": "<tabBar contentMode=\"scaleToFill\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"[ID]\">\n <frame key=\"frameInset\" height=\"49\"/>\n <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" flexibleMinY=\"YES\"/>\n <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"calibratedWhite\"/>\n <items>\n {}\n </items>\n</tabBar>",
13+
"btn-search": "<tabBarItem systemItem=\"search\" id=\"[ID]\"/>",
14+
"btn-contact": "<tabBarItem systemItem=\"contacts\" id=\"[ID]\"/>",
15+
"btn-download": "<tabBarItem systemItem=\"downloads\" id=\"[ID]\"/>",
16+
"btn-more": "<tabBarItem systemItem=\"more\" id=\"[ID]\"/>"
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"opening-tag": "{",
3+
"closing-tag": "}",
4+
"body": "<html>\n <header>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">\n<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css\" integrity=\"sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp\" crossorigin=\"anonymous\">\n<style>\n.header{margin:20px 0}nav ul.nav-pills li{background-color:#333;border-radius:4px;margin-right:10px}.col-lg-3{width:24%;margin-right:1.333333%}.col-lg-6{width:49%;margin-right:2%}.col-lg-12,.col-lg-3,.col-lg-6{margin-bottom:20px;border-radius:6px;background-color:#f5f5f5;padding:20px}.row .col-lg-3:last-child,.row .col-lg-6:last-child{margin-right:0}footer{padding:20px 0;text-align:center;border-top:1px solid #bbb}\n</style>\n <title>Scaffold</title>\n </header>\n <body>\n <main class=\"container\">\n {}\n <footer class=\"footer\">\n <p>&copy; Tony Beltramelli 2017</p>\n </footer>\n </main>\n <script src=\"js/jquery.min.js\"></script>\n <script src=\"js/bootstrap.min.js\"></script>\n </body>\n</html>\n",
5+
"header": "<div class=\"header clearfix\">\n <nav>\n <ul class=\"nav nav-pills pull-left\">\n {}\n </ul>\n </nav>\n</div>\n",
6+
"btn-active": "<li class=\"active\"><a href=\"#\">[]</a></li>\n",
7+
"btn-inactive": "<li><a href=\"#\">[]</a></li>\n",
8+
"row": "<div class=\"row\">{}</div>\n",
9+
"single": "<div class=\"col-lg-12\">\n{}\n</div>\n",
10+
"double": "<div class=\"col-lg-6\">\n{}\n</div>\n",
11+
"quadruple": "<div class=\"col-lg-3\">\n{}\n</div>\n",
12+
"btn-green": "<a class=\"btn btn-success\" href=\"#\" role=\"button\">[]</a>\n",
13+
"btn-orange": "<a class=\"btn btn-warning\" href=\"#\" role=\"button\">[]</a>\n",
14+
"btn-red": "<a class=\"btn btn-danger\" href=\"#\" role=\"button\">[]</a>",
15+
"big-title": "<h2>[]</h2>",
16+
"small-title": "<h4>[]</h4>",
17+
"text": "<p>[]</p>\n"
18+
}

compiler/assets/web-dsl-mapping.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"opening-tag": "{",
3+
"closing-tag": "}",
4+
"css-file-name": "+++",
5+
"body": "<html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6\" crossorigin=\"anonymous\">\n <link rel=\"stylesheet\" href=\"styles/common.css\" type=\"text/css\">\n <title>Scaffold</title>\n </head>\n <body>\n <aside></aside>\n <div class=\"specific\">{}</div>\n <script src=\"js/jquery.min.js\"></script>\n <script src=\"js/bootstrap.min.js\"></script>\n <script src=\"js/main.js\"></script>\n </body>\n</html>\n",
6+
"header": "<nav class=\"navbar navbar-expand-lg navbar-light\">\n <div class=\"container\">\n <div class=\"collapse navbar-collapse\">\n <ul class=\"navbar-nav mb-2 mb-lg-0\">\n {}\n </ul>\n </div>\n </div>\n</nav>\n",
7+
"header-right": "<nav class=\"navbar navbar-expand-lg navbar-light\">\n <div class=\"container\">\n <a class=\"navbar-brand\" href=\"#\">Logo</a>\n <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#demo\" aria-controls=\"demo\" aria-expanded=\"false\"><span class=\"navbar-toggler-icon\"></span></button>\n <div class=\"collapse navbar-collapse justify-content-end\">\n <ul class=\"navbar-nav mb-2 mb-lg-0\">\n {}\n </ul>\n </div>\n </div>\n</nav>\n",
8+
"btn-active": "<li class=\"nav-item\"><a class=\"nav-link active\" href=\"#\">[]</a></li>\n",
9+
"btn-inactive": "<li class=\"nav-item\"><a class=\"nav-link\" href=\"#\">[]</a></li>\n",
10+
"container": "<main class=\"container\">\n{}\n</main>\n",
11+
"row": "<div class=\"row\">{}</div>\n",
12+
"single": "<div class=\"col-lg-12\">\n{}\n</div>\n",
13+
"double": "<div class=\"col-lg-6\">\n{}\n</div>\n",
14+
"quadruple": "<div class=\"col-lg-3\">\n{}\n</div>\n",
15+
"btn": "<a class=\"btn btn-warning\" href=\"#\" role=\"button\"><span>[]</span></a>\n",
16+
"btn-green": "<a class=\"btn btn-success\" href=\"#\" role=\"button\">[]</a>\n",
17+
"btn-orange": "<a class=\"btn btn-warning\" href=\"#\" role=\"button\">[]</a>\n",
18+
"btn-red": "<a class=\"btn btn-danger\" href=\"#\" role=\"button\">[]</a>\n",
19+
"big-title": "<h1 class=\"display-1 mb-4\">[]</h1>\n",
20+
"small-title": "<h1 class=\"display-6 mb-4\">[]</h1>\n",
21+
"big-text": "<h1 class=\"mb-4\">[]</h1>",
22+
"small-text": "<h3 class=\"mb-4\">[]</h3>",
23+
"text": "<p class=\"mb-4\">[]</p>\n",
24+
"img": "<img src=\"[]\"/>",
25+
"empty": ""
26+
}

compiler/classes/Compiler.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python
2+
__author__ = 'Tony Beltramelli - www.tonybeltramelli.com'
3+
4+
import json
5+
from classes.Node import *
6+
7+
8+
class Compiler:
9+
def __init__(self, dsl_mapping_file_path):
10+
with open(dsl_mapping_file_path) as data_file:
11+
self.dsl_mapping = json.load(data_file)
12+
13+
self.opening_tag = self.dsl_mapping["opening-tag"]
14+
self.closing_tag = self.dsl_mapping["closing-tag"]
15+
self.css_file_name = self.dsl_mapping["css-file-name"]
16+
self.content_holder = self.opening_tag + self.closing_tag
17+
18+
self.root = Node("body", None, self.content_holder)
19+
20+
def compile(self, input_file_path, output_file_path, rendering_function=None):
21+
dsl_file = open(input_file_path)
22+
current_parent = self.root
23+
24+
for token in dsl_file:
25+
token = token.replace(" ", "").replace("\n", "")
26+
27+
if token.find(self.opening_tag) != -1:
28+
token = token.replace(self.opening_tag, "")
29+
30+
element = Node(token, current_parent, self.content_holder)
31+
current_parent.add_child(element)
32+
current_parent = element
33+
elif token.find(self.closing_tag) != -1:
34+
current_parent = current_parent.parent
35+
else:
36+
tokens = token.split(",")
37+
for t in tokens:
38+
element = Node(t, current_parent, self.content_holder)
39+
current_parent.add_child(element)
40+
41+
output_html = self.root.render(self.dsl_mapping, rendering_function=rendering_function)
42+
with open(output_file_path, 'w') as output_file:
43+
output_file.write(output_html)
44+
45+
# Checking for Errors
46+
if output_html is None:
47+
return "Parsing Error"
48+
49+
with open(output_file_path, 'w') as output_file:
50+
output_file.truncate(0)
51+
output_file.write(output_html)
52+
53+
output_file.close()
54+
return output_html

compiler/classes/Node.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python
2+
from __future__ import print_function
3+
__author__ = 'Tony Beltramelli - www.tonybeltramelli.com'
4+
5+
6+
class Node:
7+
def __init__(self, key, parent_node, content_holder):
8+
self.key = key
9+
self.parent = parent_node
10+
self.children = []
11+
self.content_holder = content_holder
12+
13+
def add_child(self, child):
14+
self.children.append(child)
15+
16+
def show(self):
17+
print(self.key)
18+
for child in self.children:
19+
child.show()
20+
21+
def render(self, mapping, rendering_function=None):
22+
content = ""
23+
for child in self.children:
24+
content += child.render(mapping, rendering_function)
25+
26+
value = mapping[self.key]
27+
if rendering_function is not None:
28+
value = rendering_function(self.key, value)
29+
30+
if len(self.children) != 0:
31+
value = value.replace(self.content_holder, content)
32+
33+
return value

0 commit comments

Comments
 (0)