/* $Id: PackageConverter.java,v 1.2 2007/12/11 09:54:58 nito Exp $
 * 
 * Copyright (c)ARGO 21, Corporation. 2005, 2006.  All rights reserved.
 * 
 * This file is part of Nautica Workflow.
 * 
 *  Nautica Workflow is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 * 
 *  Nautica Workflow is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with Nautica Workflow; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *  
 */
package jp.co.argo21.nautica.tool.wfd;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import jp.co.argo21.nautica.tool.wfd.element.ActivityElement;
import jp.co.argo21.nautica.tool.wfd.element.ApplicationElement;
import jp.co.argo21.nautica.tool.wfd.element.PackageElement;
import jp.co.argo21.nautica.tool.wfd.element.ParameterElement;
import jp.co.argo21.nautica.tool.wfd.element.ParticipantElement;
import jp.co.argo21.nautica.tool.wfd.element.ProcessElement;
import jp.co.argo21.nautica.tool.wfd.element.TransitionElement;
import jp.co.argo21.nautica.tool.wfd.element.VariableElement;
import jp.co.argo21.nautica.tool.wfd.expression.ConditionalExpression;
import jp.co.argo21.nautica.tool.wfd.expression.Expression;
import jp.co.argo21.nautica.workflow.xpdl.ConditionalExpressionDocument;
import jp.co.argo21.nautica.commons.util.StringUtils;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.wfmc.x2002.xpdl10.ActivitiesDocument;
import org.wfmc.x2002.xpdl10.ActivityDocument;
import org.wfmc.x2002.xpdl10.ActualParametersDocument;
import org.wfmc.x2002.xpdl10.ApplicationDocument;
import org.wfmc.x2002.xpdl10.ApplicationsDocument;
import org.wfmc.x2002.xpdl10.BasicTypeDocument;
import org.wfmc.x2002.xpdl10.BlockActivityDocument;
import org.wfmc.x2002.xpdl10.ConditionDocument;
import org.wfmc.x2002.xpdl10.DataFieldDocument;
import org.wfmc.x2002.xpdl10.DataFieldsDocument;
import org.wfmc.x2002.xpdl10.DataTypeDocument;
import org.wfmc.x2002.xpdl10.ExtendedAttributeDocument;
import org.wfmc.x2002.xpdl10.ExtendedAttributesDocument;
import org.wfmc.x2002.xpdl10.FormalParameterDocument;
import org.wfmc.x2002.xpdl10.FormalParametersDocument;
import org.wfmc.x2002.xpdl10.ImplementationDocument;
import org.wfmc.x2002.xpdl10.PackageDocument;
import org.wfmc.x2002.xpdl10.PackageHeaderDocument;
import org.wfmc.x2002.xpdl10.ParticipantDocument;
import org.wfmc.x2002.xpdl10.ParticipantTypeDocument;
import org.wfmc.x2002.xpdl10.ParticipantsDocument;
import org.wfmc.x2002.xpdl10.ProcessHeaderDocument;
import org.wfmc.x2002.xpdl10.RedefinableHeaderDocument;
import org.wfmc.x2002.xpdl10.ResponsiblesDocument;
import org.wfmc.x2002.xpdl10.SubFlowDocument;
import org.wfmc.x2002.xpdl10.ToolDocument;
import org.wfmc.x2002.xpdl10.TransitionDocument;
import org.wfmc.x2002.xpdl10.TransitionsDocument;
import org.wfmc.x2002.xpdl10.WorkflowProcessDocument;
import org.wfmc.x2002.xpdl10.WorkflowProcessesDocument;

/**
 * パッケージをXmlObjectに変換するコンバータ。
 * 
 * @author  Norihiro Itoh(ARGO 21 Corp.)
 * @version $Revision: 1.2 $
 * @since   Nautica Workflow 1.0
 */
public class PackageConverter
{
	/** 保存用空XPDL */
	private static final String EMPTY_FLOW
		= "/jp/co/argo21/nautica/tool/wfd/resources/empty.xpdl";

	/** コンバータ */
	private static final PackageConverter singleton = new PackageConverter();

	/**
	 * コンバータを返す。
	 * 
	 * @return	コンバータ
	 */
	public static PackageConverter getInstance()
	{
		return singleton;
	}

	/**
	 * パッケージコンバータを生成する。
	 */
	private PackageConverter()
	{
	}

	/**
	 * パッケージをXmlObjectに変換する。
	 * 
	 * @param elem	パッケージ
	 * @return	XmlObject
	 * @throws XmlException XML例外
	 * @throws IOException 入出力例外
	 */
	public PackageDocument convert(PackageElement elem)
	throws XmlException, IOException
	{
		if (elem == null) return null;
		PackageDocument doc = getEmptyDocument();
		PackageDocument.Package pkg = doc.getPackage();
		
		convertPackageElement(pkg, elem);
		
		return doc;
	}
	
	/**
	 * 空のワークフロー定義ドキュメントを返す。
	 * 
	 * @return ワークフロー定義ドキュメント
	 * @throws XmlException XML例外
	 * @throws IOException 入出力例外
	 */
	private PackageDocument getEmptyDocument() throws XmlException, IOException
	{
		InputStream in = getClass().getResourceAsStream(EMPTY_FLOW);
		XmlOptions opts = new XmlOptions();
		opts.setCharacterEncoding("utf-8");
		PackageDocument doc = PackageDocument.Factory.parse(in, opts);
		in.close();
		return doc;
	}

	/**
	 * パッケージをXmlObjectに変換する。
	 * 
	 * @param pkg	XmlObject
	 * @param elem
	 * @throws XmlException XML例外
	 */
	private void convertPackageElement(PackageDocument.Package pkg, PackageElement elem)
	throws XmlException
	{
		pkg.setId(elem.getID());
		pkg.setName(elem.getName());
		
		PackageHeaderDocument.PackageHeader phd = pkg.getPackageHeader();
		phd.setDescription(elem.getDescription());
		phd.setCreated(elem.getCreatedDate());
		
		RedefinableHeaderDocument.RedefinableHeader rhd = pkg.addNewRedefinableHeader();
		rhd.setAuthor(elem.getAuthor());
		rhd.setVersion(elem.getVersion());
		
		List<ApplicationElement> apps = elem.getApplications();
		if (apps.size() > 0) {
			ApplicationsDocument.Applications applications
				= pkg.addNewApplications();
			for (ApplicationElement e : apps) {
				convertApplicationElement(applications.addNewApplication(), e);
			}
		}
		
		List<ParticipantElement> parts = elem.getParticipants();
		if (parts.size() > 0) {
			ParticipantsDocument.Participants participants
				= pkg.addNewParticipants();
			for (ParticipantElement e : parts) {
				convertParticipantElement(participants.addNewParticipant(), e);
			}
		}
		
		List<VariableElement> vars = elem.getVariables();
		if (vars.size() > 0) {
			DataFieldsDocument.DataFields dataFields
				= pkg.addNewDataFields();
			for (VariableElement e : vars) {
				convertVariableElement(dataFields.addNewDataField(), e);
			}
		}
		
		List<ProcessElement> procs = elem.getProcesses();
		if (procs.size() > 0) {
			WorkflowProcessesDocument.WorkflowProcesses processes
				= pkg.addNewWorkflowProcesses();
			for (ProcessElement e : procs) {
				convertProcessElement(processes.addNewWorkflowProcess(), e);
			}
		}
	}

	/**
	 * アプリケーションをXmlObjectに変換する。
	 * 
	 * @param app	XmlObject
	 * @param elem	アプリケーション
	 * @throws XmlException XML例外
	 */
	private void convertApplicationElement(
	ApplicationDocument.Application app,
	ApplicationElement elem
	)
	throws XmlException
	{
		app.setId(elem.getID());
		app.setName(elem.getName());
		app.setDescription(elem.getDescription());
		
		List<ParameterElement> params = elem.getParameters();
		if (params.size() > 0) {
			FormalParametersDocument.FormalParameters parameters
				= app.addNewFormalParameters();
			for (ParameterElement e : params) {
				convertParameterElement(parameters.addNewFormalParameter(), e);
			}
		}
	}

	/**
	 * ワークフロー参加者をXmlObjectに変換する。
	 * 
	 * @param part	XmlObject
	 * @param elem	ワークフロー参加者
	 * @throws XmlException XML例外
	 */
	private void convertParticipantElement(
	ParticipantDocument.Participant part,
	ParticipantElement elem
	)
	throws XmlException
	{
		part.setId(elem.getID());
		part.setName(elem.getName());
		part.setDescription(elem.getDescription());
		
		ParticipantTypeDocument.ParticipantType ptype = part.addNewParticipantType();
		ParticipantTypeDocument.ParticipantType.Type.Enum type
			= ParticipantTypeDocument.ParticipantType.Type.Enum.forInt(elem.getType());
		ptype.setType(type);
	}

	/**
	 * プロセス変数をXmlObjectに変換する。
	 * 
	 * @param data	XmlObject
	 * @param elem	プロセス変数
	 * @throws XmlException XML例外
	 */
	private void convertVariableElement(
	DataFieldDocument.DataField data,
	VariableElement elem
	)
	throws XmlException
	{
		data.setId(elem.getID());
		data.setName(elem.getName());
		data.setDescription(elem.getDescription());
		data.setInitialValue(elem.getInitialValue());

		if (elem.isInitialized()) {
			DataTypeDocument.DataType dtype = data.addNewDataType();
			BasicTypeDocument.BasicType basictype = dtype.addNewBasicType();
			BasicTypeDocument.BasicType.Type.Enum type
				= BasicTypeDocument.BasicType.Type.Enum.forInt(elem.getType());
			basictype.setType(type);
		}
	}

	/**
	 * プロセスをXmlObjectに変換する。
	 * 
	 * @param proc	XmlObject
	 * @param elem	プロセス
	 * @throws XmlException
	 */
	private void convertProcessElement(
	WorkflowProcessDocument.WorkflowProcess proc,
	ProcessElement elem
	)
	throws XmlException
	{
		proc.setId(elem.getID());
		proc.setName(elem.getName());
		WorkflowProcessDocument.WorkflowProcess.AccessLevel.Enum level
		 = WorkflowProcessDocument.WorkflowProcess.AccessLevel.Enum.forInt(elem.getAccessLevel());
		proc.setAccessLevel(level);

		ProcessHeaderDocument.ProcessHeader ph = proc.addNewProcessHeader();
		ph.setDescription(elem.getDescription());
		ProcessHeaderDocument.ProcessHeader.DurationUnit.Enum unit
			 = ProcessHeaderDocument.ProcessHeader.DurationUnit.Enum.forInt(elem.getDurationUnit());
		ph.setDurationUnit(unit);
		ph.setLimit("" + elem.getLimit());

		
		List<String> rsps = elem.getResponsibles();
		if (rsps.size() > 0) {
			RedefinableHeaderDocument.RedefinableHeader rhd
				= proc.addNewRedefinableHeader();
			ResponsiblesDocument.Responsibles responsibles
				= rhd.addNewResponsibles();
			for (String r : rsps) {
				responsibles.addResponsible(r);
			}
		}

		ExtendedAttributesDocument.ExtendedAttributes eas
			= proc.addNewExtendedAttributes();

		ExtendedAttributeDocument.ExtendedAttribute ea1
			= eas.addNewExtendedAttribute();
		ea1.setName(ProcessElement.LOGICAL_WIDTH);
		ea1.setValue("" + elem.getLogicalWidth());

		ExtendedAttributeDocument.ExtendedAttribute ea2
			= eas.addNewExtendedAttribute();
		ea2.setName(ProcessElement.LOGICAL_HEIGHT);
		ea2.setValue("" + elem.getLogicalHeight());
		
		List<ParameterElement> params = elem.getParameters();
		if (params.size() > 0) {
			FormalParametersDocument.FormalParameters parameters
				= proc.addNewFormalParameters();
			for (ParameterElement e : params) {
				convertParameterElement(parameters.addNewFormalParameter(), e);
			}
		}
		
		List<ActivityElement> acts = elem.getActivities();
		if (acts.size() > 0) {
			ActivitiesDocument.Activities activities
				= proc.addNewActivities();
			for (ActivityElement e : acts) {
				convertActivityElement(activities.addNewActivity(), e);
			}
		}
		
		List<TransitionElement> trs = elem.getTransitions();
		if (trs.size() > 0) {
			TransitionsDocument.Transitions transitions
				= proc.addNewTransitions();
			for (TransitionElement e : trs) {
				convertTransitionElement(transitions.addNewTransition(), e);
			}
		}
	}

	/**
	 * パラメータをXmlObjectに変換する。
	 * 
	 * @param param	XmlObject
	 * @param elem	パラメータ
	 * @throws XmlException XML例外
	 */
	private void convertParameterElement(
	FormalParameterDocument.FormalParameter param,
	ParameterElement elem
	)
	throws XmlException
	{
		param.setId(elem.getID());
		param.setDescription(elem.getDescription());

		FormalParameterDocument.FormalParameter.Mode.Enum mode
			= FormalParameterDocument.FormalParameter.Mode.Enum.forInt(elem.getMode());
		param.setMode(mode);

		DataTypeDocument.DataType dtype = param.addNewDataType();
		BasicTypeDocument.BasicType basictype = dtype.addNewBasicType();
		BasicTypeDocument.BasicType.Type.Enum type
			= BasicTypeDocument.BasicType.Type.Enum.forInt(elem.getType());
		basictype.setType(type);
	}

	/**
	 * アクティビティをXmlObjectに変換する。
	 * 
	 * @param act	XmlObject
	 * @param elem	アクティビティ
	 * @throws XmlException XML例外
	 */
	private void convertActivityElement(
	ActivityDocument.Activity act,
	ActivityElement elem
	)
	throws XmlException
	{
		act.setId(elem.getID());
		act.setName(elem.getName());
		act.setDescription(elem.getDescription());

		String type = elem.getType();

		if (type.equals(ActivityElement.CONCUR_START)
			|| type.equals(ActivityElement.CONCUR_END)) {
				BlockActivityDocument.BlockActivity blockActivity
					= act.addNewBlockActivity();
				blockActivity.setBlockId(elem.getBlockID());
		}

		if (type.equals(ActivityElement.INTERACTIVE)
			|| type.equals(ActivityElement.AUTOMATIC)
			|| type.equals(ActivityElement.SUBPROCESS)) {
			ImplementationDocument.Implementation imp
				= act.addNewImplementation();
			act.setLimit("" + elem.getLimit());
			
			if (type.equals(ActivityElement.INTERACTIVE)) {
				act.setPerformer(elem.getPerformerID());
			} else if (type.equals(ActivityElement.AUTOMATIC)) {
				act.setPerformer(elem.getPerformerID());

				ToolDocument.Tool tool = imp.addNewTool();
				tool.setId(elem.getApplicationID());

				List<String> appParams = elem.getApplicationParameters();
				if (appParams.size() > 0) {
					ActualParametersDocument.ActualParameters actualParameters
						= tool.addNewActualParameters();
					for (String p : appParams) {
						actualParameters.addActualParameter(p);
					}
				}
			} else if (type.equals(ActivityElement.SUBPROCESS)) {
				SubFlowDocument.SubFlow sf = imp.addNewSubFlow();
				sf.setId(elem.getSubprocessID());

				SubFlowDocument.SubFlow.Execution.Enum sync
					= SubFlowDocument.SubFlow.Execution.Enum.forInt(elem.getExecution());
				sf.setExecution(sync);

				List<String> procParams = elem.getSubprocessParameters();
				if (procParams.size() > 0) {
					ActualParametersDocument.ActualParameters actualParameters
						= sf.addNewActualParameters();
					for (String p : procParams) {
						actualParameters.addActualParameter(p);
					}
				}
			}
		}

		ExtendedAttributesDocument.ExtendedAttributes eas
			= act.addNewExtendedAttributes();

		ExtendedAttributeDocument.ExtendedAttribute ea1
			= eas.addNewExtendedAttribute();
		ea1.setName(ActivityElement.LOGICAL_X);
		ea1.setValue("" + elem.getLogicalX());

		ExtendedAttributeDocument.ExtendedAttribute ea2
			= eas.addNewExtendedAttribute();
		ea2.setName(ActivityElement.LOGICAL_Y);
		ea2.setValue("" + elem.getLogicalY());

		ExtendedAttributeDocument.ExtendedAttribute ea3
			= eas.addNewExtendedAttribute();
		ea3.setName(ActivityElement.ACTIVITY_TYPE);
		ea3.setValue(type);
		
		String pbid = elem.getParentBID();
		if (StringUtils.checkNull(pbid) == false) {
			ExtendedAttributeDocument.ExtendedAttribute ea4
				= eas.addNewExtendedAttribute();
			ea4.setName(ActivityElement.PARENT_BID);
			ea4.setValue(pbid);
		}
		
		String abid = elem.getAffiliateBID();
		if (StringUtils.checkNull(abid) == false) {
			ExtendedAttributeDocument.ExtendedAttribute ea5
				= eas.addNewExtendedAttribute();
			ea5.setName(ActivityElement.AFFILIATE_BID);
			ea5.setValue(abid);
		}
		
		String engineName = elem.getEngineName();
		if (StringUtils.checkNull(engineName) == false) {
			ExtendedAttributeDocument.ExtendedAttribute ea6
				= eas.addNewExtendedAttribute();
			ea6.setName(ActivityElement.ENGINE_NANE);
			ea6.setValue(engineName);
		}
	}

	/**
	 * 遷移をXmlObjectに変換する。
	 * 
	 * @param tr	XmlObject
	 * @param elem	遷移
	 * @throws XmlException XML例外
	 */
	private void convertTransitionElement(
	TransitionDocument.Transition tr,
	TransitionElement elem
	)
	throws XmlException
	{
		tr.setId(elem.getID());
		tr.setName(elem.getName());
		tr.setDescription(elem.getDescription());
		
		tr.setFrom(elem.getSource().getID());
		tr.setTo(elem.getDestination().getID());

		Expression cond = elem.getCondition();

		if (cond != null) {
			ConditionDocument.Condition condition = tr.addNewCondition();
			ConditionDocument.Condition.Type.Enum type
				= ConditionDocument.Condition.Type.Enum.forInt(elem.getType());
			if (type.equals(ConditionDocument.Condition.Type.CONDITION)) {
				if (cond != null) {
					Expression exp = new ConditionalExpression(cond);
					ConditionalExpressionDocument cDoc
						= ConditionalExpressionDocument.Factory.newInstance();
					ConditionalExpressionDocument.ConditionalExpression ce
						= cDoc.addNewConditionalExpression();
					exp.saveTo(ce);
					condition.set(cDoc);
				}
			}
			condition.setType(type);
		}
	}
}
