1 module des.util.arch.tree; 2 3 import std.traits; 4 import std..string; 5 import std.algorithm; 6 import std.exception; 7 8 import des.util.testsuite; 9 10 /// 11 class TNodeException : Exception 12 { 13 this( string msg, string file=__FILE__, size_t line=__LINE__ ) 14 { super( msg, file, line ); } 15 } 16 17 /// 18 class TNodeCycleException : TNodeException 19 { 20 this( string file=__FILE__, size_t lien=__LINE__ ) 21 { super( "cycle detect", file, line ); } 22 } 23 24 /// 25 class TNodeNullChildException : TNodeException 26 { 27 this( string file=__FILE__, size_t lien=__LINE__ ) 28 { super( "can't append null child", file, line ); } 29 } 30 31 /// 32 template TNode(T,string prefix="",string suffix="") 33 { 34 interface TNode 35 { 36 mixin( replaceWords!(wordWrapFunc!(prefix,suffix))(q{ 37 public 38 { 39 alias T %NodeType; 40 41 @property 42 { 43 %NodeType %parent(); 44 const(%NodeType) %parent() const; 45 %NodeType %parent( %NodeType p ); 46 47 %NodeType[] %childs(); 48 const(%NodeType[]) %childs() const; 49 } 50 51 final bool %findInChilds( const(%NodeType) obj ) const 52 { 53 foreach( ch; %childs ) 54 { 55 if( obj == ch ) return true; 56 if( ch.%findInChilds(obj) ) return true; 57 } 58 return false; 59 } 60 61 void %attachChilds( %NodeType[] att... ); 62 void %detachChilds( %NodeType[] att... ); 63 } 64 65 protected 66 { 67 void %attachCallback( %NodeType[] att ); 68 void %detachCallback( %NodeType[] det ); 69 } 70 71 void __%simpleSetParent( %NodeType p ); 72 73 mixin template %TNodeHelper(bool __%release_cycle_check=false, 74 bool __%release_nullchild_check=false ) 75 { 76 protected 77 { 78 import std.traits; 79 override 80 { 81 static if( isAbstractFunction!%attachCallback ) 82 void %attachCallback( %NodeType[] att ){} 83 static if( isAbstractFunction!%detachCallback ) 84 void %detachCallback( %NodeType[] det ){} 85 } 86 87 %NodeType __%parent_node; 88 %NodeType[] __%childs_list; 89 } 90 91 void __%simpleSetParent( %NodeType p ) { __%parent_node = p; } 92 93 public @property 94 { 95 %NodeType %parent() { return __%parent_node; } 96 const(%NodeType) %parent() const { return __%parent_node; } 97 98 %NodeType %parent( %NodeType p ) 99 { 100 if( __%parent_node !is null ) 101 __%parent_node.%detachChilds( this ); 102 __%parent_node = p; 103 if( __%parent_node !is null ) 104 __%parent_node.%attachChilds( this ); 105 return p; 106 } 107 108 %NodeType[] %childs() { return __%childs_list; } 109 const(%NodeType[]) %childs() const { return __%childs_list; } 110 111 %NodeType[] %childs( %NodeType[] nch ) 112 { 113 __%childs_list = nch; 114 return nch; 115 } 116 } 117 118 final 119 { 120 public void %attachChilds( %NodeType[] att... ) 121 in { assert( att.length > 0 ); } body 122 { 123 import std.exception; 124 import des.util.arch.tree; 125 debug enum DEBUG = true; else enum DEBUG = false; 126 127 static if( __%release_nullchild_check || DEBUG ) 128 foreach( el; att ) 129 enforce( el !is null, new TNodeNullChildException ); 130 131 static if( __%release_cycle_check || DEBUG ) 132 enforce( %cycleCheck( att ), new TNodeCycleException ); 133 134 foreach( el; att ) 135 { 136 if( %findInChilds( el ) ) continue; 137 if( el.%parent !is null ) 138 el.%parent.%detachChilds( el ); 139 __%childs_list ~= el; 140 el.__%simpleSetParent( this ); 141 } 142 %attachCallback( att ); 143 } 144 145 public void %detachChilds( %NodeType[] det... ) 146 { 147 %NodeType[] buf; 148 foreach( ch; %childs ) 149 foreach( d; det ) 150 if( ch != d ) 151 buf ~= ch; 152 foreach( d; det ) 153 d.__%simpleSetParent( null ); 154 %childs = buf; 155 %detachCallback( det ); 156 } 157 158 protected bool %cycleCheck( const(%NodeType)[] list... ) const 159 { 160 const(%NodeType)[] plist = [this]; 161 162 while( plist[$-1].%parent ) 163 plist ~= plist[$-1].%parent; 164 165 foreach( p; plist ) 166 foreach( elem; list ) 167 if( elem.%findInChilds(p) ) 168 return false; 169 170 return true; 171 } 172 } 173 } 174 })); 175 } 176 } 177 178 /// 179 unittest 180 { 181 static class Test : TNode!(Test,"","XX") 182 { 183 mixin TNodeHelperXX!(true,true); 184 185 string name; 186 this( string name ) { this.name = name; } 187 188 void append( string kk ) 189 { 190 name ~= kk; 191 foreach( ch; childsXX ) 192 ch.append( kk ); 193 } 194 } 195 196 auto e0 = new Test("a"); 197 auto e1 = new Test("b"); 198 auto e2 = new Test("c"); 199 auto e3 = new Test("d"); 200 201 e1.parentXX = e0; 202 e2.parentXX = e1; 203 204 e0.append( "ok" ); 205 206 e1.attachChildsXX( e3 ); 207 208 assertEq( e0.childsXX, [ e1 ] ); 209 assertEq( e0.name, "aok" ); 210 assertEq( e1.name, "bok" ); 211 assertEq( e3.name, "d" ); 212 assertEq( e3.parentXX, e1 ); 213 } 214 215 /// 216 unittest 217 { 218 static class Test : TNode!(Test,"a_"), TNode!(Test,"b_") 219 { 220 mixin a_TNodeHelper!(true,true); 221 mixin b_TNodeHelper!(true,true); 222 223 string name; 224 this( string name ) { this.name = name; } 225 226 void append( string kk ) 227 { 228 name ~= kk; 229 foreach( ch; b_childs ) 230 ch.append( kk ); 231 } 232 } 233 234 auto e0 = new Test("e0"); 235 auto e1 = new Test("e1"); 236 auto e2 = new Test("e2"); 237 auto e3 = new Test("e3"); 238 239 e2.a_parent = e0; 240 e2.b_parent = e1; 241 242 e0.append( "ok" ); 243 244 assert( e0.name == "e0ok" ); 245 assert( e2.name == "e2" ); 246 } 247 248 /// 249 unittest 250 { 251 static interface Node : TNode!Node 252 { 253 mixin template NodeHelper() { mixin TNodeHelper!true; } 254 void inc_self(); 255 256 final void inc() 257 { 258 inc_self(); 259 foreach( ch; childs ) 260 ch.inc(); 261 } 262 } 263 264 static class Test : Node 265 { 266 mixin NodeHelper; 267 268 int i; 269 this( int i ) { this.i = i; } 270 void inc_self() { i++; } 271 } 272 273 auto a0 = new Test(10); 274 auto a1 = new Test(15); 275 a0.attachChilds( a1 ); 276 277 a0.inc(); 278 279 assert( a0.i == 11 ); 280 assert( a1.i == 16 ); 281 } 282 283 /// 284 unittest 285 { 286 static class Test : TNode!Test { mixin TNodeHelper!(true, true); } 287 288 auto a0 = new Test; 289 auto a1 = new Test; 290 auto a2 = new Test; 291 292 assert( a0.parent is null ); 293 assert( a1.parent is null ); 294 assert( a2.parent is null ); 295 296 a0.attachChilds( a1 ); 297 a1.attachChilds( a2 ); 298 299 assert( a0.childs.length == 1 ); 300 assert( a1.childs.length == 1 ); 301 302 assert( mustExcept!TNodeCycleException({ a2.attachChilds(a0); }) ); 303 assert( mustExcept!TNodeNullChildException({ a2.attachChilds(null); }) ); 304 } 305 306 unittest 307 { 308 static class Test : TNode!(Test,"a") { mixin aTNodeHelper!(true, true); } 309 auto a0 = new Test; 310 assert( a0.aParent is null ); 311 } 312 313 private 314 { 315 string replaceWords(alias fun)( string s ) pure 316 { 317 string ret; 318 string buf; 319 size_t p0 = 0, p1 = 0; 320 while( p1 < s.length ) 321 { 322 if( s[p1] == '%' ) 323 { 324 ret ~= s[p0..p1]; 325 p0 = ++p1; 326 while( p1 < s.length && identitySymbol(s[p1]) ) 327 p1++; 328 ret ~= fun(s[p0..p1]); 329 p0 = p1; 330 } 331 else if( p1 == s.length - 1 ) 332 ret ~= s[p0..p1]; 333 p1++; 334 } 335 return ret; 336 } 337 338 pure bool identitySymbol( char c ) 339 { 340 switch(c) 341 { 342 case 'a': .. case 'z': case 'A': .. case 'Z': 343 case '_': case '0': .. case '9': return true; 344 default: return false; 345 } 346 } 347 348 pure bool identityString( string s ) 349 { 350 foreach( c; s ) 351 if( !identitySymbol(c) ) return false; 352 return true; 353 } 354 355 template wordWrapFunc(string pre, string suf) 356 { 357 import std.exception; 358 static assert( identityString(pre) ); 359 static assert( identityString(suf) ); 360 string wordWrapFunc( string a ) 361 { 362 enforce( a.length ); 363 if( pre.length && pre[$-1] != '_' ) 364 a = (""~a[0]).toUpper ~ a[1..$]; 365 return pre ~ a ~ suf; 366 } 367 } 368 }