/*
 * 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.
 */
maskat.lang.Class.declare("maskat.xml.ChildNodeBinding", {

	/**
	 * 子要素のバインディング設定を定義します。
	 *
	 * この XML 要素に対応するオブジェクト (親オブジェクト) は指定された
	 * プロパティを用いて子要素に対応するオブジェクト (子オブジェクト) を
	 * 参照します。
	 *
	 * @param binder このバインディングを使用する XMLObjectBinder
	 * @param name XML 要素のローカル名
	 * @param config バインディング設定
	 *     {
	 *         property:
	 *             親オブジェクトから子オブジェクトを参照するプロパティ名
	 *             property を省略した場合、親要素と子要素は同じオブジェクト
	 *             にマッピングされます。
	 *
	 *         required:
	 *             子要素の存在が必須の場合 true、省略可能の場合は false
	 *
	 *         repeat:
	 *             子要素が繰り返し出現可能の場合は true
	 *             repeat が指定された場合、親オブジェクトのプロパティは
	 *             子オブジェクトの配列にマッピングされます。
     *
	 *         key:
	 *             子要素が繰り返し出現する場合に、子オブジェクトを一意に識別
	 *             するためのインデックスとして使用するプロパティ名
	 *             repeat と組み合わせて指定します。key が指定された場合には
	 *             親オブジェクトのプロパティはハッシュにマッピングされます。
	 *
	 *         value:
	 *             プロパティの値となる子オブジェクトのプロパティ名
	 *             value が指定されている場合、親オブジェクトのプロパティは
	 *             子オブジェクトのプロパティへの参照となります。
	 *     }
	 */
	initialize: function(binder, uri, name, config) {
		this.binder = binder;
		this.uri = uri;
		this.name = name;

		config = config || {};
		this.property = config.property;
		this.method = config.method;
		this.validate = config.validate !== false;
		this.required = config.required || false;
		this.repeat = config.repeat || false;
		this.key = config.key;
		this.value = config.value;
	},

	read: function(object, element) {
		try {
			var binding = this.binder.getElementBinding(element);
		} catch (e) {
			if (this.validate) {
				throw e;
			} else {
				return;
			}
		}

		var value;
		if (typeof(this.property) != "undefined") {
			value = binding.read(null, element);
		} else {
			value = binding.read(object, element);
		}

		if (this.method) {
			object[this.method](value);
		} else if (typeof(this.property) != "undefined") {
			if (this.repeat) {
				if (this.key) {
					if (!object[this.property]) {
						object[this.property] = {};
					}
					var key = value[this.key];
					if (this.value) {
						object[this.property][key] = value[this.value];
					} else {
						object[this.property][key] = value;
					}
				} else {
					if (!object[this.property]) {
						object[this.property] = [];
					}
					if (this.value) {
						object[this.property].push(value[this.value]);
					} else {
						object[this.property].push(value);
					}
				}
			} else {
				object[this.property] = value;
			}
		}
	},

	write: function(object, buffer) {
		if (typeof(this.property) != "undefined") {
			object = object[this.property];
		}
		if (typeof(object) == "undefined") {
			return;
		}

		var binding = this.binder.getElementBinding(this.uri, this.name); 
		if (this.repeat) {
			if (object instanceof Array) {
				for (var i = 0; i < object.length; i++) {
					if (this.value) {
						var wrapper = {};
						wrapper[this.value] = object[i];
						binding.write(wrapper, buffer);
					} else {
						binding.write(object[i], buffer);
					}
				}
			} else if (this.key) {
				for (prop in object) {
					if (this.value) {
						var wrapper = {};
						wrapper[this.key] = prop;
						wrapper[this.value] = object[prop];
						binding.write(wrapper, buffer);
					} else {
						binding.write(object[prop], buffer);
					}
				}
			}
		} else {
			binding.write(object, buffer);
		}
	}

});
