package org.phosphoresce.commons.ldaps;

import java.util.LinkedList;
import java.util.List;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

/**
 * LDAP操作クラス<br>
 *
 * @author Kitagawa<br>
 *
 *<!--
 * 更新日		更新者			更新内容
 * 2008/11/27	Kitagawa		新規作成
 *-->
 */
public class LDAPManager {

	/** 検索フィルタ(全検索) */
	public static final String FILTER_SEARCH_ALL = "(objectClass=*)";

	/** 環境設定オブジェクト */
	private LDAPManagerConfig config;

	/** ディレクトリサービスコンテキストオブジェクト */
	private DirContext context;

	/** 検索コントロールオブジェクト */
	private SearchControls controls;

	/**
	 * コンストラクタ<br>
	 */
	private LDAPManager() {
		super();
	}

	/**
	 * コンストラクタ<br>
	 * @param config LDAP接続環境変数オブジェクト
	 */
	public LDAPManager(LDAPManagerConfig config) {
		this.config = config;
	}

	/**
	 * ディレクトリサービスへの接続を切断します。<br>
	 * @throws NamingException 正常にクローズ処理を完了できなかった場合に発生
	 */
	public void close() throws NamingException {
		if (context != null) {
			context.close();
			context = null;
		}
	}

	/**
	 * ディレクトリサービスへの接続が切断されているか判定します。<br>
	 * @return ディレクトリサービスへの接続が切断されている場合にtrueを返却
	 */
	public boolean isClosed() {
		return context == null;
	}

	/**
	 * ディレクトリサービスコンテキストオブジェクトを取得します。<br>
	 * ディレクトリサービスコンテキストが初期化されていない場合は新たに生成して提供します。<br>
	 * @return ディレクトリサービスコンテキストオブジェクト
	 * @throws NamingException 環境変数から正常にコンテキストオブジェクトを生成できなかった場合に発生
	 */
	protected DirContext getContext() throws NamingException {
		if (context == null) {
			context = config.createContext();
		}
		return context;
	}

	/**
	 * 検索コントロールオブジェクトを取得します。<br>
	 * 検索コントロールが初期化されていない場合は新たに生成して提供します。<br>
	 * @return 検索コントロールオブジェクト
	 */
	protected SearchControls getSearchControls() {
		if (controls == null) {
			controls = config.createSearchControls();
		}
		return controls;
	}

	/**
	 * 指定されたディレクトリ内をフィルタで検索を行います。<br>
	 * @param dn DN文字列
	 * @param filter 検索フィルタ (例：(objectClass=*))
	 * @return LDAPエントリ配列
	 * @throws NamingException 正常に指定された条件で検索処理が行えなかった場合に発生
	 */
	public LDAPEntry[] search(String dn, String filter) throws NamingException {
		// LDAP接続コンテキスト取得
		DirContext context = getContext();
		SearchControls controls = getSearchControls();

		// 検索処理実行
		List resultList = new LinkedList();
		NamingEnumeration results = context.search(dn, filter, controls);
		while (results.hasMore()) {
			SearchResult result = (SearchResult) results.next();
			Attributes attributes = result.getAttributes();
			resultList.add(LDAPEntry.createEntry(attributes));
		}

		return (LDAPEntry[]) resultList.toArray(new LDAPEntry[resultList.size()]);
	}

	/**
	 * 指定されたディレクトリの全検索を行います。<br>
	 * @param dn DN文字列
	 * @return LDAPエントリ配列
	 * @throws NamingException 正常に指定された条件で検索処理が行えなかった場合に発生
	 */
	public LDAPEntry[] searchAll(String dn) throws NamingException {
		return search(dn, FILTER_SEARCH_ALL);
	}

	/**
	 * 指定されたディレクトリ内をフィルタで検索される件数をカウントします。<br>
	 * @param dn DN文字列
	 * @param filter 検索フィルタ (例：(objectClass=*))
	 * @return 検索される件数
	 * @throws NamingException 正常に指定された条件で検索処理が行えなかった場合に発生
	 */
	public int count(String dn, String filter) throws NamingException {
		return search(dn, filter).length;
	}

	/**
	 * 指定されたディレクトリ内を全件カウントします。<br>
	 * @param dn DN文字列
	 * @return 検索される件数
	 * @throws NamingException 正常に指定された条件で検索処理が行えなかった場合に発生
	 */
	public int countAll(String dn) throws NamingException {
		return searchAll(dn).length;
	}

	/**
	 * 指定されたディレクトリオブジェクトをLDAPエントリとして追加します。<br>
	 * @param dn DN文字列 (例：uid=foo,dn=users,dc=examhost,dc=examdomain)
	 * @param entry LDAPエントリ
	 * @throws NamingException 正常にエントリを追加できなかった場合に発生
	 */
	public void add(String dn, LDAPEntry entry) throws NamingException {
		// LDAP接続コンテキスト取得
		DirContext context = getContext();
		SearchControls controls = getSearchControls();

		// サブオブジェクト生成
		context.createSubcontext(dn, entry.createBasicAttributes());
	}

	/**
	 * 指定されたディレクトリオブジェクト内容を更新します。<br>
	 * @param dn DN文字列
	 * @param entry LDAPエントリオブジェクト
	 * @throws NamingException 正常にエントリの更新が行えなかった場合に発生
	 */
	public void update(String dn, LDAPEntry entry) throws NamingException {
		// LDAP接続コンテキスト取得
		DirContext context = getContext();
		SearchControls controls = getSearchControls();

		// 属性集合取得
		LDAPAttribute[] attributes = entry.getAttributes();

		// 更新アイテム生成
		List itemList = new LinkedList();
		for (int i = 0; i <= attributes.length - 1; i++) {
			LDAPAttribute attribute = attributes[i];
			Attribute namingAtrribute = attribute.createBasicAttribute();
			itemList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, namingAtrribute));
		}

		// 更新処理
		context.modifyAttributes(dn, (ModificationItem[]) itemList.toArray(new ModificationItem[itemList.size()]));
	}

	/**
	 * 指定されたディレクトリオブジェクトを削除します。<br>
	 * @param dn DN文字列
	 * @throws NamingException 正常にエントリの削除処理が行えなかった場合に発生
	 */
	public void delete(String dn) throws NamingException {
		// LDAP接続コンテキスト取得
		DirContext context = getContext();
		SearchControls controls = getSearchControls();

		// サブオブジェクト削除
		context.destroySubcontext(dn);
	}
}

