root/trunk/src/semitwist/treeout.d

Revision 225, 9.0 kB (checked in by Abscissa, 1 year ago)

(NB) treeout: Fixed some trouble compiling.

  • Property svn:eol-style set to native
Line 
1 // SemiTwist Library
2 // Written in the D programming language.
3
4 module semitwist.treeout;
5
6 import std.array;
7 import std.conv;
8 import std.range : ElementType;
9 import std.regex;
10 import std.stdio;
11 import std.string;
12 import std.traits;
13
14 import semitwist.util.all;
15
16 string replicate(string str, int num)
17 {
18     if(num < 1)
19         return "";
20    
21     string ret = "";
22     foreach(i; 0..num)
23         ret ~= str;
24        
25     return ret;
26 }
27
28 abstract class TreeFormatter
29 {
30     string fullIndent(int nodeDepth)
31     {
32         return strip()? "" : indent().replicate(nodeDepth);
33     }
34    
35     string newline()
36     {
37         return strip()? "" : "\n";
38     }
39    
40     string indent();
41     bool strip();
42     string processData(string content, int nodeDepth);
43     string processComment(string content, int nodeDepth);
44     string processAttribute(string name, string value, int nodeDepth);
45     string processNode(string name, string attributes, string content, int nodeDepth);
46     string reduceAttributes(string[] attributes, int nodeDepth);
47     string reduceNodes(string[] nodes, int nodeDepth);
48     string finalize(string content);
49 }
50
51 class XMLFormatter(bool _strip, string _indent="\t") : TreeFormatter
52 {
53     string toValidName(string str)
54     {
55         str = replace(str, regex("[^a-zA-Z0-9]"), "_");
56 /+      std.algorithm.map!(
57             (char a)
58             { return inPattern(a, [digits, letters])? a : '_'; }
59         )(str);
60 +//+        str.map
61         (
62             (char a)
63             { return inPattern(a, [digits, letters])? a : '_'; }
64         );+/
65
66         if(str.length > 0 && !inPattern(str[0], [digits, letters]) && str[0] != '_')
67             str = "_"~str;
68        
69         return str;
70     }
71    
72     override string indent()
73     {
74         return _indent;
75     }
76    
77     override bool strip()
78     {
79         return _strip;
80     }
81    
82     override string processData(string content, int nodeDepth)
83     {
84         string str = fullIndent(nodeDepth);
85         str ~= "<![CDATA[";
86         str ~= content;
87         str ~= "]]>";
88         str ~= newline;
89         return str;
90     }
91    
92     override string processComment(string content, int nodeDepth)
93     {
94         string str = fullIndent(nodeDepth);
95         str ~= "<!--";
96         str ~= content;
97         str ~= "-->";
98         str ~= newline;
99         return str;
100     }
101    
102     override string processAttribute(string name, string value, int nodeDepth)
103     {
104         string str = " ";
105         str ~= toValidName(name);
106         str ~= `="`;
107         str ~= value;
108         str ~= `"`;
109         return str;
110     }
111    
112     override string processNode(string name, string attributes, string content, int nodeDepth)
113     {
114         auto formatStr =
115             (content=="")?
116             "%1$s<%3$s%4$s />%2$s"
117             :
118             ("%1$s<%3$s%4$s>%2$s"~
119             "%5$s"~
120             "%1$s</%3$s>%2$s");
121            
122         return formatStr.format(fullIndent(nodeDepth), newline(), toValidName(name), attributes, content);
123     }
124    
125     override string reduceAttributes(string[] attributes, int nodeDepth)
126     {
127         string str;
128         foreach(attr; attributes)
129             str ~= attr;
130         return str;
131         //return reduce!(`a~b`)(attributes);
132 //      return attributes.reduce!(`a~" "~b`)(); // Don't work
133     }
134    
135     override string reduceNodes(string[] nodes, int nodeDepth)
136     {
137         string str;
138         foreach(node; nodes)
139             str ~= node;
140         return str;
141         //return reduce!(`a~b`)(nodes);
142     }
143    
144     override string finalize(string content)
145     {
146         return content;
147     }
148 }
149
150 class JSONFormatter(bool _strip, string _indent="\t") : TreeFormatter
151 {
152     override string fullIndent(int nodeDepth)
153     {
154         return super.fullIndent(nodeDepth+1);
155     }
156
157     override string indent()
158     {
159         return _indent;
160     }
161    
162     override bool strip()
163     {
164         return _strip;
165     }
166    
167     string processString(string content)
168     {
169         content = content.replace(`\`, `\\`).replace(`"`, `\"`);
170         string str = `"`;
171         str ~= content;
172         str ~= `"`;
173         return str;
174     }
175    
176     string processList(string[] elements, int nodeDepth)
177     {
178         if(elements.length==0)
179             return "";
180        
181         string str;
182         string sep = ", ";
183         sep ~= newline;
184         sep ~= fullIndent(nodeDepth);
185         foreach(i, elem; elements)
186         {
187             if(i != 0)
188                 str ~= sep;
189             str ~= elem;
190         }
191         return str;
192     }
193    
194     string processPair(string name, string content)
195     {
196         string str = processString(name);
197         str ~= ": ";
198         str ~= content;
199         return str;
200     }
201    
202     override string processComment(string content, int nodeDepth)
203     {
204         return "";
205     }
206
207     override string processData(string content, int nodeDepth)
208     {
209         return processString(content);
210     }
211    
212     override string processAttribute(string name, string value, int nodeDepth)
213     {
214         return processPair(name, processString(value));
215     }
216    
217     override string processNode(string name, string attributes, string content, int nodeDepth)
218     {
219         return processNode(name, attributes, content, nodeDepth, false);
220     }
221    
222     string processNode(string name, string attributes, string content, int nodeDepth, bool nameless)
223     {
224         string attrAndContent = "";
225         if(attributes != "" && content == "")
226         {
227             attrAndContent = fullIndent(nodeDepth+1);
228             attrAndContent ~= attributes;
229             attrAndContent ~= newline;
230         }
231         else if(attributes == "" && content != "")
232         {
233             attrAndContent = fullIndent(nodeDepth+1);
234             attrAndContent ~= content;
235             attrAndContent ~= newline;
236         }
237         else if(attributes != "" && content != "")
238         {
239             attrAndContent = fullIndent(nodeDepth+1);
240             attrAndContent ~= attributes;
241             attrAndContent ~= ", ";
242             attrAndContent ~= newline;
243             attrAndContent ~= fullIndent(nodeDepth+1);
244             attrAndContent ~= content;
245             attrAndContent ~= newline;
246         }
247
248         name = nameless? "" : processString(name)~": ";
249        
250         string str = name;
251         str ~= "{";
252         str ~= newline;
253         str ~= attrAndContent;
254         str ~= fullIndent(nodeDepth);
255         str ~= "}";
256         return str;
257
258 /+      return
259             ("%3$s{%2$s"
260             "%4$s"
261             "%1$s}")
262             .format
263             (
264                 fullIndent(nodeDepth), newline,
265                 name, attrAndContent
266             );
267 +/  }
268    
269     override string reduceAttributes(string[] attributes, int nodeDepth)
270     {
271         return processList(attributes, nodeDepth+1);
272     }
273    
274     override string reduceNodes(string[] nodes, int nodeDepth)
275     {
276         return processList(nodes, nodeDepth+1);
277     }
278    
279     override string finalize(string content)
280     {
281         return processNode("", "", content, -1, true);
282     }
283 }
284
285 TreeFormatter formatterTrimmedXML;
286 TreeFormatter formatterPrettyXML;
287 TreeFormatter formatterTrimmedJSON;
288 TreeFormatter formatterPrettyJSON;
289 static this()
290 {
291     formatterTrimmedXML  = new XMLFormatter !(true);
292     formatterPrettyXML   = new XMLFormatter !(false);
293     formatterTrimmedJSON = new JSONFormatter!(true);
294     formatterPrettyJSON  = new JSONFormatter!(false);
295 }
296
297 abstract class TreeNodeBase
298 {
299     abstract string toString(TreeFormatter formatter, string content, int nodeDepth);
300 }
301
302 /// NOTE: Might not be implemented correctly by the formatters, atm.
303 class TreeNodeData : TreeNodeBase
304 {
305     string data;
306    
307     this(){}
308    
309     // ctor templates don't seem to work
310 /*  this(T)(T data)
311     {
312         string dataStr;
313         
314         static if(is(T:string))
315             dataStr = data;
316         else
317             dataStr = format("%s", data);
318         
319         this.data = dataStr;
320     }
321 */
322     this(string data)
323     {
324         this.data = data;
325     }
326
327     override string toString(TreeFormatter formatter, string content, int nodeDepth)
328     {
329         return formatter.processData(content, nodeDepth);
330     }
331 }
332
333 class TreeNodeComment : TreeNodeData
334 {
335     this(){}
336     this(string data)
337     {
338         super(data);
339     }
340
341     override string toString(TreeFormatter formatter, string content, int nodeDepth)
342     {
343         return formatter.processComment(content, nodeDepth);
344     }
345 }
346
347 class TreeNode : TreeNodeBase
348 {
349     string name;
350     string[string] attributes;
351    
352     TreeNodeBase[] subNodes;
353    
354     this(string name, string[string] attributes)
355     {
356         this(name, cast(TreeNode)null, attributes);
357     }
358    
359     this(string name, TreeNodeBase subNodes=null, string[string] attributes=null)
360     {
361         TreeNodeBase[] contentArray;
362         if(subNodes !is null)
363             contentArray ~= subNodes;
364
365         this(name, contentArray, attributes);
366     }
367    
368     this(string name, TreeNodeBase[] subNodes, string[string] attributes=null)
369     {
370         mixin(initMember("name", "subNodes", "attributes"));
371     }
372    
373     TreeNode addAttribute(T, U)(T name, U value)
374     {
375         string nameStr;
376         string valueStr;
377        
378         static if(is(T==string))
379             nameStr = name;
380         else
381             nameStr = to!(string)(name);
382            
383         static if(is(U==string))
384             valueStr = value;
385         else
386             valueStr = to!(string)(value);
387            
388         attributes[nameStr] = valueStr;
389         return this;
390     }
391    
392     TreeNode addAttributes(string[string] attributes)
393     {
394         foreach(string key, string value; attributes)
395             this.attributes[key] = value;
396            
397         return this;
398     }
399    
400     TreeNode addContent(TreeNodeBase subNodes)
401     {
402         this.subNodes ~= subNodes;
403         return this;
404     }
405    
406     TreeNode addContent(TreeNodeBase[] subNodes)
407     {
408         foreach(TreeNodeBase node; subNodes)
409             this.subNodes ~= node;
410            
411         return this;
412     }
413    
414     string format(TreeFormatter formatter)
415     {
416         return formatter.finalize(this.toString(formatter, "", 0));
417     }
418    
419     override string toString(TreeFormatter formatter, string content, int nodeDepth)
420     {
421         auto reduceAttributes = &formatter.reduceAttributes;
422         auto reduceNodes      = &formatter.reduceNodes;
423        
424         //alias ElementType!(typeof(attributes)) AttributeType;
425         alias ElementType!(typeof(subNodes))   SubNodeType;
426         alias string       AttributeType;
427         //alias TreeNodeBase SubNodeType;
428        
429         auto attrStr =
430             reduceAttributes(
431                 attributes
432                 .mapAAtoA((AttributeType val, AttributeType key) {
433                     return formatter.processAttribute(key, val, nodeDepth);
434                 }),
435                 nodeDepth
436             );
437        
438         auto contentStr =
439             reduceNodes(
440                 subNodes
441                 .map((SubNodeType a){
442                     return a.toString(formatter, "", nodeDepth+1);
443                 }),
444                 nodeDepth
445             );
446        
447         return formatter.processNode(name, attrStr, contentStr, nodeDepth);
448     }
449 }
Note: See TracBrowser for help on using the browser.