package tap.metadata;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.StringBufferInputStream;
import java.util.ArrayList;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import adql.db.DBType;
import adql.db.DBType.DBDatatype;
import tap.TAPException;
import tap.metadata.TableSetParser.ForeignKey;

@SuppressWarnings("deprecation")
public class TestTableSetParser {

	private static TableSetParser parser = null;
	private static XMLInputFactory factory = null;

	private static final String namespaceDef = "xmlns:vs=\"http://www.ivoa.net/xml/VODataService/v1.1\" xmlns:vtm=\"http://www.ivoa.net/xml/VOSITables/v1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.ivoa.net/xml/VODataService/v1.1 http://vo.ari.uni-heidelberg.de/docs/schemata/VODataService-v1.1.xsd http://www.ivoa.net/xml/VOSITables/v1.0 http://vo.ari.uni-heidelberg.de/docs/schemata/VOSITables-v1.0.xsd\"";

	@BeforeClass
	public static void setUpBeforeClass() throws Exception{
		// Build an empty parser:
		parser = new TableSetParser();

		// Build the XML factory:
		factory = XMLInputFactory.newInstance();
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception{}

	@Before
	public void setUp() throws Exception{}

	@After
	public void tearDown() throws Exception{}

	private static XMLStreamReader buildReader(final String xmlContent) throws XMLStreamException{
		return factory.createXMLStreamReader(new StringBufferInputStream(xmlContent));
	}

	private static void close(final XMLStreamReader reader){
		if (reader != null){
			try{
				reader.close();
			}catch(Throwable t){}
		}
	}

	@Test
	public void testGetPosition(){
		XMLStreamReader reader = null;
		try{

			// Build a reader with an empty XML document:
			reader = buildReader("");
			assertEquals("[l.1,c.1]", parser.getPosition(reader));
			// note: reader.next() is throwing an error on an empty document => no need to test that.
			close(reader);

			// Build a reader with a simple XML:
			reader = buildReader("<A anAttr=\"attrValue\">node value</A>");

			// Position before starting reading:
			assertEquals("[l.1,c.1]", parser.getPosition(reader));

			// Position after getting the node:
			reader.next(); // START_ELEMENT("A")
			assertEquals("[l.1,c.23]", parser.getPosition(reader));
			// The position after getting an attribute should not change:
			reader.getAttributeLocalName(0); // ATTRIBUTE("attrValue")
			assertEquals("[l.1,c.23]", parser.getPosition(reader));

			// Position after getting the text:
			reader.next(); // CHARACTERS("node value")
			assertEquals("[l.1,c.35]", parser.getPosition(reader));

			// Position after getting the node ending tag:
			reader.next(); // END_ELEMENT("A")
			assertEquals("[l.1,c.37]", parser.getPosition(reader));

			// Position once the end reached:
			reader.next(); // NULL
			assertEquals("[l.-1,c.-1]", parser.getPosition(reader));

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testGoToEndTag(){
		XMLStreamReader reader = null;
		try{

			/* Test with a single empty node AND WITH NULL or ""
			 * => NO TAG SHOULD HAVE BEEN READ: */
			// CASE: null
			reader = buildReader("<A></A>");
			parser.goToEndTag(reader, null);
			assertEquals("[l.1,c.1]", parser.getPosition(reader));
			close(reader);
			// CASE: empty string
			reader = buildReader("<A></A>");
			parser.goToEndTag(reader, "");
			assertEquals("[l.1,c.1]", parser.getPosition(reader));
			close(reader);

			/* Test BEFORE having read the start element:
			 * => AN EXCEPTION SHOULD BE THROWN */
			reader = buildReader("<A></A>");
			try{
				parser.goToEndTag(reader, "A");
				fail("This function should have failed: the START ELEMENT has not yet been read!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.-1,c.-1] Malformed XML document: missing an END TAG </A>!", e.getMessage());
				else
					throw e;
			}
			close(reader);

			/* Test AFTER having read the start element:
			 * => NORMAL USAGE */
			reader = buildReader("<A></A>");
			reader.next(); // START ELEMENT("A")
			parser.goToEndTag(reader, "A");
			assertEquals("[l.1,c.8]", parser.getPosition(reader));
			close(reader);

			/* Test AFTER having read the start element:
			 * => NORMAL USAGE with an embedded node */
			// search for the root node end:
			reader = buildReader("<A><B></B></A>");
			reader.next(); // START ELEMENT("A")
			parser.goToEndTag(reader, "A");
			assertEquals("[l.1,c.15]", parser.getPosition(reader));
			close(reader);
			// variant with some texts:
			reader = buildReader("<A><B>super blabla</B></A>");
			reader.next(); // START ELEMENT("A")
			parser.goToEndTag(reader, "A");
			assertEquals("[l.1,c.27]", parser.getPosition(reader));
			close(reader);
			// variant with some texts + child node:
			reader = buildReader("<A><B>super<C>blabla</C></B></A>");
			reader.next(); // START ELEMENT("A")
			parser.goToEndTag(reader, "A");
			assertEquals("[l.1,c.33]", parser.getPosition(reader));
			close(reader);
			// search for the child node end:
			reader = buildReader("<A><B></B></A>");
			reader.next(); // START ELEMENT("A")
			reader.next(); // START ELEMENT("B")
			parser.goToEndTag(reader, "B");
			assertEquals("[l.1,c.11]", parser.getPosition(reader));
			close(reader);
			// variant with some texts:
			reader = buildReader("<A><B>super blabla</B></A>");
			reader.next(); // START ELEMENT("A")
			reader.next(); // START ELEMENT("B")
			parser.goToEndTag(reader, "B");
			assertEquals("[l.1,c.23]", parser.getPosition(reader));
			close(reader);
			// variant with some texts + child node:
			reader = buildReader("<A><B>super<C>blabla</C></B></A>");
			reader.next(); // START ELEMENT("A")
			reader.next(); // START ELEMENT("B")
			parser.goToEndTag(reader, "B");
			assertEquals("[l.1,c.29]", parser.getPosition(reader));
			close(reader);

			// Test: Search the end tag while the reader is inside one of its children:
			reader = buildReader("<A><B>super<C>blabla</C></B></A>");
			reader.next(); // START ELEMENT("A")
			reader.next(); // START ELEMENT("B")
			parser.goToEndTag(reader, "A");
			assertEquals("[l.1,c.33]", parser.getPosition(reader));
			close(reader);

			// Test with a wrong start node name:
			reader = buildReader("<A></A>");
			reader.next(); // START ELEMENT("A")
			try{
				parser.goToEndTag(reader, "B");
				fail("This function should have failed: the given node name is wrong (no such node in the XML document)!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.-1,c.-1] Malformed XML document: missing an END TAG </B>!", e.getMessage());
				else
					throw e;
			}
			close(reader);

			// Test with malformed XML document:
			// CASE: missing end tag for the root node:
			reader = buildReader("<A><B></B>");
			reader.next(); // START ELEMENT("A")
			try{
				parser.goToEndTag(reader, "A");
				fail("This function should have failed: the node A has no END TAG!");
			}catch(Exception e){
				if (e instanceof XMLStreamException)
					assertEquals("ParseError at [row,col]:[1,11]\nMessage: XML document structures must start and end within the same entity.", e.getMessage());
				else
					throw e;
			}
			close(reader);
			// CASE: missing end tag for a child:
			reader = buildReader("<A><B></A>");
			reader.next(); // START ELEMENT("A")
			try{
				parser.goToEndTag(reader, "A");
				fail("This function should have failed: the node B has no END TAG!");
			}catch(Exception e){
				if (e instanceof XMLStreamException)
					assertEquals("ParseError at [row,col]:[1,9]\nMessage: The element type \"B\" must be terminated by the matching end-tag \"</B>\".", e.getMessage());
				else
					throw e;
			}
			close(reader);
			// CASE: missing end tag for the child to search:
			reader = buildReader("<A><B></A>");
			reader.next(); // START ELEMENT("A")
			reader.next(); // START ELEMENT("B")
			try{
				parser.goToEndTag(reader, "B");
				fail("This function should have failed: the node B has no END TAG!");
			}catch(Exception e){
				if (e instanceof XMLStreamException)
					assertEquals("ParseError at [row,col]:[1,9]\nMessage: The element type \"B\" must be terminated by the matching end-tag \"</B>\".", e.getMessage());
				else
					throw e;
			}
			close(reader);

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testGetText(){
		XMLStreamReader reader = null;
		String txt;
		try{

			// Test with a simple XML and an empty text:
			reader = buildReader("<A></A>");
			txt = parser.getText(reader);
			assertEquals(0, txt.length());
			assertEquals("[l.1,c.4]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.START_ELEMENT, reader.getEventType());
			close(reader);
			// variant with spaces and tabs:
			reader = buildReader("  	 <A></A>");
			txt = parser.getText(reader);
			assertEquals(0, txt.length());
			assertEquals("[l.1,c.8]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.START_ELEMENT, reader.getEventType());
			close(reader);
			// variant with line returns:
			reader = buildReader("  \n <A></A>");
			txt = parser.getText(reader);
			assertEquals(0, txt.length());
			assertEquals("[l.2,c.5]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.START_ELEMENT, reader.getEventType());
			close(reader);

			// Test with a single line text:
			reader = buildReader("<A>	Super  blabla     </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super  blabla", txt);
			assertEquals("[l.1,c.27]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);
			// variant with CDATA:
			reader = buildReader("<A>	Super  <![CDATA[blabla   ]]>  </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super  blabla", txt);
			assertEquals("[l.1,c.39]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);

			// Test with a text of 2 lines:
			reader = buildReader("<A>	Super \n	 blabla     </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super\nblabla", txt);
			assertEquals("[l.2,c.18]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);
			// same test but with an empty line between both:
			reader = buildReader("<A>	Super \n \n	 blabla     </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super\n\nblabla", txt);
			assertEquals("[l.3,c.18]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);
			// same test but starting with an empty line:
			reader = buildReader("<A>\n	 Super \n	 bla  bla     </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super\nbla  bla", txt);
			assertEquals("[l.3,c.20]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);
			// same test but a comment splitting a text part:
			reader = buildReader("<A> Super \n	 bla<!-- a super comment -->  bla     </A>");
			reader.next(); // START ELEMENT("A")
			txt = parser.getText(reader);
			assertEquals("Super\nbla  bla", txt);
			assertEquals("[l.2,c.44]", parser.getPosition(reader));
			assertEquals(XMLStreamConstants.END_ELEMENT, reader.getEventType());
			close(reader);

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testSearchTable(){
		try{

			// Create fake metadata:
			TAPMetadata meta = new TAPMetadata();
			TAPSchema schema = new TAPSchema("SA");
			schema.addTable("TA");
			schema.addTable("TB");
			meta.addSchema(schema);
			schema = new TAPSchema("SB");
			schema.addTable("TB");
			meta.addSchema(schema);

			// Create a fake position:
			final String pos = "[l.10,c.1]";

			// Search for an existing table WITHOUT SCHEMA specification:
			TAPTable t = parser.searchTable("TA", meta, pos);
			assertEquals("TA", t.getADQLName());
			assertEquals("SA", t.getADQLSchemaName());
			// variant with a different case:
			t = parser.searchTable("ta", meta, pos);
			assertEquals("TA", t.getADQLName());
			assertEquals("SA", t.getADQLSchemaName());

			// Search for an existing table WITH SCHEMA specification:
			t = parser.searchTable("SA.TA", meta, pos);
			assertEquals("TA", t.getADQLName());
			assertEquals("SA", t.getADQLSchemaName());
			// variant with a different case:
			t = parser.searchTable("sa.ta", meta, pos);
			assertEquals("TA", t.getADQLName());
			assertEquals("SA", t.getADQLSchemaName());

			// Search with a wrong table name:
			try{
				parser.searchTable("TC", meta, pos);
				fail("This test should have not failed: there is no table named TC in the given metadata.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Unknown table: \"TC\"!", e.getMessage());
				else
					throw e;
			}
			// variant with a correct schema name:
			try{
				parser.searchTable("SA.TC", meta, pos);
				fail("This test should have not failed: there is no table named SA.TC in the given metadata.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Unknown table: \"SA.TC\"!", e.getMessage());
				else
					throw e;
			}

			// Search with a wrong schema name:
			try{
				parser.searchTable("SC.TB", meta, pos);
				fail("This test should have not failed: there is no schema named SC in the given metadata.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Unknown table: \"SC.TB\"!", e.getMessage());
				else
					throw e;
			}

			// Search with an ambiguous table name (missing schema name):
			try{
				parser.searchTable("TB", meta, pos);
				fail("This test should have not failed: there are two table named TB ; a schema name is required to choose the table to select.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Unresolved table: \"TB\"! Several tables have the same name but in different schemas (here: SA.TB, SB.TB). You must prefix this table name by a schema name (expected syntax: \"schema.table\").", e.getMessage());
				else
					throw e;
			}

			// Provide a schema + table name with a wrong syntax (missing table name or schema name):
			try{
				parser.searchTable(".TB", meta, pos);
				fail("This test should have not failed: the schema name is missing before the '.'.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Incorrect full table name - \".TB\": empty schema name!", e.getMessage());
				else
					throw e;
			}
			try{
				parser.searchTable("SB.", meta, pos);
				fail("This test should have not failed: the table name is missing after the '.'.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Incorrect full table name - \"SB.\": empty table name!", e.getMessage());
				else
					throw e;
			}
			try{
				parser.searchTable("toto.SB.TB", meta, pos);
				fail("This test should have not failed: the table name is missing after the '.'.");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals(pos + " Incorrect full table name - \"toto.SB.TB\": only a schema and a table name can be specified (expected syntax: \"schema.table\")\"!", e.getMessage());
				else
					throw e;
			}

		}catch(Exception e){
			e.printStackTrace();
			fail("Unexpected error: " + e.getMessage());
		}
	}

	@Test
	public void testParseFKey(){
		XMLStreamReader reader = null;
		try{

			// Test while search outside from the foreignKey node:
			reader = buildReader("<table><foreignKey><targetTable>SA.TB</targetTable><utype>truc.chose</utype><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey></table>");
			reader.next(); // START ELEMENT("table")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: the reader has not just read the \"foreignKey\" START ELEMENT tag.");
			}catch(Exception e){
				if (e instanceof IllegalStateException)
					assertEquals("[l.1,c.8] Illegal usage of TableSetParser.parseFKey(XMLStreamParser)! This function can be called only when the reader has just read the START ELEMENT tag \"foreignKey\".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

			// Test with a complete and correct XML foreignKey node:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><utype>truc.chose</utype><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			ForeignKey fk = parser.parseFKey(reader);
			assertEquals("SA.TB", fk.targetTable);
			assertEquals("[l.1,c.45]", fk.targetTablePosition);
			assertEquals("truc.chose", fk.utype);
			assertEquals("Foreign key\ndescription.", fk.description);
			assertEquals(1, fk.keyColumns.size());
			assertEquals("col2", fk.keyColumns.get("col1"));
			close(reader);
			// variant with some comments:
			reader = buildReader("<foreignKey><!-- Here, we are inside! --><targetTable><!-- coucou -->SA.TB</targetTable><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn><!-- Here is the end! --></foreignKey><!-- Nothing more! -->");
			reader.next(); // START ELEMENT("foreignKey")
			fk = parser.parseFKey(reader);
			assertEquals("SA.TB", fk.targetTable);
			assertEquals("Foreign key\ndescription.", fk.description);
			assertEquals(1, fk.keyColumns.size());
			assertEquals("col2", fk.keyColumns.get("col1"));
			close(reader);
			// variant with texts at unapropriate places:
			reader = buildReader("<foreignKey>Here, we are <![CDATA[inside!]]><targetTable>SA.TB</targetTable><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn>Here is the end!</foreignKey>Nothing more!");
			reader.next(); // START ELEMENT("foreignKey")
			fk = parser.parseFKey(reader);
			assertEquals("SA.TB", fk.targetTable);
			assertEquals("Foreign key\ndescription.", fk.description);
			assertEquals(1, fk.keyColumns.size());
			assertEquals("col2", fk.keyColumns.get("col1"));
			close(reader);

			// Test with a missing targetTable:
			reader = buildReader("<foreignKey><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: the targetTable node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.2,c.123] Missing \"targetTable\"!", e.getMessage());
			}finally{
				close(reader);
			}
			// variant with duplicated targetTable:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><targetTable>SA.TA</targetTable><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: the targetTable node is duplicated!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.58] Only one \"targetTable\" element can exist in a /tableset/schema/table/foreignKey!", e.getMessage());
			}finally{
				close(reader);
			}

			// Test with a missing fkColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><description>Foreign key\ndescription.</description></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: at least 1 fkColumn node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.2,c.40] Missing at least one \"fkColumn\"!", e.getMessage());
			}finally{
				close(reader);
			}
			// variant with several fkColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><description>Foreign key\ndescription.</description><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn><fkColumn><fromColumn>col3</fromColumn><targetColumn>col4</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			fk = parser.parseFKey(reader);
			assertEquals("SA.TB", fk.targetTable);
			assertEquals("Foreign key\ndescription.", fk.description);
			assertEquals(2, fk.keyColumns.size());
			assertEquals("col2", fk.keyColumns.get("col1"));
			assertEquals("col4", fk.keyColumns.get("col3"));
			close(reader);

			// Test with a missing fromColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><fkColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: the fromColumn node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.99] Missing \"fromColumn\"!", e.getMessage());
			}finally{
				close(reader);
			}
			// variant with several fromColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><fkColumn><fromColumn>col1</fromColumn><fromColumn>col1bis</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: sereval fromColumn are found!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.96] Only one \"fromColumn\" element can exist in a /tableset/schema/table/foreignKey/fkColumn !", e.getMessage());
			}finally{
				close(reader);
			}

			// Test with a missing targetColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><fkColumn><fromColumn>col1</fromColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: the targetColumn node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.95] Missing \"targetColumn\"!", e.getMessage());
			}finally{
				close(reader);
			}
			// variant with several fromColumn:
			reader = buildReader("<foreignKey><targetTable>SA.TB</targetTable><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn><targetColumn>col2bis</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			try{
				parser.parseFKey(reader);
				fail("This test should have failed: several targetColumn are found!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.131] Only one \"targetColumn\" element can exist in a /tableset/schema/table/foreignKey/fkColumn !", e.getMessage());
			}finally{
				close(reader);
			}

			// Test with a additional node:
			reader = buildReader("<foreignKey><super>blabla</super><foo>anything</foo><targetTable>SA.TB</targetTable><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey>");
			reader.next(); // START ELEMENT("foreignKey")
			fk = parser.parseFKey(reader);
			assertEquals("SA.TB", fk.targetTable);
			assertNull(fk.description);
			assertEquals(1, fk.keyColumns.size());
			assertEquals("col2", fk.keyColumns.get("col1"));
			close(reader);

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testParseDataType(){
		XMLStreamReader reader = null;
		try{

			// Test while search outside from the dataType node:
			reader = buildReader("<column " + namespaceDef + "><dataType arraysize=\"*\">char</dataType></column>");
			reader.next(); // START ELEMENT("column")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the reader has not just read the \"dataType\" START ELEMENT tag.");
			}catch(Exception e){
				if (e instanceof IllegalStateException)
					assertEquals("[l.1,c.408] Illegal usage of TableSetParser.parseDataType(XMLStreamParser)! This function can be called only when the reader has just read the START ELEMENT tag \"dataType\".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

			// Test with a correct TAP type:
			reader = buildReader("<column " + namespaceDef + "><dataType xsi:type=\"vs:TAPType\">varchar</dataType></column>");
			reader.next(); // START ELEMENT("column")
			reader.next(); // START ELEMENT("dataType")
			DBType dt = parser.parseDataType(reader);
			assertEquals(DBDatatype.VARCHAR, dt.type);
			assertEquals(-1, dt.length);
			close(reader);

			// Test with a correct VOTable type:
			reader = buildReader("<dataType " + namespaceDef + " xsi:type=\"vs:VOTableType\" arraysize=\"*\">char</dataType>");
			reader.next(); // START ELEMENT("dataType")
			dt = parser.parseDataType(reader);
			assertEquals(DBDatatype.VARCHAR, dt.type);
			assertEquals(-1, dt.length);
			close(reader);

			// Test with a missing xsi:type:
			reader = buildReader("<dataType " + namespaceDef + " arraysize=\"*\">char</dataType>");
			reader.next(); // START ELEMENT("dataType")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the attribute xsi:type is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.424] Missing attribute \"xsi:type\" (where xsi = \"" + TableSetParser.XSI_NAMESPACE + "\")! Expected attribute value: vs:VOTableType or vs:TAPType, where vs = " + TableSetParser.VODATASERVICE_NAMESPACE + ".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}
			// variant with a wrong namespace prefix
			reader = buildReader("<dataType " + namespaceDef + " xsj:type=\"vs:VOTableType\" arraysize=\"*\">char</dataType>");
			try{
				reader.next(); // START ELEMENT("dataType")
				fail("This test should have failed: the prefix of the xsi:type attribute is wrong!");
			}catch(Exception e){
				if (e instanceof XMLStreamException)
					assertEquals("ParseError at [row,col]:[1,450]\nMessage: http://www.w3.org/TR/1999/REC-xml-names-19990114#AttributePrefixUnbound?dataType&xsj:type&xsj", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}
			// variant with a missing namespace prefix:
			reader = buildReader("<dataType xsi:type=\"vs:VOTableType\" arraysize=\"*\">char</dataType>");
			try{
				reader.next(); // START ELEMENT("dataType")
				fail("This test should have failed: the namespace xsi is not defined!");
			}catch(Exception e){
				if (e instanceof XMLStreamException)
					assertEquals("ParseError at [row,col]:[1,51]\nMessage: http://www.w3.org/TR/1999/REC-xml-names-19990114#AttributePrefixUnbound?dataType&xsi:type&xsi", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

			// Test with an unsupported xsi:type:
			reader = buildReader("<dataType " + namespaceDef + " xsi:type=\"vs:foo\" arraysize=\"*\">char</dataType>");
			reader.next(); // START ELEMENT("dataType")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the type foo is not defined in VODataService!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.457] Unsupported type: \"vs:foo\"! Expected: vs:VOTableType or vs:TAPType, where vs = " + TableSetParser.VODATASERVICE_NAMESPACE + ".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}
			// variant with no namespace prefix in front of the wrong type:
			reader = buildReader("<dataType " + namespaceDef + " xsi:type=\"foo\" arraysize=\"*\">char</dataType>");
			reader.next(); // START ELEMENT("dataType")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the namespace prefix is missing in the value of xsi:type!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.439] Unresolved type: \"foo\"! Missing namespace prefix.", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

			// Test with a missing datatype:
			reader = buildReader("<dataType " + namespaceDef + " xsi:type=\"vs:TAPType\"></dataType>");
			reader.next(); // START ELEMENT("dataType")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the datatype value is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.443] Missing column datatype!", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}
			// variant with a wrong datatype:
			reader = buildReader("<dataType " + namespaceDef + " xsi:type=\"vs:TAPType\">foo</dataType>");
			reader.next(); // START ELEMENT("dataType")
			try{
				parser.parseDataType(reader);
				fail("This test should have failed: the datatype value is unknown!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.446] Unknown TAPType: \"foo\"!", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testParseColumn(){
		XMLStreamReader reader = null;
		try{

			// Test while search outside from the column node:
			reader = buildReader("<table " + namespaceDef + "><column std=\"true\"><name>col1</name><description>Column\ndescription.</description><dataType xsi:type=\"vs:TAPType\">SMALLINT</dataType><utype>truc.chose</utype><ucd>t.c</ucd><unit>deg</unit><flag>nullable</flag><flag>primary</flag><flag>indexed</flag></column></table>");
			reader.next(); // START ELEMENT("table")
			try{
				parser.parseColumn(reader);
				fail("This test should have failed: the reader has not just read the \"column\" START ELEMENT tag.");
			}catch(Exception e){
				if (e instanceof IllegalStateException)
					assertEquals("[l.1,c.407] Illegal usage of TableSetParser.parseColumn(XMLStreamParser)! This function can be called only when the reader has just read the START ELEMENT tag \"column\".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
			}

			// Test with a complete and correct XML column node:
			reader = buildReader("<table " + namespaceDef + "><column std=\"true\"><name>col1</name><description>Column\ndescription.</description><dataType xsi:type=\"vs:TAPType\">SMALLINT</dataType><utype>truc.chose</utype><ucd>t.c</ucd><unit>deg</unit><flag>nullable</flag><flag>primary</flag><flag>indexed</flag></column></table>");
			reader.next(); // START ELEMENT("table")
			reader.next(); // START ELEMENT("column")
			TAPColumn col = parser.parseColumn(reader);
			assertEquals("col1", col.getADQLName());
			assertEquals("Column\ndescription.", col.getDescription());
			assertEquals(DBDatatype.SMALLINT, col.getDatatype().type);
			assertEquals(-1, col.getDatatype().length);
			assertEquals("truc.chose", col.getUtype());
			assertEquals("t.c", col.getUcd());
			assertEquals("deg", col.getUnit());
			assertTrue(col.isIndexed());
			assertTrue(col.isPrincipal());
			assertTrue(col.isNullable());
			assertTrue(col.isStd());
			close(reader);
			// variant with entering inside the foreignKey node (as it is done by TableSetParser):
			reader = buildReader("<column " + namespaceDef + "><name>col1</name><description>Column\ndescription.</description><dataType xsi:type=\"vs:TAPType\">SMALLINT</dataType><utype>truc.chose</utype><ucd>t.c</ucd><unit>deg</unit></column>");
			reader.next(); // START ELEMENT("column")
			col = parser.parseColumn(reader);
			assertEquals("col1", col.getADQLName());
			assertEquals("Column\ndescription.", col.getDescription());
			assertEquals(DBDatatype.SMALLINT, col.getDatatype().type);
			assertEquals(-1, col.getDatatype().length);
			assertEquals("truc.chose", col.getUtype());
			assertEquals("t.c", col.getUcd());
			assertEquals("deg", col.getUnit());
			assertFalse(col.isIndexed());
			assertFalse(col.isPrincipal());
			assertFalse(col.isNullable());
			assertFalse(col.isStd());
			close(reader);
			// variant with some comments:
			reader = buildReader("<column " + namespaceDef + "><!-- Here we are inside! --><name>col1</name><!-- Here blabla about the column. --><description>Column\ndescription.</description><dataType xsi:type=\"vs:TAPType\">SMALLINT</dataType><utype>truc.chose</utype><ucd>t.c</ucd><unit>deg</unit><!-- Here is the end! --></column><!-- Nothing more! -->");
			reader.next(); // START ELEMENT("column")
			col = parser.parseColumn(reader);
			assertEquals("col1", col.getADQLName());
			assertEquals("Column\ndescription.", col.getDescription());
			assertEquals(DBDatatype.SMALLINT, col.getDatatype().type);
			assertEquals(-1, col.getDatatype().length);
			assertEquals("truc.chose", col.getUtype());
			assertEquals("t.c", col.getUcd());
			assertEquals("deg", col.getUnit());
			assertFalse(col.isIndexed());
			assertFalse(col.isPrincipal());
			assertFalse(col.isNullable());
			assertFalse(col.isStd());
			close(reader);
			// variant with texts at unapropriate places:
			reader = buildReader("<column " + namespaceDef + ">Here we are <![CDATA[inside!]]><name>col1</name><description>Column\ndescription.</description><dataType xsi:type=\"vs:TAPType\">SMALLINT</dataType><utype>truc.chose</utype><ucd>t.c</ucd><unit>deg</unit></column>Nothing more!");
			reader.next(); // START ELEMENT("column")
			col = parser.parseColumn(reader);
			assertEquals("col1", col.getADQLName());
			assertEquals("Column\ndescription.", col.getDescription());
			assertEquals(DBDatatype.SMALLINT, col.getDatatype().type);
			assertEquals(-1, col.getDatatype().length);
			assertEquals("truc.chose", col.getUtype());
			assertEquals("t.c", col.getUcd());
			assertEquals("deg", col.getUnit());
			assertFalse(col.isIndexed());
			assertFalse(col.isPrincipal());
			assertFalse(col.isNullable());
			assertFalse(col.isStd());
			close(reader);

			// Test with a missing "name" node:
			reader = buildReader("<column></column>");
			reader.next(); // START ELEMENT("column")
			try{
				parser.parseColumn(reader);
				fail("This test should have failed: the \"name\" node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.18] Missing column \"name\"!", e.getMessage());
			}finally{
				close(reader);
			}
			// variant with duplicated "name":
			reader = buildReader("<column><name>col1</name><name>colA</name></column>");
			reader.next(); // START ELEMENT("column")
			try{
				parser.parseColumn(reader);
				fail("This test should have failed: the \"name\" node is duplicated!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.32] Only one \"name\" element can exist in a /tableset/schema/table/column!", e.getMessage());
			}finally{
				close(reader);
			}

			// Test with a additional node:
			reader = buildReader("<column><name>col1</name><dbname>colA</dbname><foo>blabla</foo></column>");
			reader.next(); // START ELEMENT("foreignKey")
			col = parser.parseColumn(reader);
			assertEquals("col1", col.getADQLName());
			assertNull(col.getDescription());
			assertEquals(DBDatatype.UNKNOWN, col.getDatatype().type);
			assertEquals(-1, col.getDatatype().length);
			assertNull(col.getUtype());
			assertNull(col.getUcd());
			assertNull(col.getUnit());
			assertFalse(col.isIndexed());
			assertFalse(col.isPrincipal());
			assertFalse(col.isNullable());
			assertFalse(col.isStd());
			close(reader);

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
		}
	}

	@Test
	public void testParseTable(){
		XMLStreamReader reader = null;
		ArrayList<ForeignKey> fkeys = new ArrayList<ForeignKey>(0);
		try{

			// Test while search outside from the table node:
			reader = buildReader("<schema><table><name>TableA</name></table></schema>");
			reader.next(); // START ELEMENT("schema")
			try{
				parser.parseTable(reader, fkeys);
				fail("This test should have failed: the reader has not just read the \"table\" START ELEMENT tag.");
			}catch(Exception e){
				if (e instanceof IllegalStateException)
					assertEquals("[l.1,c.9] Illegal usage of TableSetParser.parseTable(XMLStreamParser)! This function can be called only when the reader has just read the START ELEMENT tag \"table\".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
				fkeys.clear();
			}

			// Test with a complete and correct XML table node:
			reader = buildReader("<table><name>TableA</name><description>Table \ndescription.</description><utype>truc.chose</utype><title>Table title</title><column><name>col1</name></column><foreignKey><targetTable>TB</targetTable><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey></table>");
			reader.next(); // START ELEMENT("table")
			TAPTable t = parser.parseTable(reader, fkeys);
			assertEquals("TableA", t.getADQLName());
			assertEquals("Table\ndescription.", t.getDescription());
			assertEquals("truc.chose", t.getUtype());
			assertEquals("Table title", t.getTitle());
			assertEquals(1, t.getNbColumns());
			assertNotNull(t.getColumn("col1"));
			assertEquals(0, t.getNbForeignKeys());
			assertEquals(1, fkeys.size());
			assertEquals("TB", fkeys.get(0).targetTable);
			assertEquals(t, fkeys.get(0).fromTable);
			close(reader);
			fkeys.clear();
			// variant with some comments:
			reader = buildReader("<table><!-- Here we are inside! --><name>TableA</name><description>Table \ndescription.</description><utype><!-- Table UType -->truc.chose</utype><title>Table title</title><column><name>col1</name></column><foreignKey><targetTable>TB</targetTable><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey></table><!-- Nothing more! -->");
			reader.next(); // START ELEMENT("table")
			t = parser.parseTable(reader, fkeys);
			assertEquals("TableA", t.getADQLName());
			assertEquals("Table\ndescription.", t.getDescription());
			assertEquals("truc.chose", t.getUtype());
			assertEquals("Table title", t.getTitle());
			assertEquals(1, t.getNbColumns());
			assertNotNull(t.getColumn("col1"));
			assertEquals(0, t.getNbForeignKeys());
			assertEquals(1, fkeys.size());
			assertEquals("TB", fkeys.get(0).targetTable);
			assertEquals(t, fkeys.get(0).fromTable);
			close(reader);
			fkeys.clear();
			// variant with texts at unapropriate places:
			reader = buildReader("<table>Here we are <![CDATA[inside!]]><name>TableA</name><description>Table \ndescription.</description><utype><!-- Table UType -->truc.chose</utype><title>Table title</title><column><name>col1</name></column><foreignKey><targetTable>TB</targetTable><fkColumn><fromColumn>col1</fromColumn><targetColumn>col2</targetColumn></fkColumn></foreignKey></table>Nothing more!");
			reader.next(); // START ELEMENT("table")
			t = parser.parseTable(reader, fkeys);
			assertEquals("TableA", t.getADQLName());
			assertEquals("Table\ndescription.", t.getDescription());
			assertEquals("truc.chose", t.getUtype());
			assertEquals("Table title", t.getTitle());
			assertEquals(1, t.getNbColumns());
			assertNotNull(t.getColumn("col1"));
			assertEquals(0, t.getNbForeignKeys());
			assertEquals(1, fkeys.size());
			assertEquals("TB", fkeys.get(0).targetTable);
			assertEquals(t, fkeys.get(0).fromTable);
			close(reader);
			fkeys.clear();

			// Test with a missing "name" node:
			reader = buildReader("<table></table>");
			reader.next(); // START ELEMENT("table")
			try{
				parser.parseTable(reader, fkeys);
				fail("This test should have failed: the \"name\" node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.16] Missing table \"name\"!", e.getMessage());
			}finally{
				close(reader);
				fkeys.clear();
			}
			// variant with duplicated "name":
			reader = buildReader("<table><name>Table1</name><name>TableA</name></table>");
			reader.next(); // START ELEMENT("table")
			try{
				parser.parseTable(reader, fkeys);
				fail("This test should have failed: the \"name\" node is duplicated!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.33] Only one \"name\" element can exist in a /tableset/schema/table!", e.getMessage());
			}finally{
				close(reader);
				fkeys.clear();
			}

			// Test with a additional node:
			reader = buildReader("<table><name>TableA</name><dbname>SuperTableA</dbname><foo>blabla</foo></table>");
			reader.next(); // START ELEMENT("table")
			t = parser.parseTable(reader, fkeys);
			assertEquals("TableA", t.getADQLName());
			assertNull(t.getDescription());
			assertNull(t.getUtype());
			assertNull(t.getTitle());
			assertEquals(0, t.getNbColumns());
			assertEquals(0, t.getNbForeignKeys());
			assertEquals(0, fkeys.size());
			close(reader);
			fkeys.clear();

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
			fkeys.clear();
		}
	}

	@Test
	public void testParseSchema(){
		XMLStreamReader reader = null;
		ArrayList<ForeignKey> fkeys = new ArrayList<ForeignKey>(0);
		try{

			// Test while search outside from the schema node:
			reader = buildReader("<tableset><schema><name>PublicSchema</name></schema></tableset>");
			reader.next(); // START ELEMENT("tableset")
			try{
				parser.parseSchema(reader, fkeys);
				fail("This test should have failed: the reader has not just read the \"schema\" START ELEMENT tag.");
			}catch(Exception e){
				if (e instanceof IllegalStateException)
					assertEquals("[l.1,c.11] Illegal usage of TableSetParser.parseSchema(XMLStreamParser)! This function can be called only when the reader has just read the START ELEMENT tag \"schema\".", e.getMessage());
				else
					throw e;
			}finally{
				close(reader);
				fkeys.clear();
			}

			// Test with a complete and correct XML table node:
			reader = buildReader("<schema><name>PublicSchema</name><description>Schema \ndescription.</description><utype>truc.chose</utype><title>Schema title</title><table><name>TableA</name></table></schema>");
			reader.next(); // START ELEMENT("schema")
			TAPSchema s = parser.parseSchema(reader, fkeys);
			assertEquals("PublicSchema", s.getADQLName());
			assertEquals("Schema\ndescription.", s.getDescription());
			assertEquals("truc.chose", s.getUtype());
			assertEquals("Schema title", s.getTitle());
			assertEquals(1, s.getNbTables());
			assertNotNull(s.getTable("TableA"));
			close(reader);
			fkeys.clear();
			// variant with some comments:
			reader = buildReader("<schema><!-- Here we are inside! --><name>PublicSchema <!-- schema name --></name><description>Schema \ndescription.</description><utype>truc.chose</utype><title>Schema title</title><table><name>TableA</name></table></schema><!-- Nothing more! -->");
			reader.next(); // START ELEMENT("schema")
			s = parser.parseSchema(reader, fkeys);
			assertEquals("PublicSchema", s.getADQLName());
			assertEquals("Schema\ndescription.", s.getDescription());
			assertEquals("truc.chose", s.getUtype());
			assertEquals("Schema title", s.getTitle());
			assertEquals(1, s.getNbTables());
			assertNotNull(s.getTable("TableA"));
			close(reader);
			fkeys.clear();
			// variant with texts at unapropriate places:
			reader = buildReader("<schema>Here we are <![CDATA[inside!]]><name>PublicSchema <!-- schema name --></name><description>Schema \ndescription.</description><utype>truc.chose</utype><title>Schema title</title><table><name>TableA</name></table></schema>Nothing more!");
			reader.next(); // START ELEMENT("schema")
			s = parser.parseSchema(reader, fkeys);
			assertEquals("PublicSchema", s.getADQLName());
			assertEquals("Schema\ndescription.", s.getDescription());
			assertEquals("truc.chose", s.getUtype());
			assertEquals("Schema title", s.getTitle());
			assertEquals(1, s.getNbTables());
			assertNotNull(s.getTable("TableA"));
			close(reader);
			fkeys.clear();

			// Test with a missing "name" node:
			reader = buildReader("<schema></schema>");
			reader.next(); // START ELEMENT("schema")
			try{
				parser.parseSchema(reader, fkeys);
				fail("This test should have failed: the \"name\" node is missing!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.18] Missing schema \"name\"!", e.getMessage());
			}finally{
				close(reader);
				fkeys.clear();
			}
			// variant with duplicated "name":
			reader = buildReader("<schema><name>PublicSchema</name><name>PrivateSchema</name></schema>");
			reader.next(); // START ELEMENT("schema")
			try{
				parser.parseSchema(reader, fkeys);
				fail("This test should have failed: the \"name\" node is duplicated!");
			}catch(Exception e){
				if (e instanceof TAPException)
					assertEquals("[l.1,c.40] Only one \"name\" element can exist in a /tableset/schema!", e.getMessage());
			}finally{
				close(reader);
				fkeys.clear();
			}

			// Test with a additional node:
			reader = buildReader("<schema><name>PublicSchema</name><dbname>public</dbname><foo>blabla</foo></schema>");
			reader.next(); // START ELEMENT("schema")
			s = parser.parseSchema(reader, fkeys);
			assertEquals("PublicSchema", s.getADQLName());
			assertNull(s.getDescription());
			assertNull(s.getUtype());
			assertNull(s.getTitle());
			assertEquals(0, s.getNbTables());
			close(reader);
			fkeys.clear();

		}catch(Exception e){
			e.printStackTrace();
			if (e instanceof XMLStreamException)
				fail("Unexpected error while reading the XML content: " + e.getMessage());
			else
				fail("Unexpected error: " + e.getMessage());
		}finally{
			close(reader);
			fkeys.clear();
		}
	}

}
