/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * http://www.netbeans.org/cddl-gplv2.html
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 * specific language governing permissions and limitations under the
 * License.  When distributing the software, include this License Header
 * Notice in each file and include the License file at
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the GPL Version 2 section of the License file that
 * accompanied this code. If applicable, add the following below the
 * License Header, with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Contributor(s):
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 *
 * If you wish your version of this file to be governed by only the CDDL
 * or only the GPL Version 2, indicate your decision by adding
 * "[Contributor] elects to include this software in this distribution
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 * single choice of license, a recipient has the option to distribute
 * your version of this file under either the CDDL, the GPL Version 2 or
 * to extend the choice of license to its licensees as provided above.
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 * Version 2 license, then the option applies only if the new code is
 * made subject to such option by the copyright holder.
 */
/*
 *	TestEvents - test the events
 *
 *	The following test assumes that we know the content of the
 *	graph as we get elements, add and change them. Therefore, the TestBook.xml
 *	file and this java test should be kept in sync.
 *
 * 	Test the following:
 *
 *		listener on the root - change event for add/change/remove on the same root bean
 *		listener on the root - change event for add/change/remove 
 *			on a sub node bean (propagation)
 *		listener on the root and on a subnode: test no reception, both reception and 
 *			only one reception
 *		listener on a specific property
 *		remove listeners
 *		indexed and non indexed property events
 *		check utility methods to get the index, name and parent bean 
 *			from an event name (graphManager methods)
 *		remove a subtree - get event within the subtree (no propagation)
 *			and within the original tree (propagation)
 *
 */

import java.io.*;
import java.util.*;
import org.w3c.dom.*;

import java.beans.*;

import org.netbeans.modules.schema2beans.*;
import book.*;


public class TestEvents extends BaseTest
{
    public static void main(String[] argv) {
        BaseTest o = new TestEvents();
        if (argv.length > 0)
            o.setDocumentDir(argv[0]);
        try {
            o.run();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        System.exit(0);
    }
    
	public class MyListener implements PropertyChangeListener
	{
		GraphManager	gm;
		String			listenerName;
		Object			oldValue;
		Object			newValue;
		String			propertyName;
		Object			source;
		int				index;

		boolean			remove;
		
		//	Used to check that the event is triggered once the changed are done
		Chapter			tracePara;
		
		public MyListener(BaseBean bean)
		{
			this.listenerName = bean.name();
			gm = bean.graphManager();
			this.remove = false;
			out("new listener for " + this.listenerName);
		}

		public void reset()
		{
			this.oldValue = null;
			this.newValue = null;
			this.propertyName = null;
			this.source = null;
			this.index = -1;
		}

		public void traceParagraphs(Chapter c)
		{
			this.tracePara = c;
		}

		
		public void propertyChange(PropertyChangeEvent e)
		{
			this.oldValue = e.getOldValue();
			this.newValue = e.getNewValue();
			this.propertyName = e.getPropertyName();
			this.source = e.getSource();
			String n = this.propertyName;
			this.index = gm.getPropertyIndex(n);

			out("<Lnr:" + this.listenerName + " Evt:" + n + 
				" Src:" + this.source.getClass().getName() + ">");
			if (remove)
			{
				out("<" + gm.getPropertyName(n) + "[" + this.index + 
					"]" + " - Parent: " + gm.getPropertyParentName(n) + ">");
			}
			else
			{
				out("<" + gm.getPropertyName(n) + "[" + this.index + 
					"]" + " - Parent: " + gm.getPropertyParentName(n) +
					"/" + gm.getPropertyParent(n).getClass().getName() + ">");
			}

			if (this.tracePara != null)
			{
				String[] p = this.tracePara.getParagraph();
				for (int i=0; i<p.length; i++)
					out("From event listener: " + p[i]);
			}
			//out("received " + this.oldValue + "/" + this.newValue + "/" +
			//	this.propertyName);
		}

		public void removeMode()
		{
			this.remove = true;
		}
		
		public Object oldValue()
		{
			return this.oldValue;
		}

		public String stringOldValue()
		{
			if (this.oldValue == null)
				return "<null>";
			else
				return this.oldValue.toString();
		}

		public Object newValue()
		{
			return this.newValue;
		}

		public String stringNewValue()
		{
			if (this.newValue == null)
				return "<null>";
			else
				return this.newValue.toString();
		}
		
		public String name()
		{
			return this.propertyName;
		}

		public String toString()
		{
			return this.name() + " raised from source " +
				this.source.getClass().getName();
		}
	}
	
	public void run()
		throws Exception
	{
		Book book;

		this.readDocument();
		
		out("creating the bean graph");
		book = Book.createGraph(this.doc);
		GraphManager gm = book.graphManager();

		//	Check that we can read the graph an it is complete
		out("bean graph created");
		//out(book.toString());

		/*
		 *	Book
		 *	  Index[1,n]
		 *	         Word - String
		 *	         Ref[1,n]
		 *	                Page - String
		 *	                Line - String
		 *	  Chapter[1,n]
		 *	         Comment? - String
		 *	         Paragraph[0,n] - String
		 *	  Summary? - String
		 *	  Author[1,n] - String
		 *	  Good - Boolean
		 *	  Available - Boolean
		 */

		//
		//	Set a change event on the root
		//
		MyListener l = new MyListener(book);
		
		book.addPropertyChangeListener(l);
		
		setTest("simple change event on the root");
		l.reset();
		String s = "This book is about how to raise the event familly";
		//	Change a property on the root - this should raises an event
		book.setSummary(s);
		//	Check the received event
		check(l.oldValue() == null, "(old value)");
		check(l.newValue().equals(s), "(new value)");

		setTest("change the same property on the root");
		l.reset();
		String s2 = "This book is about nothing at all";
		//	Change a property on the root - this should raises an event
		book.setSummary(s2);
		//	Check the received event
		check(l.oldValue().equals(s), "(old value)");
		check(l.newValue().equals(s2), "(new value)");

		setTest("remove this same property");
		l.reset();
		//	Change a property on the root - this should raises an event
		book.setSummary(null);
		//	Check the received event
		check(l.oldValue().equals(s2), "(old value)");
		check(l.newValue() == null, "(new value)");


		//
		//	Keep the same event on the root, but change a property of another
		//	property (not a direct property of the root)
		//
		setTest("propagation of the event");
		Chapter c = book.getChapter(0);
		l.reset();
		s = c.getComment();
		s2 = "Comment on the first chapter";
		c.setComment(s2);
		check(l.oldValue.equals(s), "(oldvalue)");
		check(l.newValue.equals(s2), "(newvalue)");

		//	Add three paragraphs

		setTest("event on indexed property - add new");
		l.reset();
		s = "This is a new paragraph";
		int i = c.addParagraph(s);
		check(l.oldValue == null, "(no old value - new element)");
		check(l.newValue.equals(s), "(new value)");
		check(l.index == i, "(correct index)");

		setTest("event on indexed property - add new");
		l.reset();
		s2 = "This is another paragraph";
		int j = c.addParagraph(s2);
		check(l.oldValue == null, "(no old value - new element)");
		check(l.newValue.equals(s2), "(new value)");
		check(l.index == j, "(correct index)");

		setTest("event on indexed property - add new");
		l.reset();
		s2 = "This is yet another paragraph";
		j = c.addParagraph(s2);
		check(l.oldValue == null, "(no old element - new element)");
		check(l.newValue.equals(s2), "(new value)");
		check(l.index == j, "(correct index)");

		//	Remove the first added
		setTest("event on indexed property - remove index");
		l.reset();
		c.setParagraph(i, null);
		check(l.oldValue.equals(s), "(old value)");
		check(l.newValue == null, "(no new value)");
		check(l.index == i, "(correct index)");

		setTest("event on indexed property - reset new");
		l.reset();
		c.setParagraph(i, s);
		check(l.oldValue == null, "(old value)");
		check(l.newValue.equals(s), "(no new value)");
		check(l.index == i, "(correct index)");

		//
		//	Add another listener on an intermediate node and on a leaf
		//
		
		Chapter c2 = book.getChapter(1);
		
		MyListener l2 = new MyListener(c2);
		
		//	Get the events only for Paragraph changes
		c2.addPropertyChangeListener("Paragraph", l2);

		//	Check that we receive the event twice
		setTest("two listeners - both receiving");
		l2.reset();
		l.reset();
		s = "This is a brand new one";
		c2.addParagraph(s);
		check(l.oldValue == null, "(no old value)");
		check(l.newValue.equals(s), "(new value)");
		check(l.oldValue == l2.oldValue, "(same old value - both listeners)");
		check(l.newValue.equals(l2.newValue), "(same new value - both listeners)");
		check(l.index == l2.index, "(same index - both listeners)");
		check(l.source == l2.source, "(same source - both listeners)");

		//	Check that modifying the comment won't notify the Paragraph listener
		setTest("two listeners - one receiving");
		l2.reset();
		l.reset();
		s = "That's a new comment value";
		c2.setComment(s);
		check(l.newValue.equals(s), "(root listener: yep)");
		check(l2.newValue == null, "(chapter listener: noop)");


		//	Remove the book listener
		book.removePropertyChangeListener(l);
		setTest("one listener - no receiving");
		l2.reset();
		l.reset();
		s = "That's another new comment value";
		c2.setComment(s);
		check(l.newValue == null, "(root listener: noop)");
		check(l2.newValue == null, "(chapter listener: noop)");

		setTest("one listener - one receiving");
		l2.reset();
		l.reset();
		s = "That's a brand new paragraph";
		c2.addParagraph(s);
		check(l.newValue == null, "(root listener: noop)");
		check(l2.newValue.equals(s), "(chapter listener: yep)");

		setTest("no listener - no receiving");
		c2.removePropertyChangeListener("Paragraph", l2);
		l2.reset();
		l.reset();
		s = "That's yet another brand new paragraph";
		c2.addParagraph(s);
		check(l.newValue == null, "(root listener: noop)");
		check(l2.newValue == null, "(chapter listener: noop)");
		
		//	Register again the listeners
		book.addPropertyChangeListener(l);
		c2.addPropertyChangeListener("Paragraph", l2);
		l.removeMode();
		l2.removeMode();
		l2.reset();
		l.reset();
		book.removeChapter(c2);
		out("should have received paragraph events on Chapter and Chapter event on Book");

		//
		//	Make sure that the event is triggered after the property
		//	has changed.
		//
		c = new Chapter();
		c.addParagraph("1. this is a paragraph");
		c.addParagraph("2. this is a paragraph");
		c.addParagraph("3. this is a paragraph");
		c.addParagraph("4. this is a paragraph");
		out("should receive Chapter event and get 4 strings from the event");
		l.traceParagraphs(c);
		book.addChapter(c);
		String[] pp = c.getParagraph();
		String[] pp2 = new String[3];
		pp2[0] = pp[0];
		pp2[1] = pp[3];
		pp2[2] = pp[2];
		out("should receive Chapter event and get 3 strings from the event (1, 4, 3)");
		c.setParagraph(pp2);
		l.traceParagraphs(null);
 	}

}



