OSDN Git Service

28fc4ad7aa11d641a8de5537583e5075bb435345
[pettanr/clientJs.git] / 0.6.x / js / 00_core / 04_XClass.js
1 /**\r
2  * Class を定義し システムの管理下に置く.\r
3  * 全てのクラスと pool が有効の場合インスタンスへの参照が保持される.\r
4  *  1. X.Class.create( opt_settings, opt_name, opt_privateClass, opt_props ) でクラスを登録.\r
5  *  2. コンストラクタ となるメソッドは、Constructor : function( arg ){ ... }, に書く.\r
6  *  3. 通常通り new で インスタンス生成\r
7  *  4. kill() でオブジェクトをクリーンして削除、pool が有効の場合は pool される.\r
8  *  5. pool が有効の場合、new で pool されたインスタンスが返される.\r
9  *  6. \r
10  * \r
11  */\r
12 \r
13 // ------------------------------------------------------------------------- //\r
14 // ------------ local variables -------------------------------------------- //\r
15 // ------------------------------------------------------------------------- //\r
16 var X_Class_CLASS_LIST         = [],\r
17         X_Class_DEF_LIST           = [],\r
18         X_Class_PRIVATE_CLASS_LIST = [],\r
19         X_Class_PRIVATE_DEF_LIST   = [],\r
20         X_Class_killPrivateFlag    = false,\r
21         X_Class_traits             = null,\r
22         X_Class_useObjectCreate    = false, // !!Object.create, http://jsperf.com/prototype-vs-object-create-perf\r
23         X_Class_use_proto_         = !!X.emptyFunction.prototype.__proto__;\r
24 \r
25 // ------------------------------------------------------------------------- //\r
26 // --- interface ----------------------------------------------------------- //\r
27 // ------------------------------------------------------------------------- //\r
28 X.Class = {\r
29         \r
30         POOL_OBJECT  :  1,\r
31         ABSTRACT     :  2,\r
32         FINAL        :  4,\r
33         SUPER_ACCESS :  8,\r
34         PRIVATE_DATA : 16,\r
35         \r
36         SINGLETON    : 32, // 未実装\r
37         \r
38         create : function( /* displayName, classSetting, opt_PrivateClass, props */ ){\r
39                 var args        = X.copyArray( arguments ),\r
40                         displayName = args[ 0 ],\r
41                         classSetting,\r
42                         opt_pool, opt_abstract, opt_final, opt_private,\r
43                         privateDef,\r
44                         props,\r
45                         klass,\r
46                         classDef = {};\r
47                 if( X.Type.isString( displayName ) === true ){\r
48                         classDef.displayName = displayName;\r
49                         args.shift();\r
50                 };\r
51                 \r
52                 // クラス設定\r
53                 classDef.setting = classSetting = args[ 0 ];\r
54                 if( X.Type.isNumber( classSetting ) ){\r
55                         opt_pool     = !!( classSetting & X.Class.POOL_OBJECT  );\r
56                         opt_abstract = !!( classSetting & X.Class.ABSTRACT     );\r
57                         opt_final    = !!( classSetting & X.Class.FINAL        );\r
58                         opt_private  = !!( classSetting & X.Class.PRIVATE_DATA );\r
59                         if( opt_final && opt_abstract ){\r
60                                 X.Notification.critical( 'final & Abstract!' );\r
61                                 return;\r
62                         };      \r
63                         args.shift();\r
64                 } else {\r
65                         classDef.setting = 0;\r
66                 };\r
67                 \r
68                 // シャドウクラス\r
69                 if( X_Class_PRIVATE_CLASS_LIST.indexOf( args[ 0 ] ) !== -1 ){\r
70                         privateDef = X_Class_getClassDef( args[ 0 ] );\r
71                         if( privateDef.isPrivate !== true ){\r
72                                 X.Notification.critical( 'PrivateClass not found! please, X.Class.create( X.Class.PRIVATE, {...} ).' );\r
73                                 return;\r
74                         } else\r
75                         if( privateDef.Abstract === true ){\r
76                                 X.Notification.critical( 'PrivateClass is Abstract!' );\r
77                                 return;\r
78                         };\r
79                         classDef.privateClass = args.shift();\r
80                 };\r
81                 \r
82                 // インスタンスのメンバー\r
83                 props = args[ 0 ];\r
84                 if( !X.Type.isObject( props ) ){\r
85                         // サブクラスの場合、クラス定義の上書きがなくても作成可能\r
86                         // サブクラスでなくても、クラスメンバ用オブジェクトが無しでも作成可能\r
87                         //if( !X_Class_traits ){\r
88                         //      X.Notification.critical( 'No Class Def!' );\r
89                         //      return;\r
90                         //};\r
91                         props = {};\r
92                 } else\r
93                 if( props.Constructor && X.Type.isFunction( props.Constructor ) ){\r
94                         classDef.Constructor = props.Constructor;\r
95                 };\r
96 \r
97                 // TODO use X.Function\r
98                 klass = new Function( 'var a=arguments,f=a.callee;if(f.__new)return f.__new(a)' );\r
99                 klass.__new = X_Class_actualConstructor;\r
100                 klass.superClassOf = X_Class_superClassOf;\r
101                 klass.subClassOf   = X_Class_subClassOf;\r
102                 \r
103                 if( X_Class_useObjectCreate ){\r
104                         klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonProps, false );\r
105                         klass.prototype.constructor = klass;\r
106                 } else\r
107                 if( X_Class_use_proto_ ){\r
108                         X_Class_override( klass.prototype, props, true );\r
109                         if( X_Class_traits ){\r
110                                 klass.prototype.__proto__ = X_Class_traits;\r
111                         } else {\r
112                                 X_Class_override( klass.prototype, X_Class_CommonProps, false );\r
113                         };\r
114                 } else {\r
115                         klass.prototype = X_Class_override( X_Class_override( X_Class_traits || klass.prototype, props, true ), X_Class_CommonProps, false );\r
116                         klass.prototype.constructor = klass;\r
117                 };\r
118                 klass.name = displayName;\r
119                 \r
120                 if( opt_abstract === true ){\r
121                         classDef.Abstract = true;\r
122                 } else\r
123                 if( opt_pool === true ){\r
124                         classDef.pool = [];\r
125                         if( opt_private === false ) classDef.live = [];\r
126                 };                      \r
127                 if( opt_final === true ){\r
128                         classDef.Final = true;\r
129                 } else {\r
130                         klass.inherits = X_Class_inherits;\r
131                 };                      \r
132                 if( opt_private === true ){\r
133                         if( classDef.privateClass ){\r
134                                 X.Notification.critical( 'Private Data Class has no PrivateClass!' );\r
135                                 return;\r
136                         };\r
137                         classDef.isPrivate = true;\r
138                         X_Class_PRIVATE_CLASS_LIST.push( klass );\r
139                         X_Class_PRIVATE_DEF_LIST.push( classDef );\r
140                 } else {\r
141                         X_Class_CLASS_LIST.push( klass );\r
142                         X_Class_DEF_LIST.push( classDef );                              \r
143                 };\r
144                 return klass;\r
145         },\r
146         \r
147         _getClass    : X_Class_getClass,\r
148         \r
149         _getClassDef : X_Class_getClassDef,\r
150         \r
151         _newPrivate  : X_Class_newPrivate,\r
152         \r
153         _getPrivate  : X_Class_getPrivate,\r
154         \r
155         _override    : X_Class_override\r
156         \r
157 };\r
158 \r
159 // ------------------------------------------------------------------------- //\r
160 // --- implements ---------------------------------------------------------- //\r
161 // ------------------------------------------------------------------------- //\r
162 function X_Class_getClass( instance ){\r
163         var cList    = X_Class_CLASS_LIST,\r
164                 i        = cList.length,\r
165                 klass;\r
166         for( ; i; ){\r
167                 klass = cList[ --i ];\r
168                 if( instance.constructor === klass ) return klass;\r
169         };\r
170         cList = X_Class_PRIVATE_CLASS_LIST;\r
171         i     = cList.length;\r
172         for( ; i; ){\r
173                 klass = cList[ --i ];\r
174                 if( instance.constructor === klass ) return klass;\r
175         };\r
176         \r
177         if( cList.indexOf( instance ) !== -1 ) return instance;\r
178         if( X_Class_CLASS_LIST.indexOf( instance ) !== -1 ) return instance;\r
179 };\r
180 \r
181 function X_Class_getClassDef( KlassOrInstance ){\r
182         var i = X_Class_CLASS_LIST.indexOf( KlassOrInstance );\r
183         if( i === -1 ) i = X_Class_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
184         if( i !== -1 ) return X_Class_DEF_LIST[ i ];\r
185         \r
186         i = X_Class_PRIVATE_CLASS_LIST.indexOf( KlassOrInstance );\r
187         if( i === -1 ) i = X_Class_PRIVATE_CLASS_LIST.indexOf( X_Class_getClass( KlassOrInstance ) );\r
188         if( i !== -1 ) return X_Class_PRIVATE_DEF_LIST[ i ];\r
189         \r
190         if( X_Class_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
191         if( X_Class_PRIVATE_DEF_LIST.indexOf( KlassOrInstance ) !== -1 ) return KlassOrInstance;\r
192 };\r
193 \r
194 function X_Class_newPrivate( /* instance, args */ ){\r
195         var args         = X.copyArray( arguments ),\r
196                 user         = args.shift(),\r
197                 def          = X_Class_getClassDef( user ),\r
198                 privateClass = def.privateClass,\r
199                 privateDef   = X_Class_getClassDef( privateClass ),\r
200                 i            = -1;\r
201         if( def.userList ){\r
202                 i = def.userList.indexOf( user );\r
203         } else {\r
204                 def.userList = [];\r
205                 def.dataList = [];\r
206         };\r
207         if( i !== -1 ){\r
208                 X.Notification.critical( 'PrivateData already exist!' );\r
209                 return;\r
210         };\r
211         if( privateDef._tempUser ){\r
212                 X.Notification.critical( 'newPrivate を連続呼び出しされたところ破綻' );\r
213                 return;\r
214         };\r
215         privateDef._tempUser = user;\r
216         return privateClass.__new( args );\r
217 };\r
218 \r
219 function X_Class_getPrivate( instance ){\r
220         var def = X_Class_getClassDef( instance ),\r
221                 i   = def.userList.indexOf( instance );\r
222         if( i !== -1 ) return def.dataList[ i ];\r
223 };\r
224 \r
225 /* over のプロパティを target にコピーする.ただし target の プロパティが優先, force で解除 */\r
226 function X_Class_override( target, src, force ){\r
227         var p;\r
228         for( p in src ){\r
229                 if( p === 'Super' || p === 'SuperConstructor' || p === '__proto__' || p === 'prototype' || p === 'constructor' ){\r
230                         X.Notification.critical( 'Super & SuperConstructor is reserved!' );\r
231                         return;\r
232                 };\r
233                 if( force || target[ p ] === void 0 ){\r
234                         target[ p ] = src[ p ];\r
235                 };\r
236         };\r
237         return target;\r
238 };\r
239 \r
240 function X_Class_superClassOf( klass ){\r
241         var myDef      = X_Class_getClassDef( this ),\r
242                 targetDef  = X_Class_getClassDef( klass ),\r
243                 SuperClass = klass;\r
244 \r
245         if( !myDef || !targetDef || this === klass ) return false;\r
246         \r
247         while( SuperClass = X_Class_getClassDef( SuperClass ).SuperClass ){\r
248                 if( SuperClass === this ) return true;\r
249         };\r
250         \r
251         return false;\r
252 };\r
253 \r
254 function X_Class_subClassOf( klass ){\r
255         return X_Class_superClassOf.call( klass, this );\r
256 };\r
257         \r
258 /* サブクラスを作るメソッド  \r
259  * var subClass = superClass.inherits( ... ) \r
260  * http://d.hatena.ne.jp/m-hiyama/20051018/1129605002\r
261  */\r
262 function X_Class_inherits( /* displayName, classSetting, opt_PrivateClass, props */ ){\r
263         var args        = X.copyArray( arguments ),\r
264                 params      = [],\r
265                 Super       = this,\r
266                 superDef    = X_Class_getClassDef( Super ),\r
267                 displayName = args[ 0 ],\r
268                 classSetting,\r
269                 opt_super,\r
270                 klass, def;\r
271         if( superDef.Final ) X.Notification.critical( 'X.Class inherits, Class is final!' );\r
272         \r
273         // サブクラス名\r
274         if( X.Type.isString( displayName ) ){\r
275                 args.shift();\r
276         } else {\r
277                 displayName = 'SubClass of ' + superDef.displayName;\r
278         };\r
279         params.push( displayName );\r
280         \r
281         // サブクラス設定\r
282         classSetting = args[ 0 ];\r
283         if( X.Type.isNumber( classSetting ) ){\r
284                 args.shift();\r
285         } else {\r
286                 // クラス設定がない場合、親からコピーして、Abstract flag は落とす??\r
287                 classSetting = superDef.setting;// &= ~X.Class.ABSTRACT;\r
288         };\r
289         if( superDef.isPrivate ) classSetting = classSetting | X.Class.PRIVATE_DATA;\r
290         opt_super = !!( classSetting & X.Class.SUPER_ACCESS );\r
291         params.push( classSetting );\r
292 \r
293         // サブクラスのシャドウ\r
294         if( args[ 0 ] && X_Class_getClass( args[ 0 ] ) ){\r
295                 params.push( args.shift() );\r
296         } else\r
297         if( superDef.privateClass ){\r
298                 params.push( superDef.privateClass );\r
299         };\r
300         params.push( args[ 0 ] ); /* props サブクラスでは未定義でも可 */\r
301         \r
302         // 継承クラスの作成\r
303         if( X_Class_useObjectCreate ){\r
304                 X_Class_traits = Object.create( Super.prototype );\r
305         } else\r
306         if( X_Class_use_proto_ ){\r
307                 X_Class_traits = Super.prototype;\r
308         } else {\r
309                 Super.__new = null;\r
310                 X_Class_traits = new Super();\r
311                 Super.__new = X_Class_actualConstructor;                        \r
312         };\r
313         klass  = X.Class.create.apply( X.Class, params );\r
314         X_Class_traits = null;\r
315         \r
316         def    = X_Class_getClassDef( klass );\r
317         // 継承用プロパティを控える\r
318         if( opt_super === true ){\r
319                 def.superAccess = true;\r
320                 def.SuperClass  = Super;\r
321                 def.SuperProto  = Super.prototype;\r
322                 def.SuperConstructor = superDef.Constructor || superDef.SuperConstructor;\r
323         } else {\r
324                 def.SuperClass = Super; // instanceOf() で親クラスを調べる!\r
325         };\r
326         \r
327         return klass;\r
328 };\r
329 \r
330 /* X.Class.create で作られたクラスのインスタンスが共通で備えるメソッド \r
331  *\r
332  *\r
333  */\r
334 var X_Class_CommonProps = {\r
335         kill : function(){\r
336                 var instance = this,\r
337                         klass    = X_Class_getClass( instance ),\r
338                         def      = X_Class_getClassDef( klass ),\r
339                         data, p, i;\r
340                 if( def.isPrivate && !X_Class_killPrivateFlag ){\r
341                         X.Notification.critical( 'PrivateInstance.kill() work in PrivateUser.kill().' );\r
342                         return;\r
343                 };\r
344                 X_Class_killPrivateFlag = false; // onKill 内で PrivateInstance.kill() を防ぐため\r
345                 \r
346                 // onKill() === false の場合、kill のキャンセル\r
347                 // private      は false での キャンセル は無視される\r
348                 \r
349                 if( this.instanceOf( X.EventDispatcher ) ){\r
350                         console.log( 'this.instanceOf( X.EventDispatcher )! ' + this._dispatching );\r
351                         if( !def.isPrivate ){\r
352                                 if( this._dispatching ){\r
353                                         this.dispatch( X.Event.BEFORE_KILL_INSTANCE );\r
354                                         this._killReserved = true;\r
355                                         this.dispatch( X.Event.KILL_INSTANCE_CANCELED );\r
356                                         return;\r
357                                 } else\r
358                                 if( this.dispatch( X.Event.BEFORE_KILL_INSTANCE ) & X.Callback.PREVENT_DEFAULT ){\r
359                                         this.dispatch( X.Event.KILL_INSTANCE_CANCELED );\r
360                                         return;\r
361                                 };\r
362                         } else {\r
363                                 this.dispatch( X.Event.BEFORE_KILL_INSTANCE );  \r
364                         };\r
365                         this.dispatch( X.Event.KILL_INSTANCE );\r
366                         this._listeners && this.unlisten();\r
367                 } else\r
368                 if( X.Type.isFunction( instance.onKill ) && instance.onKill() === false && !def.isPrivate ){\r
369                         return;\r
370                 };\r
371                 \r
372                 console.log('kill ' + this._dispatching);\r
373                 \r
374                 for( p in instance ){\r
375                         if( instance.hasOwnProperty && !instance.hasOwnProperty( p ) ) continue;\r
376                         delete instance[ p ];\r
377                 };\r
378                 if( def.pool ){\r
379                         def.live && def.live.splice( def.live.indexOf( instance ), 1 );\r
380                         def.pool[ def.pool.length ] = instance;\r
381                 };\r
382                 if( def.privateClass ){\r
383                         i = def.userList.indexOf( instance );\r
384                         if( i !== -1 ){\r
385                                 data            = X_Class_getPrivate( instance );\r
386                                 X_Class_killPrivateFlag = true;\r
387                                 if( data._dispatching && data.instanceOf( X.EventDispatcher ) ){\r
388                                         data._killReserved = true;\r
389                                 } else {\r
390                                         data.kill();\r
391                                 };\r
392                                 def.dataList.splice( i, 1 );\r
393                                 def.userList.splice( i, 1 );\r
394                         };\r
395                 };\r
396         },\r
397         \r
398         // TODO Super\r
399         // superCall\r
400         \r
401         instanceOf : function( klass ){\r
402                 var Super = this;\r
403                 if( this.constructor === klass ) return true;\r
404                 while( Super = X_Class_getClassDef( Super ).SuperClass ){\r
405                         if( Super === klass ) return true;\r
406                 };\r
407                 return false;\r
408         }\r
409 };\r
410         \r
411 /*\r
412  * new の実体.コンストラクタの機能は instance.Constructor に書く.\r
413  * これにより pool された オブジェクト(破棄されたインスタンス) を再利用できる\r
414  */\r
415 function X_Class_actualConstructor( args ){\r
416         var klass    = this,\r
417                 def      = X_Class_getClassDef( klass ),\r
418                 dataUser = def._tempUser,\r
419                 instance, obj,\r
420                 userDef;\r
421         if( def.Abstract ){\r
422                 X.Notification.critical( 'AbstractClass!' );\r
423                 return;\r
424         };\r
425         if( def.isPrivate && !dataUser ){\r
426                 X.Notification.critical( 'use myClass.newPrivate( instance, ...args )!' );\r
427                 return;\r
428         };\r
429         klass.__new = null;\r
430         instance = def.pool && def.pool.length > 0 ?\r
431                                         def.pool.pop() :\r
432                                 X_Class_useObjectCreate ?\r
433                                         Object.create( klass.prototype ) :\r
434                                         new klass();\r
435         klass.__new = X_Class_actualConstructor;\r
436         \r
437         if( def.isPrivate ){\r
438                 userDef = X_Class_getClassDef( dataUser );\r
439                 userDef.dataList.push( instance );\r
440                 userDef.userList.push( dataUser );\r
441                 instance.User = dataUser;\r
442                 def._tempUser = null;\r
443         } else {\r
444                 def.live && def.live.push( instance );\r
445         };\r
446         if( def.superAccess ){\r
447                 // TODO klass.prototype に移動\r
448                 instance.Super = def.SuperProto;\r
449                 instance.SuperConstructor = X_Class_superConstructor;\r
450         };\r
451         obj = def.Constructor ?\r
452                         def.Constructor.apply( instance, args ) :\r
453                 def.SuperConstructor &&\r
454                         def.SuperConstructor.apply( instance, args );\r
455         if( ( X.Type.isObject( obj ) && obj !== instance ) || X.Type.isFunction( obj ) ){ // Class\r
456                 instance.kill();\r
457                 return obj;\r
458         };\r
459         return instance;\r
460 };\r
461 \r
462 function X_Class_superConstructor(){\r
463         var s = X_Class_getClassDef( this ).SuperConstructor;\r
464         return s && s.apply( this, arguments );\r
465 };\r
466 \r
467 console.log( 'X.Core.Class' );\r