/*
 * Copyright (c) 2005- Shinji Kashihara.
 * All rights reserved. This program are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at epl-v10.html.
 */
package jp.sourceforge.mergedoc.pleiades.runtime.advice;

import java.util.ArrayList;
import java.util.List;

import jp.sourceforge.mergedoc.org.apache.commons.lang.StringUtils;
import jp.sourceforge.mergedoc.org.apache.commons.lang.builder.ToStringBuilder;
import jp.sourceforge.mergedoc.org.apache.commons.lang.builder.ToStringStyle;

/**
 * ポイント・カットです。
 * <p>
 * pleiades-config.xml 上では pointCut 内に複数の jointPoint が定義されて
 * いますが、このオブジェクト上は pointCut 内の jointPoint は 1 つに
 * 分割されています。つまり、同じアドバイスを持つ pointCut が複数作成されます。
 * <p>
 * @author cypher256
 */
public class PointCut {

	/** タイミング enum */
	public enum Timing {
		/** 前後 */
		AROUND,
		/** 前 */
		BEFORE,
		/** 後 */
		AFTER
	}

	/** タイミング */
	private Timing timing;

	/** アドバイス */
	private String advice;

	/** レベル */
	private String level;

	/** ジョイント・ポイント */
	private JointPoint jointPoint;

	/** 除外ジョイント・ポイント・リスト */
	private List<JointPoint> excludeWheres;

	/** 限定ジョイント・ポイント・リスト */
	private List<JointPoint> includeWheres;

	/** 除外トレース・ジョイント・ポイント・リスト */
	private List<TraceJointPoint> excludeTrace;

	/** 限定トレース・ジョイント・ポイント・リスト */
	private List<TraceJointPoint> includeTrace;

	/**
	 * ポイント・カットを構築します。
	 */
	public PointCut() {
	}

	/**
	 * ポイント・カットを構築します。<br>
	 * 引数のシャロー・コピーを行うコンストラクタです。
	 * <p>
	 * @param pointCut ポイントカット
	 */
	public PointCut(PointCut pointCut) {
		timing = pointCut.timing;
		advice = pointCut.advice;
	}

	/**
	 * アドバイスを取得します。
	 * <p>
	 * @return アドバイス
	 */
	public String getAdvice() {
		return "{" + advice + "}";
	}

	/**
	 * アドバイスをセットします。
	 * <p>
	 * @param advice アドバイス
	 */
	public void setAdvice(String advice) {
		this.advice = advice;
	}
	/**
	 * レベルを取得します。
	 * <p>
	 * @return レベル
	 */
	public String getLevel() {
		return level;
	}

	/**
	 * レベルをセットします。
	 * <p>
	 * @param level レベル
	 */
	public void setLevel(String level) {
		this.level = level;
	}

	/**
	 * タイミングを取得します。
	 * <p>
	 * @return タイミング
	 */
	public Timing getTiming() {
		return timing;
	}

	/**
	 * タイミングをセットします。
	 * <p>
	 * @param timing タイミング
	 */
	public void setTiming(String timing) {
		try {
			this.timing = Timing.valueOf(timing.toUpperCase());
		} catch (RuntimeException e) {
			throw new IllegalArgumentException(
				timing + " is illegal timing. " +
				StringUtils.join(Timing.values(), " or "));
		}
	}

	/**
	 * ジョイント・ポイントを取得します。
	 * <p>
	 * @return ジョイント・ポイント
	 */
	public JointPoint getJointPoint() {
		return jointPoint;
	}

	/**
	 * ジョイント・ポイントをセットします。
	 * <p>
	 * @param jointPoint ジョイント・ポイント
	 */
	public void setJointPoint(JointPoint jointPoint) {
		this.jointPoint = jointPoint;
	}

	/**
	 * 除外ジョイント・ポイント・リストを取得します。
	 * <p>
	 * @return 除外ジョイント・ポイント・リスト
	 */
	public List<JointPoint> getExcludeWheres() {
		if (excludeWheres == null) {
			excludeWheres = new ArrayList<JointPoint>();
		}
		return excludeWheres;
	}

	/**
	 * 限定ジョイント・ポイント・リストを取得します。
	 * <p>
	 * @return 限定ジョイント・ポイント・リスト
	 */
	public List<JointPoint> getIncludeWheres() {
		if (includeWheres == null) {
			includeWheres = new ArrayList<JointPoint>();
		}
		return includeWheres;
	}

	/**
	 * 除外トレース・ジョイント・ポイント・リストを取得します。
	 * <p>
	 * @return 除外トレース・ジョイント・ポイント・リスト
	 */
	public List<TraceJointPoint> getExcludeTrace() {
		if (excludeTrace == null) {
			excludeTrace = new ArrayList<TraceJointPoint>();
		}
		return excludeTrace;
	}

	/**
	 * 限定トレース・ジョイント・ポイント・リストを取得します。
	 * <p>
	 * @return 限定トレース・ジョイント・ポイント・リスト
	 */
	public List<TraceJointPoint> getIncludeTrace() {
		if (includeTrace == null) {
			includeTrace = new ArrayList<TraceJointPoint>();
		}
		return includeTrace;
	}

	/**
	 * このオブジェクトの文字列表現を取得します。
	 */
	@Override
	public String toString() {
		return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
			.append("timing", this.timing)
			.append("advice", getAdvice())
			.append("level", getLevel())
			.append("excludeWheres", this.excludeWheres)
			.append("includeWheres", this.includeWheres)
			.append("excludeTrace", this.excludeTrace)
			.append("includeTrace", this.includeTrace)
			.toString();
	}
}
