package jp.sourceforge.asclipse.as3.adapter;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

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

import jp.sourceforge.asclipse.as3.IAS3GlobalContext;
import jp.sourceforge.asclipse.as3.TestUtil;
import jp.sourceforge.asclipse.as3.ParserUtil.AS3ParserException;
import jp.sourceforge.asclipse.as3.adapter.HierarchyMemberProvider.AS3Member;
import jp.sourceforge.asclipse.as3.element.AS3Type;
import jp.sourceforge.asclipse.as3.internal.DefaultAS3GlobalContext;

import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link HierarchyMemberProvider}クラスのテストケース。
 * @author shin1
 */
public class HierarchyMemberProviderTest {

	static final Logger logger = LoggerFactory.getLogger(HierarchyMemberProviderTest.class);

	static IAS3GlobalContext globalContext;


	/**
	 * <dl>
	 * <dt>ClassA</dt><dd><ol>
	 * <li>private function funcA01():void {}</li>
	 * <li>protected function funcA02():void {}</li>
	 * <li>function funcA03():void {}</li>
	 * <li>public function funcA04():void {}</li>
	 * <li>protected var propA01:int;</li>
	 * <li>private var propA02;</li>
	 * </ol></dd>
	 * <dt>Object</dt><dd><ol>
	 * <li>public function hasOwnProperty(V:* = null):Boolean;</li>
	 * <li>public function isPrototypeOf(V:* = null):Boolean;</li>
	 * <li>public function propertyIsEnumerable(V:* = null):Boolean;</li>
	 * <li>public function toString():String;</li>
	 * <li>public function valueOf():Object;</li>
	 * <li>public function setPropertyIsEnumerable(name:String, isEnum:Boolean = true):void;</li>
	 * </ol><ul>
	 * <li>public function Object(); コンストラクタ</li>
	 * </ul></dd>
	 * </dl>
	 */
	@Test
	public void classA() {
		AS3Type type = TestUtil.findType(globalContext, "ClassA");
		assertThat(type, notNullValue());
		HierarchyMemberProvider adapter = new HierarchyMemberProvider(globalContext);
		type.addAdapter(adapter);
		List<AS3Member> members = adapter.getMembers();
		assertThat(members.size(), is(/*A*/6 + /*Object*/+6));
	}

	/**
	 * <dl>
	 * <dt>abstract class ClassB(pkgA) implements InterfaceC</dt><dd><ol>
	 * <li>private function funcB01():void {}</li>
	 * <li>protected function funcB02():void {}</li>
	 * <li>function funcB03():void {}</li>
	 * <li>public function funcB04():void {}</li>
	 * <li>public function funcC01() {}</li>
	 * <li>public function funcC02() {}</li>
	 * <li>private var propB01a</li>
	 * <li>, propB01b</li>
	 * <li>protected var propB02a</li>
	 * <li>, propB02b;</li>
	 * <li>public var propB03a</li>
	 * <li>, propB03b;</li>
	 * <li>var propB04a</li>
	 * <li>, propB04b;</li>
	 * </ol></dd>
	 * <dt>InterfaceC</dt><dd><ol>
	 * <li>public function funcC03() {}</li>
	 * </ol><ul>
	 * <li>public function funcC01() {} ClassBにて実装されている</li>
	 * <li>public function funcC02() {} ClassBにて実装されている</li>
	 * </ul></dd>
	 * <dt>Object</dt><dd><ol>
	 * <li>public function hasOwnProperty(V:* = null):Boolean;</li>
	 * <li>public function isPrototypeOf(V:* = null):Boolean;</li>
	 * <li>public function propertyIsEnumerable(V:* = null):Boolean;</li>
	 * <li>public function toString():String;</li>
	 * <li>public function valueOf():Object;</li>
	 * <li>public function setPropertyIsEnumerable(name:String, isEnum:Boolean = true):void;</li>
	 * </ol><ul>
	 * <li>public function Object(); コンストラクタ</li>
	 * </ul></dd>
	 * </dl>
	 */
	@Test
	public void classB() {
		AS3Type type = TestUtil.findType(globalContext, "pkgA.ClassB");
		assertThat(type, notNullValue());
		HierarchyMemberProvider adapter = new HierarchyMemberProvider(globalContext);
		type.addAdapter(adapter);
		List<AS3Member> members = adapter.getMembers();
		for (AS3Member member : members) {
			logger.trace(member.toString());
		}
		assertThat(members.size(), is(/*B*/14 + /*C*/1 + /*Object*/6));
	}

	/**
	 * <dl>
	 * <dt>public class ClassD extends ClassB</dt><dd><ol>
	 * <li>public function funcD01() {}</li>
	 * <li>public function funcD02() {}</li>
	 * <li>public override function funcB04():void {}</li>
	 * <li>public function funcC03():void {}</li>
	 * </ol></dd>
	 * <dt>abstract class ClassB(pkgA) implements InterfaceC</dt><dd><ol>
	 * <li>protected function funcB02():void {}</li>
	 * <li>function funcB03():void {} internal(pkgA)なのでpkgA.ClassDから見える</li>
	 * <li>public function funcC01() {}</li>
	 * <li>public function funcC02() {}</li>
	 * <li>protected var propB02a</li>
	 * <li>, propB02b;</li>
	 * <li>public var propB03a</li>
	 * <li>, propB03b;</li>
	 * <li>var propB04a; internal(pkgA)なのでpkgA.ClassDから見える</li>
	 * <li>, propB04b; internal(pkgA)なのでpkgA.ClassDから見える</li>
	 * </ol><ul>
	 * <li>private function funcB01():void {} privateなのでClassDから見えない</li>
	 * <li>public function funcB04():void {} ClassDにてoverrideされている</li>
	 * <li>private var propB01a; privateなのでClassDから見えない</li>
	 * <li>, propB01b privateなのでClassDから見えない</li>
	 * </ul></dd>
	 * <dt>InterfaceC</dt><dd><ol>
	 * </ol><ul>
	 * <li>public function funcC01() {} ClassBにて実装されている</li>
	 * <li>public function funcC02() {} ClassBにて実装されている</li>
	 * <li>public function funcC03() {} ClassDにて実装されている</li>
	 * </ul></dd>
	 * <dt>Object</dt><dd><ol>
	 * <li>public function hasOwnProperty(V:* = null):Boolean;</li>
	 * <li>public function isPrototypeOf(V:* = null):Boolean;</li>
	 * <li>public function propertyIsEnumerable(V:* = null):Boolean;</li>
	 * <li>public function toString():String;</li>
	 * <li>public function valueOf():Object;</li>
	 * <li>public function setPropertyIsEnumerable(name:String, isEnum:Boolean = true):void;</li>
	 * </ol><ul>
	 * <li>public function Object(); コンストラクタ</li>
	 * </ul></dd>
	 * </dl>
	 */
	@Test
	public void classD() {
		AS3Type type = TestUtil.findType(globalContext, "pkgA.ClassD");
		assertThat(type, notNullValue());
		HierarchyMemberProvider adapter = new HierarchyMemberProvider(globalContext);
		type.addAdapter(adapter);
		List<AS3Member> members = adapter.getMembers();
		for (AS3Member member : members) {
			logger.debug(member.toString());
		}
		assertThat(members.size(), is(/*D*/4 + /*B*/10 + /*C*/0 + /*Object*/6));
	}

	/**
	 * <dl>
	 * <dt>public class ClassF extends ClassD implements InterfaceE</dt><dd><ol>
	 * <li>var propF01a;</li>
	 * <li>public function toString():String;</li>
	 * </ol></dd>
	 * <dt>public interface InterfaceE extends InterfaceC</dt><dd><ol>
	 * <li>function funcE01():void;</li>
	 * </ol></dd>
	 * <dt>public class ClassD extends ClassB</dt><dd><ol>
	 * <li>public function funcD01() {}</li>
	 * <li>public override function funcB04():void {}</li>
	 * <li>public function funcC03():void {}</li>
	 * </ol><ul>
	 * <li>public function funcD02() {} internal(pkgA)なのでpkgB.ClassFから見えない。</li>
	 * </ul></dd>
	 * <dt>abstract class ClassB(pkgA) implements InterfaceC</dt><dd><ol>
	 * <li>protected function funcB02():void {}</li>
	 * <li>public function funcC01() {}</li>
	 * <li>public function funcC02() {}</li>
	 * <li>protected var propB02a</li>
	 * <li>, propB02b;</li>
	 * <li>public var propB03a</li>
	 * <li>, propB03b;</li>
	 * </ol><ul>
	 * <li>private function funcB01():void {} privateなのでClassFから見えない</li>
	 * <li>function funcB03():void {} internal(pkgA)なのでClassFから見えない</li>
	 * <li>public function funcB04():void {} ClassDにてoverrideされている</li>
	 * <li>private var propB01a; privateなのでClassDから見えない</li>
	 * <li>, propB01b privateなのでClassDから見えない</li>
	 * <li>var propB04a; internal(pkgA)なのでClassFから見えない</li>
	 * <li>, propB04b; internal(pkgA)なのでClassFから見えない</li>
	 * </ul></dd>
	 * <dt>InterfaceC</dt><dd><ol>
	 * </ol><ul>
	 * <li>public function funcC01() {} ClassBにて実装されている</li>
	 * <li>public function funcC02() {} ClassBにて実装されている</li>
	 * <li>public function funcC03() {} ClassDにて実装されている</li>
	 * </ul></dd>
	 * <dt>Object</dt><dd><ol>
	 * <li>public function hasOwnProperty(V:* = null):Boolean;</li>
	 * <li>public function isPrototypeOf(V:* = null):Boolean;</li>
	 * <li>public function propertyIsEnumerable(V:* = null):Boolean;</li>
	 * <li>public function valueOf():Object;</li>
	 * <li>public function setPropertyIsEnumerable(name:String, isEnum:Boolean = true):void;</li>
	 * </ol><ul>
	 * <li>public function Object(); コンストラクタ</li>
	 * <li>public function toString():String; ClassFでoverrideされている</li>
	 * </ul></dd>
	 * </dl>
	 */
	@Test
	public void classF() {
		AS3Type type = TestUtil.findType(globalContext, "pkgB.ClassF");
		assertThat(type, notNullValue());
		HierarchyMemberProvider adapter = new HierarchyMemberProvider(globalContext);
		type.addAdapter(adapter);
		List<AS3Member> members = adapter.getMembers();
		for (AS3Member member : members) {
			logger.debug(member.toString());
		}
		assertThat(members.size(), is(/*F*/2 + /*E*/1 + /*D*/3 + /*B*/7 + /*C*/0
				+ /*Object*/5));
	}

	/**
	 * ビルトインクラス類とテスト用のソースフォルダ配下を取り込む。
	 * @throws AS3ParserException
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws AS3ParserException, IOException,
			ClassNotFoundException {
		globalContext = new DefaultAS3GlobalContext();
		globalContext.loadBuiltin(IAS3GlobalContext.DEFAULT_BUILTIN);
		globalContext.addContext(TestUtil.createAS3Context(globalContext,
				"src/test/resources/sampleprj"));
	}
}
