/*
 * Copyright (c)  2006-2007 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var maskat = {
	/** マスカットフレームワークのバージョン識別子 */
	version: "2.0.0.v20080521",
	
	lang: {
		Object: {
			/**
			 * スコープ内から指定したパス文字列で取得できるオブジェクトを
			 * 返します。
			 *
			 * @param path パス文字列 (プロパティを "." で区切った文字列)、
			 *             または文字列の配列
			 * @param scope スコープの起点オブジェクト、省略時は window
			 *
			 * @return JavaScript オブジェクト、見つからなければ undefined
			 */
			find: function(path, scope){
				var segments = (path instanceof Array) ? path : path.split(".");
				scope = (arguments.length > 1) ? scope : window;

				if (scope) {
					for (var i = 0; i < segments.length; i++) {
						if (typeof(scope[segments[i]]) == "undefined") {
							return undefined;
						}
						scope = scope[segments[i]];
					}
				}
				return scope;
			},
			
			/**
			 * スコープ内の指定したパスにオブジェクトを生成します。
			 *
			 * パス上に該当するオブジェクトが存在しない場合には、それらの
			 * オブジェクトが自動的に生成されます。
			 *
			 * @param path パス文字列 (プロパティを "." で区切った文字列)、
			 *             または文字列の配列
			 * @param scope スコープの起点オブジェクト、省略時は window
			 *
			 * @return 生成されたオブジェクト
			 */
			create: function(path, scope){
				var segments = (path instanceof Array) ? path : path.split(".");
				scope = (arguments.length > 1) ? scope : window;

				if (scope) {
					for (var i = 0; i < segments.length; i++) {
						if (!scope[segments[i]]) {
							scope[segments[i]] = {};
						}
						scope = scope[segments[i]];
					}
				}
				return scope;
			},
			
			/**
			 * あるオブジェクトのプロパティをすべて、もう一方のオブジェクトに
			 * コピーします。
			 *
			 * @param object コピー先のオブジェクト
			 * @param properties コピーするプロパティを格納したオブジェクト
			 *
			 * @return プロパティをコピーされたオブジェクト
			 */
			populate: function(object, properties){
				for (var key in properties) {
					object[key] = properties[key];
				}
				return object;
			},

			/**
			 * 指定したオブジェクトのプロパティをすべて削除します。
			 *
			 * @param object プロパティを削除するオブジェクト
			 */
			dispose: function(object) {
				// TODO: Safari 2.02 以前には hasOwnProperty メソッドがない
				for (var property in object) {
					if (object.hasOwnProperty(property)) {
						delete object[property];
					}
				}
			}
		},
		
		Class: {
			/**
			 * 新しいクラスを宣言します。
			 *
			 * この静的メソッドは新しいクラスのコンストラクタ関数を作成し、
			 * prototype オブジェクトに指定されたプロパティを追加します。
			 *
			 * @param name クラス名を表す文字列
			 * @param properties プロパティを格納したオブジェクト
			 *
			 * @return 新しいクラスのコンストラクタ関数
			 */
			declare: function(name, properties){
				/* サブクラスが先に宣言された場合、親クラスは存在している */
				var clazz = maskat.lang.Object.find(name);
				if (!clazz) {
					/* コンストラクタ関数を生成 */
					clazz = function() {
						this.initialize.apply(this, arguments);
					};
					maskat.lang.Object.populate(clazz, this.methods);

					/* コンストラクタ関数を名前空間に格納 */
					var index = name.lastIndexOf(".");
					var namespace = (index == -1) ? window :
						maskat.lang.Object.create(name.substring(0, index));
					var simpleName = name.substring(index + 1, name.length);
					namespace[simpleName] = clazz;
				}
				
				if (properties) {
					/* 他のクラスを継承しない (ルート階層) */
					clazz.addProperties(properties);
					
					delete clazz.deferred;
					var children = clazz.subclasses;
					if (children) {
						for (var i = 0; i < children.length; i++) {
							children[i].inherit(clazz, children[i].properties);
						}
					}
				} else {
					/* 他のクラスを継承する: プロパティ追加を遅延 */
					clazz.deferred = true;
				}
				return clazz;
			},
			
			methods: {
				/**
				 * このクラスに指定したプロパティを追加します。
				 *
				 * @param properties プロパティを格納したオブジェクト
				 *
				 * @return このクラス
				 */
				addProperties: function(properties){
					/* 静的プロパティをクラスに追加 */
					if (properties._static) {
						maskat.lang.Object.populate(this, properties._static);
						delete properties._static;
					}
					
					/* プロパティを prototype オブジェクトに追加 */
					maskat.lang.Object.populate(this.prototype, properties);
					
					/*
					 * コンストラクタが省略されている場合には、デフォルトの
					 * コンストラクタを定義する
					 */
					if (!this.prototype.initialize) {
						this.prototype.initialize = maskat.lang.EmptyFunction;
					}
					
					/* クラス・イニシャライザを呼び出し */
					if (this.initialize) {
						this.initialize();
					}
					return this;
				},
				
				/**
				 * このクラスを引数 name で指定したクラスのサブクラスにして、
				 * プロパティを追加します。
				 *
				 * @param name 親クラス名を表す文字列
				 * @param properties プロパティを格納したオブジェクト
				 *
				 * @return このクラス
				 */
				extend: function(name, properties){
					var parent = maskat.lang.Object.find(name);
					if (!parent) {
						/* 親クラスが未定義の場合: クラスを宣言する */
						parent = maskat.lang.Class.declare(name);
					}
					this.superclass = parent;
					
					if (!parent.subclasses) {
						parent.subclasses = [];
					}
					parent.subclasses.push(this);
					
					if (parent.deferred) {
						/* 親クラスの初期化が完了していないため、継承を遅延 */
						this.properties = properties;
					} else {
						/* 親クラスを継承させる */
						this.inherit(parent, properties);
					}
					return this;
				},
				
				/**
				 * このクラスを引数 parent で指定したクラスのサブクラスにして、
				 * プロパティを追加します。
				 *
				 * @private
				 *
				 * @param name 親クラス名を表す文字列
				 * @param properties プロパティを格納したオブジェクト
				 */
				inherit: function(parent, properties){
					/* 親クラスの initialize 関数をダミーに置き換える */
					var base = parent.prototype.initialize;
					parent.prototype.initialize = maskat.lang.EmptyFunction;
					
					/* 親クラスを継承する */
					this.prototype = new parent();
					this.prototype.initialize = base;
					this.prototype.base = base;
					this.addProperties(properties);
					
					/* 親クラスの initialize 関数を元に戻す */
					parent.prototype.initialize = base;
					
					/* このクラスにサブクラスがある場合、自分を継承させる */
					var children = this.subclasses;
					if (children) {
						for (var i = 0; i < children.length; i++) {
							if (children[i].deferred) {
								children[i].inherit(this, children[i].properties);
							}
						}
					}
					delete this.deferred;
				}
			}
		},
		
		String: {
			/**
			 * 指定した文字列から、先頭と末尾の空白を取り除いた新しい文字列を
			 * 返します。
			 *
			 * @param string 文字列
			 *
			 * @return 文字列の先頭と末尾の空白を取り除いた文字列
			 */
			trim: function(string){
				return string.replace(/^\s+|\s+$/g, "");
			}
		},
		
		/** 空の関数 */
		EmptyFunction: function(){
		},
		
		/** 常に null を返す関数 */
		ReturnNullFunction: function(){
			return null;
		},
		
		/** 常に true を返す関数 */
		ReturnTrueFunction: function(){
			return true;
		},
		
		/** 常に false を返す関数 */
		ReturnFalseFunction: function(){
			return false;
		}
	}
};
