/* ChildrenBindingImpl.java

	Purpose:
		
	Description:
		
	History:
		2012/1/2 Created by Dennis Chen

Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
package org.zkoss.bind.impl;

import java.util.Map;

import org.zkoss.bind.BindContext;
import org.zkoss.bind.Binder;
import org.zkoss.bind.Converter;
import org.zkoss.bind.sys.BindEvaluatorX;
import org.zkoss.bind.sys.ChildrenBinding;
import org.zkoss.bind.sys.ConditionType;
import org.zkoss.xel.ExpressionX;
import org.zkoss.zk.ui.Component;

/**
 * A base implementation of {@link ChildrenBinding}.
 * @author dennis
 * @since 6.0.0
 */
public abstract class ChildrenBindingImpl extends BindingImpl implements ChildrenBinding {
	private static final long serialVersionUID = 1463169907348730644L;
	protected final AccessInfo _accessInfo;
	private final ExpressionX _converter;
	private final Map<String, Object> _converterArgs;

	/**
	 * @param binder
	 * @param comp
	 * @param accessExpr the binding expression , to access the bean
	 * @param conditionType the condition type
	 * @param command the command, if the conditionType is not prompt, then command must not null
	 * @param bindingArgs
	 */
	protected ChildrenBindingImpl(Binder binder, Component comp, String accessExpr, ConditionType conditionType,
			String command, Map<String, Object> bindingArgs, String converterExpr, Map<String, Object> converterArgs) {
		super(binder, comp, bindingArgs);
		final BindEvaluatorX eval = binder.getEvaluatorX();
		final Class<Object> returnType = Object.class;
		this._accessInfo = AccessInfo.create(this, accessExpr, returnType, conditionType, command, ignoreTracker());
		_converterArgs = converterArgs;
		_converter = converterExpr == null ? null : parseConverter(eval, converterExpr);
	}

	//should this binding set the ignore tracker attribute when evaluate the expression.
	protected boolean ignoreTracker() {
		return false;
	}

	private ExpressionX parseConverter(BindEvaluatorX eval, String converterExpr) {
		final BindContext ctx = BindContextUtil.newBindContext(getBinder(), this, false, null, getComponent(), null);
		//provide a bindcontext when pare expression of converter with this binding,
		//do so, the tracker will also tracking the converter dependence with this binding.
		return eval.parseExpressionX(ctx, converterExpr, Object.class);
	}

	public Converter getConverter() {
		if (_converter == null)
			return null;

		final BindContext ctx = BindContextUtil.newBindContext(getBinder(), this, false, null, getComponent(), null);
		final BindEvaluatorX eval = getBinder().getEvaluatorX();
		Object obj = eval.getValue(ctx, getComponent(), _converter);

		if (obj instanceof Converter) {
			return (Converter) obj;
		} else if (obj instanceof String) {
			return getBinder().getConverter((String) obj); //binder will throw exception if not found
		} else {
			throw new ClassCastException(
					"result of expression '" + _converter.getExpressionString() + "' is not a Converter, is " + obj);
		}
	}

	public String getCommandName() {
		return this._accessInfo.getCommandName();
	}

	public String getPropertyString() {
		return BindEvaluatorXUtil.getExpressionString(this._accessInfo.getProperty());
	}

	public ConditionType getConditionType() {
		return this._accessInfo.getConditionType();
	}

	/*package*/ ExpressionX getProperty() {
		return this._accessInfo.getProperty();
	}

	public String toString() {
		return new StringBuilder().append(getClass().getSimpleName()).append("@")
				.append(Integer.toHexString(hashCode())).append(",component:").append(getComponent()).append(",access:")
				.append(getProperty().getExpressionString()).append(",condition:").append(getConditionType())
				.append(",command:").append(getCommandName()).toString();
	}
}
