/*
* garoon-google
* Copyright (c) 2015 Cybozu
*
* Licensed under the MIT License
*/
package com.cybozu.garoon3.schedule;

import java.io.IOException;
import java.io.StringReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.xml.dtm.ref.DTMNodeList;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
 * スケジュールAPI関連で利用するユーティリティクラスです。<br />
 * <br />
 * このクラスは二つのメソッドを提供します。<br />
 * ひとつはAPIから取得したレスポンスを com.cybozu.garoon3.schedule.Event にパースする getEventList メソッドです。<br />
 * もうひとつは、作成した Event インスタンスをリクエストで利用する形に変換する toParameter メソッドです。
 *
 * @author $api_author Garoon Team@Cybozu$
 * @version $api_version ver 1.0.0$
 */
public class ScheduleUtil {

	/**
	 * OMElement を Event インスタンスのリストに変換します。<br />
	 * input が想定した形式でない場合、空のリストを返します。
	 *
	 * @param input
	 * @return 予定一覧
	 */
	public static List<Event> getEventList( OMElement input, Integer apiVersion )
	{
		List<Event> events = new ArrayList<Event>();

		input = (OMElement) input.getChildElements().next();
		Document document = getDocument(input);
		String inputString = input.toString();

		DTMNodeList eventNodes = null;
		try{
			XPath xPath = XPathFactory.newInstance().newXPath();
			XPathExpression xPathExpression = xPath.compile( "returns/schedule_event" );
			eventNodes = (DTMNodeList)xPathExpression.evaluate(document, XPathConstants.NODESET);
		} catch(XPathExpressionException e) {
			return Collections.emptyList();
		}

		for (int i = 0; i < eventNodes.getLength(); i++){
			TimeZone timezone = null;
			
			Event event = new Event();
			Node eventNode = eventNodes.item(i);
			NamedNodeMap attrs = eventNode.getAttributes();

			//ID
			Node attr = attrs.getNamedItem("id");
			event.setId( Integer.valueOf(attr.getNodeValue()) );

			//Version
			attr = attrs.getNamedItem("version");
			event.setVersion( Long.valueOf(attr.getNodeValue()) );

			//Event Type
			attr = attrs.getNamedItem("event_type");
			if( attr != null )
				event.setEventType( EventType.valueOf(attr.getNodeValue().toUpperCase()) );

			//Public Type
			attr = attrs.getNamedItem("public_type");
			if( attr != null )
				event.setPublicType( PublicType.valueOf(attr.getNodeValue().toUpperCase()) );

			//Plan
			attr = attrs.getNamedItem("plan");
			if( attr != null )
				event.setPlan( attr.getNodeValue() );

			//Detail
			attr = attrs.getNamedItem("detail");
			if( attr != null )
				event.setDetail( attr.getNodeValue() );

			//Description
			attr = attrs.getNamedItem("description");
			if( attr != null )
				event.setDescription( attr.getNodeValue() );

			//Description(改行対応)
			String regex = "id=\"" + event.getId() + "\" [^<>]*description=\"([^\"]*)\"";
			Pattern p = Pattern.compile(regex);
			Matcher m = p.matcher(inputString);
			if( m.find() ) {
				event.setDescription( m.group(1) );
			}

			//TimeZone
			attr = attrs.getNamedItem("timezone");
			if( attr != null ){
				timezone = TimeZone.getTimeZone(attr.getNodeValue());
				event.setTimezone( timezone );
			}

			//StartOnly
			attr = attrs.getNamedItem("start_only");
			if (attr != null) {
				event.setStartOnly(Boolean.parseBoolean(attr.getNodeValue()));
			}

			//AllOnly
			attr = attrs.getNamedItem("allday");
			if (attr != null) {
				event.setAllDay(Boolean.parseBoolean(attr.getNodeValue()));
			}

			event.setRepeatInfo( getRepeatInfo(eventNode, timezone) );
			event.setMembers( getMemberList(eventNode) );
			event.setSpans( getSpanList(eventNode, timezone) );
			event.setFollows( getFollowList(eventNode, inputString, timezone) );
			event.setCustomer( getCustomer(eventNode) );
			event.setObservers( getObserverList(eventNode, apiVersion) );
			events.add( event );
			//System.out.println(event);
		}
		return events;
	}

	private static RepeatInfo getRepeatInfo( Node eventNode, TimeZone timezone )
	{
		NodeList children = eventNode.getChildNodes();

		Node repeatInfoNode = null;
		for( int i=0; i<children.getLength(); i++)
			if( children.item(i).getNodeName().equals("repeat_info") )
				repeatInfoNode = children.item(i);

		if( repeatInfoNode == null )
			return null;

		RepeatInfo info = new RepeatInfo();

		//get condition node
		Node conditionNode = repeatInfoNode.getChildNodes().item(1);

		String repeatEventType = getAttribute(conditionNode, "type").toUpperCase();
		if (repeatEventType.equals("1STWEEK")) {
			info.setType(RepeatEventType.WEEK_1ST);
		}
		else if (repeatEventType.equals("2NDWEEK")) {
			info.setType(RepeatEventType.WEEK_2ND);
		}
		else if (repeatEventType.equals("3RDWEEK")) {
			info.setType(RepeatEventType.WEEK_3RD);
		}
		else if (repeatEventType.equals("4THWEEK")) {
			info.setType(RepeatEventType.WEEK_4TH);
		}
		else if (repeatEventType.equals("LASTWEEK")) {
			info.setType(RepeatEventType.WEEK_LAST);
		}
		else {
			info.setType( RepeatEventType.valueOf(getAttribute(conditionNode, "type").toUpperCase()) );
		}
		info.setDay( Integer.valueOf(getAttribute(conditionNode, "day")));

		//SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		try{
			//info.setStartDate( formatter.parse(getAttribute(conditionNode,"start_date")) );
			info.setStartDate( parseDate(getAttribute(conditionNode,"start_date"), timezone) );
		}catch(ParseException e)
		{}

		try{
			//info.setEndDate( formatter.parse(getAttribute(conditionNode,"end_date")) );
			info.setEndDate( parseDate(getAttribute(conditionNode,"end_date"), timezone) );
		}catch(ParseException e)
		{}

		info.setStartTime( getAttribute(conditionNode, "start_time") );
		info.setEndTime( getAttribute(conditionNode, "end_time") );
		info.setWeek( Integer.valueOf(getAttribute(conditionNode,"week")));

		// get exclusive datetimes
		Node exDateTimesNode = repeatInfoNode.getChildNodes().item(3);
		NodeList exDateTimeList = exDateTimesNode.getChildNodes();
		List<Span> exclusiveDateTimes = new ArrayList<Span>();
		for( int i=0; i<exDateTimeList.getLength(); i++ )
		{
			Node item = exDateTimeList.item(i);

			if( !"exclusive_datetime".equals(item.getNodeName()))
				continue;


			Span span = new Span();
			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
			Date start, end;
			
			
			//Date start = parseISO8601( getAttribute(item, "start") );
			Date start_date_only = null, end_date_only = null;
			try {
				//start_date_only = parseISO8601( getAttribute(item, "start") );
				start_date_only = parseWhenDatetime( getAttribute(item, "start"), timezone );
			} catch (ParseException e) {
				try {
					//start_date_only = parseISO8601_3( getAttribute(item, "start") );
					start_date_only = parseExclusiveDatetime( getAttribute(item, "start"), timezone );
				} catch (ParseException e1) {

				}
			}			
			try {
				Date time_only = sdf.parse(getAttribute(conditionNode, "start_time"));
				//start = new Date(start_date_only.getTime() + time_only.getTime());
				start = new Date(start_date_only.getTime() + time_only.getTime() + TimeZone.getDefault().getRawOffset());
			} catch(Exception e) {
				start = new Date(start_date_only.getTime());
			}
			span.setStart(start);


			//Date end = parseISO8601( getAttribute(item, "end") );
			try {
				//end_date_only = parseISO8601( getAttribute(item, "end") );
				end_date_only = parseWhenDatetime( getAttribute(item, "end"), timezone );
			} catch (ParseException e) {
				try {
					//end_date_only = parseISO8601_3( getAttribute(item, "end") );
					end_date_only = parseExclusiveDatetime( getAttribute(item, "end"), timezone );
				} catch (ParseException e1) {
				}
			}
			try {
				Date time_only = sdf.parse(getAttribute(conditionNode, "end_time"));
				//end = new Date(end_date_only.getTime() + time_only.getTime());
				end = new Date(end_date_only.getTime() + time_only.getTime() + TimeZone.getDefault().getRawOffset());
			} catch(Exception e) {
				end = new Date(end_date_only.getTime());
			}
			span.setEnd(end);


			exclusiveDateTimes.add( span );
		}

		info.setExclusiveDateTimes(exclusiveDateTimes);
		return info;
	}

	private static List<Follow> getFollowList( Node eventNode, String inputString, TimeZone timezone )
	{
		NodeList children = eventNode.getChildNodes();

		Node followsNode = null;
		for( int i=0; i<children.getLength(); i++)
			if( children.item(i).getNodeName().equals("follows") )
				followsNode = children.item(i);

		if( followsNode == null )
			return Collections.emptyList();

		children = followsNode.getChildNodes();
		List<Follow> follows = new ArrayList<Follow>();
		for( int i=0; i<children.getLength(); i++)
		{
			if( !children.item(i).getNodeName().equals("follow") )
				continue;

			Node followNode = children.item(i);

			int id = Integer.valueOf( getAttribute(followNode, "id") );
			long version = Long.valueOf( getAttribute(followNode, "version") );
			String text = getAttribute(followNode, "text");

			//follow(改行対応)
			String regex = "id=\"" + id + "\" [^<>]*text=\"([^\"]*)\"";
			Pattern p = Pattern.compile(regex);
			Matcher m = p.matcher(inputString);
			if( m.find() ) {
				text = m.group(1);
			}

			Node c = followNode.getChildNodes().item(1);
			Date date = null;
			try {
				//date = parseISO8601( getAttribute(c, "date") );
				date = parseWhenDatetime( getAttribute(c, "date"), timezone );
			} catch (ParseException e) {
			}

			// 削除されたユーザーの場合、user_idが取得できないので0をセットする
			int userId = 0;
			try {
				userId = Integer.valueOf( getAttribute(c, "user_id") );
			} catch (NumberFormatException e) {
			}

			String name = getAttribute(c, "name");

			Follow follow = new Follow();
			follow.setId(id);
			follow.setVersion(version);
			follow.setText(text);
			follow.setCreatorId(userId);
			follow.setCreatorName(name);
			follow.setDatetime(date);
			follows.add( follow );
		}
		return follows;
	}

	private static Customer getCustomer( Node eventNode )
	{
		NodeList children = eventNode.getChildNodes();

		Node customerNode = null;
		for( int i=0; i<children.getLength(); i++)
			if( children.item(i).getNodeName().equals("customer") )
				customerNode = children.item(i);

		if( customerNode == null )
			return new Customer();

		String name 		= getAttribute(customerNode, "name");
		String address 		= getAttribute(customerNode, "address");
		String phone 		= getAttribute(customerNode, "phone");
		String map	 		= getAttribute(customerNode, "map");
		String route 		= getAttribute(customerNode, "route");
		String routeFare	= getAttribute(customerNode, "route_fare");
		String routeTime	= getAttribute(customerNode, "route_time");
		String zipCode		= getAttribute(customerNode, "zipcode");

		Customer customer = new Customer();
		customer.setName(name);
		customer.setAddress( address );
		customer.setPhone(phone);
		customer.setRoute(route);
		customer.setMap(map);
		customer.setRouteFare(routeFare);
		customer.setRouteTime(routeTime);
		customer.setZipCode(zipCode);
		return customer;
	}

	private static List<Observer> getObserverList(Node eventNode, Integer apiVersion) {
		NodeList children = eventNode.getChildNodes();

		Node observersNode = null;
		for (int i = 0; i < children.getLength(); i++) {
			if (children.item(i).getNodeName().equals("observers")) {
				observersNode = children.item(i);
			}
		}

		if (observersNode == null) {
			return Collections.emptyList();
		}

		children = observersNode.getChildNodes();
		List<Observer> observers = new ArrayList<Observer>();
		for (int i = 0; i < children.getLength(); i++) {
			if (!children.item(i).getNodeName().equals("observer")) {
				continue;
			}

			Node observerNode = children.item(i);

			Integer observerId, observerOrder;
			// APIバージョン180から構成が変わったため
			if (apiVersion >= 180) {
				Node c = observerNode.getChildNodes().item(1);
				observerId = Integer.valueOf(getAttribute(c, "id" ));
				observerOrder = Integer.valueOf(getAttribute(c, "order"));
			} else {
				observerId = Integer.valueOf(getAttribute(observerNode, "id" ));
				observerOrder = Integer.valueOf(getAttribute(observerNode, "order"));
			}

			Observer observer = new Observer();
			observer.setID(observerId);
			observer.setOrder(observerOrder);

			observers.add(observer);
		}

		return observers;
	}

	private static List<Span> getSpanList( Node eventNode, TimeZone timezone )
	{
		NodeList children = eventNode.getChildNodes();

		Node whenNode = null;
		for( int i=0; i<children.getLength(); i++)
			if( children.item(i).getNodeName().equals("when") )
				whenNode = children.item(i);

		if( whenNode == null )
			return null;

		children = whenNode.getChildNodes();
		List<Span> spans = new ArrayList<Span>();
		for( int i=0; i<whenNode.getChildNodes().getLength(); i++)
		{
			if( !children.item(i).getNodeName().equals("datetime") && !children.item(i).getNodeName().equals("date") )
				continue;

			Node datetimeNode = children.item(i);

			String start = getAttribute(datetimeNode, "start");
			String end   = getAttribute(datetimeNode, "end");

			Span span = new Span();

			try {
				if( start != null){
					//span.setStart( parseISO8601(start) );
					span.setStart( parseWhenDatetime(start, timezone) );
				}
			} catch (ParseException e) {
				try {
					//span.setStart( parseISO8601_2(start) );
					span.setStart( parseDate(start, timezone) );
				} catch (ParseException e1) {
				}
			}
			try {
				if( end != null ){
					//span.setEnd( parseISO8601(end) );
					span.setEnd( parseWhenDatetime(end, timezone) );
				}
			} catch (ParseException e) {
				try {
					//span.setEnd( parseISO8601_2(end) );
					span.setEnd( parseDate(end, timezone) );
				} catch (ParseException e1) {
				}
			}
			spans.add(span);
		}
		return spans;
	}

	private static List<Member> getMemberList( Node eventNode )
	{
		NodeList children = eventNode.getChildNodes();

		Node membersNode = null;
		for( int i=0; i<children.getLength(); i++)
			if( children.item(i).getNodeName().equals("members") )
				membersNode = children.item(i);

		if( membersNode == null )
			return Collections.emptyList();

		children = membersNode.getChildNodes();
		List<Member> members = new ArrayList<Member>();
		for( int i=0; i<children.getLength(); i++)
		{
			if( children.item(i).getNodeType() == Node.TEXT_NODE)
				continue;

			Node memberNode = children.item(i);
			Node c = memberNode.getChildNodes().item(1);

			MemberType type = MemberType.valueOf( c.getNodeName().toUpperCase() );
			int id 	  = Integer.valueOf( getAttribute(c, "id") );
			int order = Integer.valueOf( getAttribute(c, "order" ) );
			String name = String.valueOf( getAttribute(c, "name" ) );

			Member member = new Member( type, id, order, name );
			members.add( member );
		}
		return members;
	}

	private static Document getDocument( OMElement input )
	{
		Document document = null;
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			document = builder.parse(new InputSource(new StringReader(input.toString())));
		} catch(IOException e) {
		} catch (SAXException e) {
		} catch (ParserConfigurationException e) {
		}

		return document;
	}

	/*
	private static Date parseISO8601(String source) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", Locale.JAPAN);
		formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
		return formatter.parse(source);
	}

	private static Date parseISO8601_2(String source) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd", Locale.US);
		formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
		return formatter.parse(source);
	}

	private static Date parseISO8601_3(String source) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss", Locale.US);
		formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
		return formatter.parse(source.replaceAll(":([0-9]{2})$", "$1"));
	}
	*/
	
	private static Date parseWhenDatetime(String source, TimeZone timezone) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'");
		formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
		return formatter.parse(source);
	}
	
	private static Date parseDate(String source, TimeZone timezone) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd");
		formatter.setTimeZone(timezone);
		return formatter.parse(source);
	}
	
	private static Date parseExclusiveDatetime(String source, TimeZone timezone) throws ParseException
	{
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ");
		formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
		return formatter.parse(source.replaceAll(":([0-9]{2})$", "$1"));
	}

	private static String getAttribute( Node node, String name)
	{
		try{
			return node.getAttributes().getNamedItem(name).getNodeValue();
		}catch( Exception e ) {
			return "";
		}
	}

	/**
	 * 予定一覧を API のパラメーターとして利用可能な形式に変換します。
	 *
	 * @param events 予定一覧
	 * @return パラメーター
	 */
	public static OMElement toParameter( List<Event> events )
	{
		OMFactory omFactory = OMAbstractFactory.getOMFactory();
		OMElement parameters = omFactory.createOMElement("parameters", null);
		for( Event event : events )
		{
			OMElement eventNode = omFactory.createOMElement("schedule_event", null);
			eventNode.addAttribute("id", String.valueOf(event.getId()), null);
			eventNode.addAttribute("version", String.valueOf(event.getVersion()), null);
			eventNode.addAttribute("event_type", event.getEventType().toString().toLowerCase(), null);
			eventNode.addAttribute("public_type", event.getPublicType().toString().toLowerCase(), null);
			eventNode.addAttribute("plan", event.getPlan(), null);
			eventNode.addAttribute("detail", event.getDetail(), null);
			eventNode.addAttribute("description", event.getDescription(), null);
			eventNode.addAttribute("timezone", event.getTimezone().getID(), null);
			eventNode.addAttribute("allday", String.valueOf(event.isAllDay()), null);
			eventNode.addAttribute("start_only", String.valueOf(event.isStartOnly()), null);

			//Members node
			OMElement membersNode = omFactory.createOMElement("members", null);
			eventNode.addChild( membersNode );
			for( Member member : event.getMembers() )
			{
				OMElement memberNode = omFactory.createOMElement("member", null);
				OMElement userNode = omFactory.createOMElement(member.getType().toString().toLowerCase(), null);
				userNode.addAttribute("id", String.valueOf(member.getID()), null);
				userNode.addAttribute("order", String.valueOf(member.getOrder()), null);
				memberNode.addChild( userNode );
				membersNode.addChild( memberNode );
			}

			//When node
			if( event.getSpans().get(0) != null )
			{
				OMElement whenNode = omFactory.createOMElement("when", null);

				OMElement datetimeNode = omFactory.createOMElement("datetime", null);
				String start = DateFormatUtils.ISO_DATETIME_FORMAT.format( event.getSpans().get(0).getStart() );
				String end   = DateFormatUtils.ISO_DATETIME_FORMAT.format( event.getSpans().get(0).getEnd() );
				datetimeNode.addAttribute("start", start, null);
				datetimeNode.addAttribute("end", end, null);

				whenNode.addChild( datetimeNode );
				eventNode.addChild( whenNode );
			}

			//Observers Node
			if( event.getObservers() != null )
			{
				OMElement observersNode = omFactory.createOMElement("observers", null);
				for( Observer observer : event.getObservers() )
				{
					OMElement observerNode = omFactory.createOMElement("observer", null);
					observerNode.addAttribute("id", String.valueOf(observer.getID()), null);
					observerNode.addAttribute("order", String.valueOf(observer.getOrder()), null);

					observersNode.addChild(observerNode);
				}
				eventNode.addChild(observersNode);
			}

			//Repeat info
			if( event.getRepeatInfo() != null )
			{
				RepeatInfo info = event.getRepeatInfo();

				OMElement repeatInfoNode = omFactory.createOMElement("repeat_info", null);

				//Condition node
				OMElement conditionNode = omFactory.createOMElement("condition", null);
				conditionNode.addAttribute("day", String.valueOf(info.getDay()), null);
				conditionNode.addAttribute("week", String.valueOf(info.getWeek()), null);
				conditionNode.addAttribute("type", info.getType().toString().toLowerCase(), null);

				SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
				conditionNode.addAttribute("start_date", formatter.format(info.getStartDate()), null);
				conditionNode.addAttribute("start_time", info.getStartTime(), null);

				conditionNode.addAttribute("end_date", formatter.format(info.getEndDate()), null);
				conditionNode.addAttribute("end_time", info.getEndTime(), null);

				repeatInfoNode.addChild(conditionNode);

				//Exclusive datetime node
				if( info.getExclusiveDateTimes() != null )
				{
					OMElement exclusiveDatetimesNode = omFactory.createOMElement("exclusive_datetimes", null);
					eventNode.addChild(exclusiveDatetimesNode);

					for( Span span : info.getExclusiveDateTimes())
					{
						OMElement exDateNode = omFactory.createOMElement("exclusive_datetime", null);
						String start = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(span.getStart());
						String end   = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(span.getEnd());

						exDateNode.addAttribute("start", start, null);
						exDateNode.addAttribute("end", end, null);

						exclusiveDatetimesNode.addChild(exDateNode);
					}
				}

				eventNode.addChild(repeatInfoNode);
			}

			parameters.addChild( eventNode );
		}
		return parameters;
	}

	public static List<Facility> getFacilityList( OMElement input )
	{
		List<Facility> facilities = new ArrayList<Facility>();

		input = (OMElement) input.getChildElements().next();
		Document document = getDocument(input);

		DTMNodeList facilityNodes = null;
		try{
			XPath xPath = XPathFactory.newInstance().newXPath();
			XPathExpression xPathExpression = xPath.compile( "returns/facility" );
			facilityNodes = (DTMNodeList)xPathExpression.evaluate(document, XPathConstants.NODESET);
		} catch(XPathExpressionException e) {
			return Collections.emptyList();
		}

		for (int i = 0; i < facilityNodes.getLength(); i++){
			Facility facility = new Facility();
			Node failityNode = facilityNodes.item(i);
			NamedNodeMap attrs = failityNode.getAttributes();

			//ID
			Node attr = attrs.getNamedItem("key");
			facility.setKey(Integer.valueOf(attr.getNodeValue()));

			//name
			attr = attrs.getNamedItem("name");
			facility.setName(attr.getNodeValue());

			//code
			attr = attrs.getNamedItem("facility_code");
			facility.setCode(attr.getNodeValue());

			//version
			attr = attrs.getNamedItem("version");
			facility.setVersion(Long.parseLong(attr.getNodeValue()));

			//order
			attr = attrs.getNamedItem("order");
			facility.setOrder(Long.parseLong(attr.getNodeValue()));

			//desciption
			attr = attrs.getNamedItem("description");
			if (attr != null) {
				facility.setDescription(attr.getNodeValue());
			}
			else {
				facility.setDescription("");
			}

			//ID
			attr = attrs.getNamedItem("belong_facility_group");
			if (attr != null) {
				facility.setBelongFacilityGroup(Integer.valueOf(attr.getNodeValue()));
			}

			facilities.add(facility);
		}

		return facilities;
	}
}