/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.test.xml;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.Calendar;
import junit.framework.TestCase;
import org.jboss.logging.Logger;
import org.jboss.test.xml.book.Book;
import org.jboss.test.xml.book.BookCharacter;
import org.jboss.test.xml.book.BookGenericObjectModelFactory;
import org.jboss.test.xml.book.BookGenericObjectModelProvider;
import org.jboss.test.xml.book.BookObjectFactory;
import org.jboss.test.xml.book.BookObjectProvider;
import org.jboss.xb.binding.DtdMarshaller;
import org.jboss.xb.binding.GenericObjectModelFactory;
import org.jboss.xb.binding.Marshaller;
import org.jboss.xb.binding.ObjectModelFactory;
import org.jboss.xb.binding.ObjectModelProvider;
import org.jboss.xb.binding.Unmarshaller;
import org.jboss.xb.binding.UnmarshallerFactory;
import org.jboss.xb.binding.XsMarshaller;
import org.jboss.xb.binding.TypeBinding;
import org.jboss.xb.binding.SimpleTypeBindings;

/**
 *
 * @version <tt>$Revision: 1.6.2.3 $</tt>
 * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
 */
public class SimpleTestCase
   extends TestCase
{
   private static final Logger log = Logger.getLogger(SimpleTestCase.class);
   
   public SimpleTestCase(String name)
   {
      super(name);
   }

   public void testUnmarshalBookDtd() throws Exception
   {
      // create an object model factory
      ObjectModelFactory factory = new BookObjectFactory();
      unmarshalBook("book-dtd.xml", factory);
   }

   public void testUnmarshalBookXs() throws Exception
   {
      // create an object model factory
      ObjectModelFactory factory = new BookObjectFactory();
      unmarshalBook("book-xs.xml", factory);
   }

   public void testUnmarshalBookXsGenericFactory() throws Exception
   {
      // create an object model factory
      GenericObjectModelFactory factory = new BookGenericObjectModelFactory();
      unmarshalBook("book-xs.xml", factory);
   }

   public void testMarshallBookDtd() throws Exception
   {
      log.debug("<test-marshall-book-dtd>");

      // obtain an instance of Book to marshal
      Book book = createBook();

      // get the output writter to write the XML content
      StringWriter xmlOutput = new StringWriter();

      // get the DTD source
      InputStream is = getResource("xml/book/books.dtd");
      Reader dtdReader = new InputStreamReader(is);

      // create an instance of DTD marshaller
      DtdMarshaller marshaller = new DtdMarshaller();
      marshaller.addBinding("since", new TypeBinding()
      {
         public Object unmarshal(String value)
         {
            // todo: implement unmarshal
            throw new UnsupportedOperationException("unmarshal is not implemented.");
         }

         public String marshal(Object value)
         {
            return SimpleTypeBindings.marshalDate((Calendar)value);
         }
      }
      );

      // map publicId to systemId as it should appear in the resulting XML file
      marshaller.mapPublicIdToSystemId("-//DTD Books//EN", "resources/xml/book/books.dtd");

      // create an instance of ObjectModelProvider with the book instance to be marshalled
      ObjectModelProvider provider = new BookObjectProvider();

      // marshal the book
      marshaller.marshal(dtdReader, provider, book, xmlOutput);

      // close DTD reader
      dtdReader.close();

      checkMarshalledBook(xmlOutput.getBuffer().toString(), book);

      log.debug("</test-marshall-book-dtd>");
   }

   public void testMarshallBookXs() throws Exception
   {
      log.debug("<test-marshall-book-xs>");

      // obtain an instance of Book to marshal
      Book book = createBook();

      // get the output writter to write the XML content
      StringWriter xmlOutput = new StringWriter();

      // get the XML Schema source
      InputStream is = getResource("xml/book/books.xsd");
      Reader xsReader = new InputStreamReader(is);

      // create an instance of XML Schema marshaller
      XsMarshaller marshaller = new XsMarshaller();

      // we need to specify what elements are top most (roots) providing namespace URI, prefix and local name
      marshaller.addRootElement("http://example.org/ns/books/", "", "book");

      // declare default namespace
      marshaller.declareNamespace("bk", "http://example.org/ns/books/");

      // add schema location by declaring xsi namespace and adding xsi:schemaReader attribute
      marshaller.declareNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
      marshaller.addAttribute("xsi", "schemaReader", "string", "http://example.org/ns/books/ resources/book/books.xsd");

      // create an instance of Object Model Provider with no book
      ObjectModelProvider provider = new BookObjectProvider();

      // marshall Book instance passing it as an argument instead of using the one that is returned by the BookObjectProvider
      marshaller.marshal(xsReader, provider, book, xmlOutput);

      // close XML Schema reader
      xsReader.close();

      checkMarshalledBook(xmlOutput.toString(), book);

      log.debug("</test-marshall-book-xs>");
   }

   public void testMarshallBookDtdGeneric() throws Exception
   {
      log.debug("<test-marshall-book-dtd-generic>");

      // obtain an instance of Book to marshal
      Book book = createBook();

      // get the output writter to write the XML content
      StringWriter xmlOutput = new StringWriter();

      // get the DTD source
      InputStream is = getResource("xml/book/books.dtd");
      Reader dtdReader = new InputStreamReader(is);

      // create an instance of DTD marshaller
      Marshaller marshaller = new DtdMarshaller();

      // map publicId to systemId as it should appear in the resulting XML file
      marshaller.mapPublicIdToSystemId("-//DTD Books//EN", "resources/xml/book/books.dtd");

      // create an instance of ObjectModelProvider with the book instance to be marshalled
      ObjectModelProvider provider = new BookGenericObjectModelProvider();

      // marshal the book
      marshaller.marshal(dtdReader, provider, book, xmlOutput);

      // close DTD reader
      dtdReader.close();

      checkMarshalledBook(xmlOutput.getBuffer().toString(), book);

      log.debug("</test-marshall-book-dtd-generic>");
   }

   // Private

   private void unmarshalBook(String xmlSource, ObjectModelFactory factory) throws Exception
   {
      log.debug("<test-unmarshal-" + xmlSource + '>');

      // get the XML stream
      InputStream is = new FileInputStream("resources/xml/book/" + xmlSource);

      // create unmarshaller
      Unmarshaller unmarshaller = UnmarshallerFactory.newInstance()
            .newUnmarshaller();

      // let the object model factory to create an instance of Book and populate it with data from XML
      Book book = (Book)unmarshaller.unmarshal(is, factory, null);

      // close the XML stream
      is.close();

      checkUnmarshalledBook(book);

      log.debug("</test-unmarshal-" + xmlSource + '>');
   }

   private void checkMarshalledBook(String content, Book book)
      throws Exception
   {
      log.debug("marshalled content: " + content);

      Book unmarshalled = new Book();
      ObjectModelFactory factory = new BookObjectFactory();

      Unmarshaller reader = UnmarshallerFactory.newInstance()
            .newUnmarshaller();

      StringReader strReader = new StringReader(content);
      reader.unmarshal(strReader, factory, unmarshalled);
      strReader.close();

      assertEquals(book, unmarshalled);
   }

   private void checkUnmarshalledBook(Book book)
   {
      log.debug("unmarshalled book: " + book);

      assertEquals("Being a Dog Is a Full-Time Job", book.getTitle());
      assertEquals("Charles M. Schulz", book.getAuthor());
      assertEquals("0836217462", book.getIsbn());
      assertEquals(book.getCharactersTotal(), 2);

      for(Iterator iter = book.getCharacters().iterator(); iter.hasNext();)
      {
         BookCharacter character = (BookCharacter)iter.next();
         final String name = character.getName();
         if(name.equals("Snoopy"))
         {
            assertEquals(character.getFriendOf(), "Peppermint Patty");
            assertEquals(character.getSince(), "1950-10-04");
            assertEquals(character.getQualification(), "extroverted beagle");
         }
         else if(name.equals("Peppermint Patty"))
         {
            assertEquals(character.getFriendOf(), null);
            assertEquals(character.getSince(), "1966-08-22");
            assertEquals(character.getQualification(), "bold, brash and tomboyish");
         }
      }
   }

   private static Book createBook()
   {
      Book book = new Book();
      book.setIsbn("0836217462");
      book.setTitle("Being a Dog Is a Full-Time Job");
      book.setAuthor("Charles M. Schulz");

      BookCharacter character = new BookCharacter();
      character.setName("Snoopy");
      character.setFriendOf("Peppermint Patty");
      character.setSince("1950-10-04");
      character.setQualification("extroverted beagle");
      book.addCharacter(character);

      character = new BookCharacter();
      character.setName("Peppermint Patty");
      character.setSince("1966-08-22");
      character.setQualification("bold, brash and tomboyish");
      book.addCharacter(character);

      return book;
   }

   private static InputStream getResource(String name)
   {
      InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
      if(is == null)
         throw new IllegalStateException("Resource not found: " + name);
      return is;
   }
}
