/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Common Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/cpl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/


package org.aspectj.weaver.patterns;

import java.io.*;

import org.aspectj.weaver.bcel.*;
import org.aspectj.bridge.AbortException;
import org.aspectj.util.FuzzyBoolean;

import junit.framework.TestCase;
import org.aspectj.weaver.*;

public class TypePatternTestCase extends TestCase {		
	/**
	 * Constructor for PatternTestCase.
	 * @param name
	 */
	public TypePatternTestCase(String name) {
		super(name);
	}
	
	World world;
	
	public void testStaticMatch() {
		world = new BcelWorld();
		
		checkMatch("java.lang.Object", "java.lang.Object", true);
		checkMatch("java.lang.Object+", "java.lang.Object", true);
		checkMatch("java.lang.Object+", "java.lang.String", true);
		checkMatch("java.lang.String+", "java.lang.Object", false);
		checkMatch("java.lang.Integer", "java.lang.String", false);

		checkMatch("java.lang.Integer", "int", false);

		checkMatch("java.lang.Number+", "java.lang.Integer", true);
		
		
		checkMatch("java..*", "java.lang.Integer", true);
		checkMatch("java..*", "java.lang.reflect.Modifier", true);
		checkMatch("java..*", "int", false);
		checkMatch("java..*", "javax.swing.Action", false);
		checkMatch("java..*+", "javax.swing.Action", true);
		
		checkMatch("*.*.Object", "java.lang.Object", true);
		checkMatch("*.Object", "java.lang.Object", false);
        checkMatch("*..*", "java.lang.Object", true);
        checkMatch("*..*", "int", false);
        checkMatch("java..Modifier", "java.lang.reflect.Modifier", true);
        checkMatch("java.lang.reflect.Mod..ifier", "java.lang.reflect.Modifier", false);
        
        checkMatch("java..reflect..Modifier", "java.lang.reflect.Modifier", true);
        checkMatch("java..lang..Modifier", "java.lang.reflect.Modifier", true);        
        checkMatch("java..*..Modifier", "java.lang.reflect.Modifier", true);
        checkMatch("java..*..*..Modifier", "java.lang.reflect.Modifier", true);
        checkMatch("java..*..*..*..Modifier", "java.lang.reflect.Modifier", false);        
        //checkMatch("java..reflect..Modifier", "java.lang.reflect.Modxifier", false);
        checkMatch("ja*va..Modifier", "java.lang.reflect.Modifier", true);
        checkMatch("java..*..Mod*ifier", "java.lang.reflect.Modifier", true);        
	
	}
	
	
	// three levels:
	//  0. defined in current compilation unit, or imported by name
	//  1. defined in current package/type/whatever
	//  2. defined in package imported by *
	/**
	 * We've decided not to test this here, but rather in any compilers
	 */
	public void testImportResolve() {
		world = new BcelWorld();
//		checkIllegalImportResolution("List", new String[] { "java.util", "java.awt", }, 
//			ZERO_STRINGS);


	}
	
	
	// Assumption for bcweaver:  Already resolved type patterns with no *s or ..'s into exact type
	// patterns.  Exact type patterns don't have import lists.  non-exact-type pattens don't
	// care about precedence, so the current package can be included with all the other packages, 
	// and we don't care about compilation units, and we don't care about ordering.

	// only giving this wild-type patterns	
	public void testImportMatch() {
		world = new BcelWorld();

		checkImportMatch("*List", new String[] { "java.awt.", }, ZERO_STRINGS, "java.awt.List", true);
		checkImportMatch("*List", new String[] { "java.awt.", }, ZERO_STRINGS, "java.awt.List", true);
		checkImportMatch("*List", new String[] { "java.awt.", }, ZERO_STRINGS, "java.util.List", false);
		checkImportMatch("*List", new String[] { "java.util.", }, ZERO_STRINGS, "java.awt.List", false);
		checkImportMatch("*List", new String[] { "java.util.", }, ZERO_STRINGS, "java.util.List", true);

		checkImportMatch("*List", ZERO_STRINGS, new String[] { "java.awt.List", }, "java.awt.List", true);

		checkImportMatch("awt.*List", ZERO_STRINGS, new String[] { "java.awt.List", }, "java.awt.List", false);
		checkImportMatch("*Foo", ZERO_STRINGS, new String[] { "java.awt.List", }, "java.awt.List", false);

		checkImportMatch("*List", new String[] { "java.util.", "java.awt.", }, 
			ZERO_STRINGS, "java.util.List", true);
		checkImportMatch("*List", new String[] { "java.util.", "java.awt.", }, 
			ZERO_STRINGS, "java.awt.List", true);

		checkImportMatch("*..List", new String[] { "java.util." }, ZERO_STRINGS, "java.util.List", true);
		checkImportMatch("*..List", new String[] { "java.util." }, ZERO_STRINGS, "java.awt.List", true);
		
	}
	
	public void testImportMatchWithInners() {
		world = new BcelWorld();
		checkImportMatch("*Entry", new String[] { "java.util.", "java.util.Map$"}, ZERO_STRINGS,
						"java.util.Map$Entry", true);
	
		checkImportMatch("java.util.Map.*Entry", ZERO_STRINGS, ZERO_STRINGS,
						"java.util.Map$Entry", true);

		checkImportMatch("*Entry", new String[] { "java.util.", }, ZERO_STRINGS,
						"java.util.Map$Entry", false);
						
		checkImportMatch("*.Entry", new String[] { "java.util.", }, ZERO_STRINGS,
						"java.util.Map$Entry", true);
						
		checkImportMatch("Map.*", new String[] { "java.util.", }, ZERO_STRINGS,
						"java.util.Map$Entry", true);
						
		checkImportMatch("Map.*", ZERO_STRINGS, new String[] { "java.util.Map" },
						"java.util.Map$Entry", true);
	}

	private void checkImportMatch(
		String wildPattern,
		String[] importedPackages,
		String[] importedNames,
		String matchName,
		boolean shouldMatch) 
	{
		WildTypePattern p = makeResolvedWildTypePattern(wildPattern, importedPackages, importedNames);
		checkPatternMatch(p, matchName, shouldMatch);	
	}


	private WildTypePattern makeResolvedWildTypePattern(
		String wildPattern,
		String[] importedPackages,
		String[] importedNames) 
	{
		WildTypePattern unresolved = (WildTypePattern) new PatternParser(wildPattern).parseTypePattern();
		
		WildTypePattern resolved = resolve(unresolved, importedPackages, importedNames);
		return resolved;
		
	}

	private WildTypePattern resolve(
		WildTypePattern unresolved,
		String[] importedPrefixes,
		String[] importedNames) 
	{
		
		TestScope scope = makeTestScope();
		scope.setImportedPrefixes(importedPrefixes);
		scope.setImportedNames(importedNames);
		return (WildTypePattern) unresolved.resolveBindings(scope, Bindings.NONE, false, false);
	}



	public static final String[] ZERO_STRINGS = new String[0];

  	public void testInstanceofMatch() {
		world = new BcelWorld();
		
		checkInstanceofMatch("java.lang.Object", "java.lang.Object", FuzzyBoolean.YES);

		checkIllegalInstanceofMatch("java.lang.Object+", "java.lang.Object");
		checkIllegalInstanceofMatch("java.lang.Object+", "java.lang.String");
		checkIllegalInstanceofMatch("java.lang.String+", "java.lang.Object");
		checkIllegalInstanceofMatch("java.lang.*", "java.lang.Object");
		checkInstanceofMatch("java.lang.Integer", "java.lang.String", FuzzyBoolean.NO);

		checkInstanceofMatch("java.lang.Number", "java.lang.Integer", FuzzyBoolean.YES);
		checkInstanceofMatch("java.lang.Integer", "java.lang.Number", FuzzyBoolean.MAYBE);
				
		
		checkIllegalInstanceofMatch("java..Integer", "java.lang.Integer");

		
		checkInstanceofMatch("*", "java.lang.Integer", FuzzyBoolean.YES);
		
	
	}

	private void checkIllegalInstanceofMatch(String pattern, String name) {
		try {
			TypePattern p = makeTypePattern(pattern);
			ResolvedTypeX type = world.resolve(name);
			/*FuzzyBoolean result = */p.matchesInstanceof(type);
		} catch (AbortException e) {
			return;
		}
		assertTrue("matching " + pattern + " with " + name + " should fail", false);
	}
  
  	private void checkInstanceofMatch(String pattern, String name, FuzzyBoolean shouldMatch) {
		TypePattern p = makeTypePattern(pattern);
		ResolvedTypeX type = world.resolve(name);
		
		p = p.resolveBindings(makeTestScope(), null, false, false);
		
		
		//System.out.println("type: " + p);
		FuzzyBoolean result = p.matchesInstanceof(type);
		String msg = "matches " + pattern + " to " + type;
		assertEquals(msg, shouldMatch, result);
	}

	private TestScope makeTestScope() {
		TestScope scope = new TestScope(ZERO_STRINGS, ZERO_STRINGS, world);
		return scope;
	}
  
	private TypePattern makeTypePattern(String pattern) {
		return new PatternParser(pattern).parseSingleTypePattern();
	}

	private void checkMatch(String pattern, String name, boolean shouldMatch) {
		TypePattern p = makeTypePattern(pattern);
		p = p.resolveBindings(makeTestScope(), null, false, false);
		checkPatternMatch(p, name, shouldMatch);
	}

	private void checkPatternMatch(
		TypePattern p,
		String name,
		boolean shouldMatch) 
	{
		ResolvedTypeX type = world.resolve(name);
		//System.out.println("type: " + type);
		boolean result = p.matchesStatically(type);
		String msg = "matches " + p + " to " + type + " expected ";
		if (shouldMatch) {
			assertTrue(msg + shouldMatch, result);
		} else {
			assertTrue(msg + shouldMatch, !result);
		}
	}
	
	public void testSerialization() throws IOException {
		String[] patterns = new String[] {
			"java.lang.Object", "java.lang.Object+", "java.lang.Integer",
			"int", "java..*", "java..util..*", "*.*.Object", "*",
		};
		
		for (int i=0, len=patterns.length; i < len; i++) {
			checkSerialization(patterns[i]);
		}
	}

	/**
	 * Method checkSerialization.
	 * @param string
	 */
	private void checkSerialization(String string) throws IOException {
		TypePattern p = makeTypePattern(string);
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		DataOutputStream out = new DataOutputStream(bo);
		p.write(out);
		out.close();
		
		ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
		DataInputStream in = new DataInputStream(bi);
		TypePattern newP = TypePattern.read(in, null);
		
		assertEquals("write/read", p, newP);	
	}
	
}
