/*******************************************************************************
 * Copyright (c) 2009, 2014 Google, Inc and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * 	   Sergey Prigogin (Google) - initial API and implementation
 * 	   Thomas Corbat (IFS)
 *******************************************************************************/
package org.eclipse.cdt.core.parser.tests.ast2;

import static org.eclipse.cdt.core.parser.tests.VisibilityAsserts.assertVisibility;
import junit.framework.TestSuite;

import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.AccessContext;
import org.eclipse.cdt.internal.core.parser.ParserException;

public class AccessControlTests extends AST2TestBase {
	
	protected class AccessAssertionHelper extends BindingAssertionHelper {
		AccessAssertionHelper(String contents) throws ParserException {
			super(contents, true);
		}
		
		void assertAccessible(String section, int len) {
			IASTName name = findName(section, len);
			IBinding binding = name.resolveBinding();
    		assertNotNull(binding);
			assertTrue(AccessContext.isAccessible(binding, name));
		}
		
		void assertNotAccessible(String section, int len) {
			IASTName name = findName(section, len);
			IBinding binding = name.resolveBinding();
    		assertNotNull(binding);
			assertFalse(AccessContext.isAccessible(binding, name));
		}
	}
	
	public AccessControlTests() {
	}
	
	public AccessControlTests(String name) {
		super(name);
	}
	
	public static TestSuite suite() {
		return suite(AccessControlTests.class);
	}

	private AccessAssertionHelper getAssertionHelper() throws Exception {
		final String code = getAboveComment();
		parseAndCheckBindings(code, ParserLanguage.CPP);
		return new AccessAssertionHelper(code);
	}

	//	class A {
	//	public:
	//	  int a;
	//    typedef char E;
	//	};
	//	class B : private A {
	//    friend void test();
	//    int b;
	//    typedef char* F;
	//	};
	//  class C : protected B {
	//  };
	//	void test() {
	//    class D : public C {
	//      void m() {
	//        a; //1
	//        b; //1
	//        E(); //1
	//        F(); //1
	//      }
	//	  };
	//    B b;
	//    b.a; //2
	//    b.b; //2
	//    B::E(); //2
	//    B::F(); //2
	//    C c;
	//    c.a; //3
	//    c.b; //3
	//    C::E(); //3
	//    C::F(); //3
	//	}
	public void testFriends() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertAccessible("a; //1", 1);
		ah.assertAccessible("b; //1", 1);
		ah.assertAccessible("E(); //1", 1);
		ah.assertAccessible("F(); //1", 1);
		ah.assertAccessible("a; //2", 1);
		ah.assertAccessible("b; //2", 1);
		ah.assertAccessible("E(); //2", 1);
		ah.assertAccessible("F(); //2", 1);
		ah.assertNotAccessible("a; //3", 1);
		ah.assertNotAccessible("b; //3", 1);
		ah.assertNotAccessible("E(); //3", 1);
		ah.assertNotAccessible("F(); //3", 1);
	}

	//	class A {
	//	public:
	//	  int a;
	//	};
	//
	//	class B : public A {
	//	private:
	//	  int a;
	//	};
	//
	//	void test(B x) {
	//	  x.a = 0;
	//	}
	public void testHiddenMember() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertNotAccessible("a = 0", 1);
	}
	
	//	class A0 {
	//		public:
	//			enum Ex {e1};
	//	};
	//
	//	class A : public A0 {
	//		class B {
	//			void test() {
	//				Ex a;  // we compute 'B' as the naming type, whereas it should be 'A'
	//			}
	//		};
	//	};
	public void testEnclosingAsNamingClass_292232() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertAccessible("Ex a;", 2);
	}
	
	// // Example from C++-specification 11.2-3
	// class B { 
	//   public:
	//     int mi;
	//     static int si;
	// };
	// class D : private B {};
	// class DD : public D {
	//   void f();
	// };
	//
	// void DD::f() {
	//   mi=3;  // private
	//   si=3;  // private 
	//   B b;
	//   b.mi=4;
	//   b.si=4;
	//   B* bp;
	//   bp->mi=5;
	// }
	public void testEnclosingAsNamingClass_292232a() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertNotAccessible("mi=3;", 2);
		ah.assertNotAccessible("si=3;", 2);
		ah.assertAccessible("mi=4;", 2);
		ah.assertAccessible("si=4;", 2);
		ah.assertAccessible("mi=5;", 2);
	}

	//	class A {
	//	private:
	//		typedef int Waldo;
	//	};
	//	A::Waldo waldo;
	public void testPrivateTypedef_427730() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertNotAccessible("Waldo waldo", 5);
	}

	//	class A {
	//	private:
	//		class B {};
	//	public:
	//		typedef B Waldo;
	//	};
	//	A::Waldo waldo;
	public void testPublicTypedefForPrivateMemberClass_427730() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertAccessible("Waldo waldo", 5);
	}

	//	class A {
	//	private:
	//		class B {};
	//		friend class C;
	//	};
	//	class C {
	//	public:
	//		typedef A::B Waldo;
	//	};
	//	C::Waldo waldo;
	public void testPublicTypedefForFriendClass_427730() throws Exception {
		AccessAssertionHelper ah = getAssertionHelper();
		ah.assertAccessible("Waldo waldo", 5);
	}

	//	class Outer {
	//		class Inner {};
	//	protected:
	//		using AliasInner = Inner;
	//		typedef Inner TypedefInner;
	//	};
	public void testAccessibilityForAliasedTypeInSameClass_427730() throws Exception {
		BindingAssertionHelper bh = getAssertionHelper();
		ICPPClassType outerClass = bh.assertNonProblem("Outer");
		IBinding aliasInner = bh.assertNonProblem("AliasInner");
		assertVisibility(ICPPClassType.v_protected, outerClass.getVisibility(aliasInner));
		IBinding typedefInner = bh.assertNonProblem("TypedefInner");
		assertVisibility(ICPPClassType.v_protected, outerClass.getVisibility(typedefInner));
	}
}
