package net.w_horse.excelpojo.xml;

import java.util.List;

import net.w_horse.excelpojo.excel.cellseeker.CellSeekerManager;
import net.w_horse.excelpojo.xml.tag.DataDirection;
import net.w_horse.excelpojo.xml.tag.RetrieveFrom;
import net.w_horse.excelpojo.xml.tag.RetrieveType;
import net.w_horse.excelpojo.xml.tag.Use;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

public class ExcelPOJOXmlParser extends AbstractBeanDefinitionParser
								implements CellSeekerFactoryBeanAcceptableXMLParser {

	private static final String TAG_NM_SHEET_NAME = "sheetName";
	private static final String TAG_NM_TARGET_CLASS = "targetClass";
	private static final String TAG_NM_TARGET_BEAN_PROPERTY = "targetBean-property";
	private static final String TAG_NM_RETRIEVE_TYPE = RetrieveType.getTagName();
	private static final String TAG_NM_LABEL = "label";
	private static final String TAG_NM_PREVIOUS_LABEL = "previousLabel";
	private static final String TAG_NM_POSITION = "position";
	private static final String TAG_NM_RETRIEVE_FROM = RetrieveFrom.getTagName();
	private static final String TAG_NM_VALUE = "value";
	private static final String TAG_NM_MARGED_LABEL = "margedLabel";
	private static final String TAG_NM_MARGED_ROWS = "margedRows";
	private static final String TAG_NM_TERMINATE = "terminate";
	private static final String TAG_NM_USE = Use.getTagName();
	private static final String TAG_NM_DATA_DIRECTION = DataDirection.getTagName();
	private static final String TAG_NM_NAME = "name";
	private static final String TAG_NM_BEAN = "bean";
	private static final String TAG_NM_REF = "ref";
	private static final String TAG_NM_ELEMENT_BEAN = "elementBean";
	private static final String TAG_NM_LIST_CLASS = "listClass";
	private static final String TAG_NM_SUCCEED_FIELDS = "succeedFields";
	private static final String TAG_NM_RANGE = "range";
	private static final String TAG_NM_CONVERTER = "converter";

	private static final String TAG_NM_SCOPE = "scope";
	private static final String TAG_NM_CLASS = "class";
	private static final String TAG_NM_INDEX = "index";

	private static final String BRIDGE_PROPERTY_NM_TARGET_BEAN_PROPERTIES = "targetClassProperties";
	private static final String BRIDGE_PROPERTY_NM_CELL_SEEKER = "cellSeeker";
	private static final String BRIDGE_ROPERTY_NM_SHEET_NAME = TAG_NM_SHEET_NAME;
	private static final String BRIDGE_PROPERTY_NM_TARGET_CLASS = TAG_NM_TARGET_CLASS;
	private static final String BRIDGE_PROPERTY_NM_RETRIEVE_TYPE = TAG_NM_RETRIEVE_TYPE;
	private static final String BRIDGE_PROPERTY_NM_CONVERTER = TAG_NM_CONVERTER;

	private static final String CELL_SEEKER_PROPERTY_NM_LABEL = TAG_NM_LABEL;
	private static final String CELL_SEEKER_PROPERTY_NM_POSITION = TAG_NM_POSITION;
	private static final String CELL_SEEKER_PROPERTY_NM_RETRIEVE_FROM = TAG_NM_RETRIEVE_FROM;
	private static final String CELL_SEEKER_PROPERTY_NM_VALUE = TAG_NM_VALUE;
	private static final String CELL_SEEKER_PROPERTY_NM_MARGED_ROWS = TAG_NM_MARGED_ROWS;
	private static final String CELL_SEEKER_PROPERTY_NM_TERMINATE = TAG_NM_TERMINATE;
	private static final String CELL_SEEKER_PROPERTY_NM_MARGED_LABEL = TAG_NM_MARGED_LABEL;
	private static final String CELL_SEEKER_PROPERTY_NM_LIST_CLASS = TAG_NM_LIST_CLASS;
	private static final String CELL_SEEKER_PROPERTY_NM_PREVIOUS_LABEL = TAG_NM_PREVIOUS_LABEL;
	private static final String CELL_SEEKER_PROPERTY_NM_USE = TAG_NM_USE;
	private static final String CELL_SEEKER_PROPERTY_NM_DATA_DIRECTION = TAG_NM_DATA_DIRECTION;
	private static final String CELL_SEEKER_PROPERTY_NM_RANGE = TAG_NM_RANGE;
	private static final String CELL_SEEKER_PROPERTY_NM_SUCCEED_FIELDS = TAG_NM_SUCCEED_FIELDS;
	private static final String CELL_SEEKER_PROPERTY_NM_EXCEL_POJO_BRIDGE = "excelPOJOBridge";

	private static final String CONVERTER_PROPERTY_NM_CONVERTER_CLASS_NAME = "converterClassName";
	private static final String CONVERTER_PROPERTY_NM_CONSTRACTOR_ARGS = "constractorArgs";
	private static final String CONVERTER_PROPERTY_NM_PROPERTIES = "properties";


	@SuppressWarnings("unchecked")
	@Override
	protected AbstractBeanDefinition parseInternal(Element element,
											ParserContext parserContext) {
		BeanDefinitionBuilder xpojoBeanBeanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(ExcelPOJOBeanFactoryBean.class);

		String scope = element.getAttribute(TAG_NM_SCOPE);
		if (StringUtils.hasLength(scope)) {
			xpojoBeanBeanDefinitionBuilder.setScope(scope);
		}

		// シート名の取得
		String sheetName = element.getAttribute(TAG_NM_SHEET_NAME);
		if (!StringUtils.hasLength(sheetName)) {
			sheetName = DomUtils.getChildElementValueByTagName(element, TAG_NM_SHEET_NAME);
		}
		xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_ROPERTY_NM_SHEET_NAME, sheetName);

		// ターゲットの取得
		String targetClass = element.getAttribute(TAG_NM_TARGET_CLASS);
		xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_TARGET_CLASS, targetClass);

		// ターゲットクラスが指定されていない場合
		// ⇒表を直接読み込む場合
		if (!StringUtils.hasLength(targetClass)) {
			String retrieveType = element.getAttribute(TAG_NM_RETRIEVE_TYPE);
			BeanDefinitionBuilder seekerBeanDefinitionBuilder
				= buildCellSeekerBeanDefinitionBuilder(element, parserContext);
			xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_CELL_SEEKER,
												seekerBeanDefinitionBuilder.getBeanDefinition());
			xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_RETRIEVE_TYPE, retrieveType);
		}

		// フィールドの読み込み情報取得
		ManagedMap targetClassProperties = new ManagedMap();
		List<Element> nodeList
			= DomUtils.getChildElementsByTagName(element, TAG_NM_TARGET_BEAN_PROPERTY);
		for(Element child : nodeList) {
			BeanDefinitionBuilder seekerBeanDefinitionBuilder
				= buildCellSeekerBeanDefinitionBuilder(child, parserContext);

			// Converterのセット
			seekerBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_CONVERTER,
					createConverterBeanDefinition(child, parserContext));

			// フィールドとCellSeekerのマップを登録
			targetClassProperties.put(child.getAttribute(TAG_NM_NAME),
							seekerBeanDefinitionBuilder.getBeanDefinition());
		}
		xpojoBeanBeanDefinitionBuilder.addPropertyValue(
				BRIDGE_PROPERTY_NM_TARGET_BEAN_PROPERTIES, targetClassProperties);

		return xpojoBeanBeanDefinitionBuilder.getBeanDefinition();
	}


	// CellSeekerごとに異なる内容をセットする
	@Override
	public BeanDefinitionBuilder parse(Element element,
						LabeledCellSeekerFactoryBean factory,
						ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_LABEL, element.getAttribute(TAG_NM_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RETRIEVE_FROM, element.getAttribute(TAG_NM_RETRIEVE_FROM));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_MARGED_ROWS, element.getAttribute(TAG_NM_MARGED_ROWS));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_MARGED_LABEL, element.getAttribute(TAG_NM_MARGED_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_USE, element.getAttribute(TAG_NM_USE));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RANGE, element.getAttribute(TAG_NM_RANGE));

		return beanDefinitionBuilder;
	}
	@Override
	public BeanDefinitionBuilder parse(Element element,
						PointedCellSeekerFactoryBean factory,
						ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_POSITION, element.getAttribute(TAG_NM_POSITION));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_USE, element.getAttribute(TAG_NM_USE));

		return beanDefinitionBuilder;
	}
	@Override
	public BeanDefinitionBuilder parse(Element element,
						ConstantValueCellSeekerFactoryBean factory,
						ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_VALUE, getTagOrElementValue(element, TAG_NM_VALUE));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_USE, element.getAttribute(TAG_NM_USE));

		return beanDefinitionBuilder;
	}
	@Override
	public BeanDefinitionBuilder parse(Element element,
						VerticalRepeatsSeekerFactoryBean factory,
						ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		setRepeatsSeeker(element, beanDefinitionBuilder,
					parserContext,
					RetrieveFrom.BOTTOM.getValue(),
					DataDirection.RIGHT.getValue());

		return beanDefinitionBuilder;
	}
	@Override
	public BeanDefinitionBuilder parse(Element element,
			HorizontalRepeatsSeekerFactoryBean factory, ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		setRepeatsSeeker(element, beanDefinitionBuilder,
					parserContext,
					RetrieveFrom.RIGHT.getValue(),
					DataDirection.DOWN.getValue());

		return beanDefinitionBuilder;
	}
	@Override
	public BeanDefinitionBuilder parse(Element element,
						MappedCellSeekerFactoryBean factory,
						ParserContext parserContext) {
		BeanDefinitionBuilder beanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(factory.getClass());

		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_PREVIOUS_LABEL, element.getAttribute(TAG_NM_PREVIOUS_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_POSITION, element.getAttribute(TAG_NM_POSITION));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RETRIEVE_FROM, element.getAttribute(TAG_NM_RETRIEVE_FROM));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_DATA_DIRECTION, element.getAttribute(TAG_NM_DATA_DIRECTION));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_MARGED_LABEL, element.getAttribute(TAG_NM_MARGED_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_USE, element.getAttribute(TAG_NM_USE));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RANGE, element.getAttribute(TAG_NM_RANGE));

		return beanDefinitionBuilder;
	}

	private void setElementBeanAttribute(Element elementBean, Element parentElement) {
		elementBean.setAttribute(TAG_NM_LABEL, parentElement.getAttribute(TAG_NM_LABEL));
		elementBean.setAttribute(TAG_NM_POSITION, parentElement.getAttribute(TAG_NM_POSITION));
		elementBean.setAttribute(TAG_NM_RETRIEVE_FROM, parentElement.getAttribute(TAG_NM_RETRIEVE_FROM));
		elementBean.setAttribute(TAG_NM_TERMINATE, parentElement.getAttribute(TAG_NM_TERMINATE));
		elementBean.setAttribute(TAG_NM_MARGED_ROWS, parentElement.getAttribute(TAG_NM_MARGED_ROWS));
		elementBean.setAttribute(TAG_NM_PREVIOUS_LABEL, parentElement.getAttribute(TAG_NM_PREVIOUS_LABEL));
		elementBean.setAttribute(TAG_NM_LIST_CLASS, parentElement.getAttribute(TAG_NM_LIST_CLASS));
		elementBean.setAttribute(TAG_NM_USE, parentElement.getAttribute(TAG_NM_USE));
		elementBean.setAttribute(TAG_NM_RANGE, parentElement.getAttribute(TAG_NM_RANGE));
	}

	@SuppressWarnings("unchecked")
	private void setRepeatsSeeker(Element element, BeanDefinitionBuilder beanDefinitionBuilder,
			ParserContext parserContext, String defaultRetrieveFrom, String dataDirection) {

		Element elementBeanElement = getChildElement(element, TAG_NM_ELEMENT_BEAN);
		if (elementBeanElement == null) {
			elementBeanElement = element;
		} else {
			setElementBeanAttribute(elementBeanElement, element);
		}
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_LABEL, elementBeanElement.getAttribute(TAG_NM_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_POSITION, elementBeanElement.getAttribute(TAG_NM_POSITION));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_TERMINATE, elementBeanElement.getAttribute(TAG_NM_TERMINATE));
		String retrievedFrom = elementBeanElement.getAttribute(TAG_NM_RETRIEVE_FROM);
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RETRIEVE_FROM, (retrievedFrom.isEmpty() ? defaultRetrieveFrom : retrievedFrom));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_MARGED_ROWS, Boolean.valueOf(elementBeanElement.getAttribute(TAG_NM_MARGED_ROWS)));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_MARGED_LABEL,elementBeanElement.getAttribute(TAG_NM_MARGED_LABEL));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_LIST_CLASS, elementBeanElement.getAttribute(TAG_NM_LIST_CLASS));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_SUCCEED_FIELDS, elementBeanElement.getAttribute(TAG_NM_SUCCEED_FIELDS));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_USE, elementBeanElement.getAttribute(TAG_NM_USE));
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_RANGE, elementBeanElement.getAttribute(TAG_NM_RANGE));

		Element beanElement = getChildElement(elementBeanElement, TAG_NM_BEAN);
		BeanDefinitionBuilder xpojoBeanBeanDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(ExcelPOJOBeanFactoryBean.class);
		xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_TARGET_CLASS, beanElement.getAttribute(TAG_NM_TARGET_CLASS));
		ManagedMap elementBeanProperties = new ManagedMap();
		List<Element> nodeList = DomUtils.getChildElementsByTagName(beanElement, TAG_NM_TARGET_BEAN_PROPERTY);
		for(Element child : nodeList) {
			child.setAttribute(TAG_NM_RETRIEVE_FROM, defaultRetrieveFrom);
			child.setAttribute(TAG_NM_DATA_DIRECTION, dataDirection);
			BeanDefinitionBuilder fieldSeekerBeanDefinitionBuilder
				= buildCellSeekerBeanDefinitionBuilder(child, parserContext);

			// Converterのセット
			fieldSeekerBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_CONVERTER,
					createConverterBeanDefinition(child, parserContext));

			// フィールドとCellSeekerのマップを登録
			elementBeanProperties.put(child.getAttribute(TAG_NM_NAME), fieldSeekerBeanDefinitionBuilder.getBeanDefinition());
		}
		xpojoBeanBeanDefinitionBuilder.addPropertyValue(BRIDGE_PROPERTY_NM_TARGET_BEAN_PROPERTIES, elementBeanProperties);
		beanDefinitionBuilder.addPropertyValue(CELL_SEEKER_PROPERTY_NM_EXCEL_POJO_BRIDGE, xpojoBeanBeanDefinitionBuilder.getBeanDefinition());
	}

	private BeanDefinitionBuilder buildCellSeekerBeanDefinitionBuilder(
										Element element, ParserContext parserContext) {
		String retrieveType = element.getAttribute(TAG_NM_RETRIEVE_TYPE);
		AbstractCellSeekerFactoryBean cellSeekerFactoryBean = null;
		try {
			cellSeekerFactoryBean = CellSeekerManager.getCellSeekerFactoryBeanInstance(
													RetrieveType.getElement(retrieveType));
		} catch (Exception e) {
		}

		return cellSeekerFactoryBean.parse(element, this, parserContext);
	}
	private BeanDefinition createBeanTypeBeanDefinition(Element element,
														ParserContext parserContext) {

		// 要素にbeanが定義されている場合
		Element beanElement = DomUtils.getChildElementByTagName(element, TAG_NM_BEAN);
		if (beanElement != null) {
			// 変換タグにbean要素が設定されていれば、
			// そのbeanで作成された変換クラスを作成するBeanDefinitionを返す
			BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
			BeanDefinitionHolder beanDefinition
				= delegate.parseBeanDefinitionElement(beanElement);

			return beanDefinition.getBeanDefinition();
		}

		// refでbean要素が定義されている場合
		Element refElement = DomUtils.getChildElementByTagName(element, TAG_NM_REF);
		if (element.hasAttribute(TAG_NM_REF)
			|| (refElement != null)
		) {
			String refId;
			if (element.hasAttribute(TAG_NM_REF)) {
				refId = element.getAttribute(TAG_NM_REF);
			} else {
				refId = refElement.getAttribute(TAG_NM_BEAN);
			}
			Element refBeanElement = element.getOwnerDocument().getElementById(refId);
			if (refBeanElement == null) {
				return null;
			}
			String className = refBeanElement.getAttribute(TAG_NM_CLASS);
			BeanDefinitionParserDelegate delegate = parserContext.getDelegate();
			return delegate.parseBeanDefinitionElement(refBeanElement, className, null);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	private BeanDefinition createConverterBeanDefinition(Element element,
														ParserContext parserContext) {
		BeanDefinitionBuilder nothingConverterDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(NothingConverterFactoryBean.class);

		Element converterElement = null;
		if (element.hasAttribute(TAG_NM_CONVERTER)) {
			// 属性でクラスが指定されている場合はそのクラスを
			// デフォルトコンストラクタでインスタンス化してセット
			String converterClassName = element.getAttribute(TAG_NM_CONVERTER);
			BeanDefinitionBuilder converterDefinitionBuilder
				= BeanDefinitionBuilder.rootBeanDefinition(ConverterFactoryBean.class);
			converterDefinitionBuilder.addPropertyValue(CONVERTER_PROPERTY_NM_CONVERTER_CLASS_NAME, converterClassName);
			return converterDefinitionBuilder.getBeanDefinition();
		} else {
			converterElement = DomUtils.getChildElementByTagName(element, TAG_NM_CONVERTER);
			if (converterElement == null) {
				// 属性にも要素にも指定がない場合は何も変換しないクラスを作成する
				// BeanDefinitionを返す
				return nothingConverterDefinitionBuilder.getBeanDefinition();
			}
		}

		// 要素にbeanが定義されている場合またはrefでbean要素が定義されている場合
		Element beanElement = DomUtils.getChildElementByTagName(converterElement, TAG_NM_BEAN);
		Element refElement = DomUtils.getChildElementByTagName(converterElement, TAG_NM_REF);
		if ((beanElement != null)
			|| (refElement != null)
			|| (converterElement.hasAttribute(TAG_NM_REF))
		) {
			BeanDefinition beanDefinition
				= createBeanTypeBeanDefinition(converterElement, parserContext);
			if (beanDefinition != null) {
				return beanDefinition;
			}
			// refの参照先IDが存在しない場合は何も変換しないクラスを作成する
			// BeanDefinitionを返す
			return nothingConverterDefinitionBuilder.getBeanDefinition();
		}

		// 変換クラスの名称
		String converterClassName = converterElement.getAttribute(TAG_NM_CLASS);
		if (!StringUtils.hasLength(converterClassName)) {
			// 属性にも要素にもクラスの指定がない場合は何も変換しないクラスを作成する
			// BeanDefinitionを返す
			return nothingConverterDefinitionBuilder.getBeanDefinition();
		}
		BeanDefinitionBuilder converterDefinitionBuilder
			= BeanDefinitionBuilder.rootBeanDefinition(ConverterFactoryBean.class);
		converterDefinitionBuilder.addPropertyValue(CONVERTER_PROPERTY_NM_CONVERTER_CLASS_NAME, converterClassName);

		// プロパティが指定されていた場合
		ManagedMap properties = new ManagedMap();
		List<Element> propertiesNodeList = DomUtils.getChildElementsByTagName(converterElement, "property");
		for(Element propertyElement : propertiesNodeList) {
			properties.put(propertyElement.getAttribute(TAG_NM_NAME),
					getTagOrElementValue(propertyElement, TAG_NM_VALUE));
		}
		converterDefinitionBuilder.addPropertyValue(CONVERTER_PROPERTY_NM_PROPERTIES, properties);

		// コンストラクタが指定されていた場合
		ManagedMap constractorArgs = new ManagedMap();
		List<Element> constractorArgsNodeList = DomUtils.getChildElementsByTagName(converterElement, "constructor-arg");
		int index = 0;
		for(Element constractorArgElement : constractorArgsNodeList) {
			String key = constractorArgElement.getAttribute(TAG_NM_INDEX);
			if (!StringUtils.hasLength(key)) {
				key = String.valueOf(index);
			}
			constractorArgs.put(key,
					getTagOrElementValue(constractorArgElement, TAG_NM_VALUE));
			index++;
		}
		converterDefinitionBuilder.addPropertyValue(CONVERTER_PROPERTY_NM_CONSTRACTOR_ARGS, constractorArgs);

		return converterDefinitionBuilder.getBeanDefinition();
	}

	private String getTagOrElementValue(Element element, String attrName) {
		String value = element.getAttribute(attrName);
		if (!StringUtils.hasLength(value)) {
			value = DomUtils.getChildElementValueByTagName(element, attrName);
		}

		return value;
	}
	private Element getChildElement(Element element, String attrName) {
		Element child = DomUtils.getChildElementByTagName(element, attrName);
		if (child == null) {
			// 直接タグに要素が書き込まれていない場合は
			// ref から参照先を取得する
			Element ref = DomUtils.getChildElementByTagName(element, TAG_NM_REF);
			if (ref == null) return null;

			child = ref.getOwnerDocument().getElementById(ref.getAttribute(attrName));
		}
		return child;
	}

}
