package org.ontoware.rdf2go.impl.jena;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.ontoware.rdf2go.exception.ModelRuntimeException;
import org.ontoware.rdf2go.impl.jena.GeneralDataType;
import org.ontoware.rdf2go.impl.jena.TypeConversion;
import org.ontoware.rdf2go.model.node.DatatypeLiteral;
import org.ontoware.rdf2go.model.node.URI;
import org.ontoware.rdf2go.model.node.impl.DatatypeLiteralImpl;
import org.ontoware.rdf2go.model.node.impl.URIImpl;

import com.hp.hpl.jena.datatypes.BaseDatatype;
import com.hp.hpl.jena.datatypes.RDFDatatype;
import com.hp.hpl.jena.datatypes.TypeMapper;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.impl.LiteralLabel;
import com.hp.hpl.jena.graph.impl.LiteralLabelFactory;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.shared.impl.JenaParameters;


public class DataTypeTesting {
	
	@Test
	public void testDataTypeEqualness() throws Exception {
		
		// die erste DatenTyp URI
		URI testA = new URIImpl("test://somedata-A", false);
		// die zweite DatenTyp URI
		URI testB = new URIImpl("test://somedata-B", false);
		
		// der erste BaseDatatype wird von der ersten DatenTyp URI erzeugt
		BaseDatatype BDtestA1 = new BaseDatatype(testA + "");
		// der zweite BaseDatatype ebenso
		BaseDatatype BDtestA2 = new BaseDatatype(testA + "");
		// f�r den dritten BaseDatatype nehmen wir eine neue Datentyp URI
		BaseDatatype BDtestB = new BaseDatatype(testB + "");
		
		// alle Literals haben den gleichen Inhalt
		
		// das erste Literal kriegt den ersten BaseDatatype
		LiteralLabel litA11 = LiteralLabelFactory.create("teststring", "", BDtestA1);
		// das zweite Liertal kriegt den auch, es hat also komplett die gleichen
		// Eigenschaften wie das erste Literal aber in einem neuen Objekt
		LiteralLabel litA12 = LiteralLabelFactory.create("teststring", "", BDtestA1);
		// jetzt machen wir ein drittes Literal, welches aber den zweiten
		// BasedataType
		// bekommt, der aber eigentlich von der ersten DataTypeURI stammt
		LiteralLabel litA2 = LiteralLabelFactory.create("teststring", "", BDtestA2);
		// und jetzt machen wir noch nen vierten Literal, der von einem neuen
		// Basedatatype komt, welche wiederrum von einer neuen DatatypeURI ist
		LiteralLabel litB = LiteralLabelFactory.create("teststring", "", BDtestB);
		
		// dann wollen wir mal schauen was passiert:
		
		// reflexivit�t: A == A , passt
		assertTrue(litA11.sameValueAs(litA11));
		// gleicher Inhalt, in zwei versch. Objekten, passt auch
		assertTrue(litA11.sameValueAs(litA12));
		// zwei Objekte, mit untersch. BaseDatatypes, von der gleichen Datatype
		// URI:
		// nein
		assertFalse(litA11.sameValueAs(litA2));
		// und zur sicherheit: 2 versch Datentyp URIs: nein
		assertFalse(litA11.sameValueAs(litB));
		
		// und nochmal der negativ Test:
		assertTrue(litB.sameValueAs(litB));
		assertFalse(litB.sameValueAs(litA11));
		assertFalse(litB.sameValueAs(litA12));
		assertFalse(litB.sameValueAs(litA2));
		
		// weiterlesen ;)
		
		// das liegt an der Implementierung von BaseDatatype.isEqual()
		assertFalse(BDtestA1.isEqual(litA11, litA2));
		
		/**
		 * Compares two instances of values of the given datatype. This default
		 * requires value and datatype equality.
		 * 
		 * public boolean isEqual(LiteralLabel value1, LiteralLabel value2) {
		 * return value1.getDatatype() == value2.getDatatype() ^^^^^^ &&
		 * value1.getValue().equals(value2.getValue()); }
		 */
		
		// Es werden dirket 2 Object Referenzen verglichen, und 2 versch.
		// Basedatatype Objecte sind ebend unterschiedlich.
		// wir m�ssen entweder irgendwo auf der Seite von RDF2GO
		// BaseDatatypes recyclen, oder einen eigenen Datentyp schreiben,
		// bei dem isEqual() den Inhalt der Datentyp URIs der beiden
		// Basedatatypes
		// vergleicht.
	}
	
	@Test
	public void testGeneralDataTypeEqualness() throws Exception {
		
		// Die Idee mit dem GeneralDataType ist an sich echt gut, aber:
		// Es kann passieren das mit addStatement(object, object, object,
		// object)
		// als Object des Tripels ein Literal mit Daten Typ erzeugt wird.
		// Wenn ich nun mein Literal mit einem GeneralDataType erzeuge,
		// es in den Graphen einf�ge, und ich sp�ter abfragen will
		// ob dieses data typed Literal sich wirklich im Graphen befindet,
		// dann wird das literal mit dem GeneralDataType mit dem eigentlich
		// gleichen Literal verglichen, aber die zweite Instanz hat einen
		// BaseDatatype
		// weil sie automatisch in Search-Triple oder irgend sowas eingef�gt
		// wurde.
		// Da GeneralDataType auf BaseDatatype down-gecastet werden kann, werden
		// sie
		// dann mit dem BaseDatatype.equals() verglichen und dann passt es
		// wieder nicht.
		
		// L�sung siehe Funktion testDataTypesWithUnknownType()
		
		// die erste DatenTyp URI
		URI testA = new URIImpl("test://somedata-A", false);
		// die zweite DatenTyp URI
		URI testB = new URIImpl("test://somedata-B", false);
		
		// der erste BaseDatatype wird von der ersten DatenTyp URI erzeugt
		GeneralDataType BDtestA1 = new GeneralDataType(testA + "");
		// der zweite BaseDatatype ebenso
		GeneralDataType BDtestA2 = new GeneralDataType(testA + "");
		// f�r den dritten BaseDatatype nehmen wir eine neue Datentyp URI
		GeneralDataType BDtestB = new GeneralDataType(testB + "");
		
		// alle Literals haben den gleichen Inhalt
		
		// das erste Literal kriegt den ersten BaseDatatype
		LiteralLabel litA11 = LiteralLabelFactory.create("teststring", "", BDtestA1);
		// das zweite Liertal kriegt den auch, es hat also komplett die gleichen
		// Eigenschaften wie das erste Literal aber in einem neuen Objekt
		LiteralLabel litA12 = LiteralLabelFactory.create("teststring", "", BDtestA1);
		// jetzt machen wir ein drittes Literal, welches aber den zweiten
		// BasedataType
		// bekommt, der aber eigentlich von der ersten DataTypeURI stammt
		LiteralLabel litA2 = LiteralLabelFactory.create("teststring", "", BDtestA2);
		// und jetzt machen wir noch nen vierten Literal, der von einem neuen
		// Basedatatype komt, welche wiederrum von einer neuen DatatypeURI ist
		LiteralLabel litB = LiteralLabelFactory.create("teststring", "", BDtestB);
		
		// dann wollen wir mal schauen was passiert:
		
		// reflexivit�t: A == A , passt
		assertTrue(litA11.sameValueAs(litA11));
		// gleicher Inhalt, in zwei versch. Objekten, passt auch
		assertTrue(litA11.sameValueAs(litA12));
		// zwei Objekte, mit untersch. BaseDatatypes, von der gleichen Datatype
		// URI:
		
		// ACHTUNG: mit GeneralDataType passt das jetzt weil f�r die Gleichheit
		// der Datentypen
		// nun nur die Gleichheit der URIs der Datentypen notwendig ist
		assertTrue(litA11.sameValueAs(litA2));
		// und zur sicherheit: 2 versch Datentyp URIs: nein
		assertFalse(litA11.sameValueAs(litB));
		
		// und nochmal der negativ Test:
		assertTrue(litB.sameValueAs(litB));
		assertFalse(litB.sameValueAs(litA11));
		assertFalse(litB.sameValueAs(litA12));
		assertFalse(litB.sameValueAs(litA2));
		
		// und hier nochmal der Low Level Test:
		// mit GeneralDataType ist nun isEqual()
		// so implementiert, das die URIs der Datentypen verglichen werden und
		// die Werte der Strings
		// und deswegen ist das jetzt hier True
		assertTrue(BDtestA1.isEqual(litA11, litA2));
		
	}
	
	@Test
	public void testOldDataTypesUsedAsIntended() throws Exception {
		
		// laut der jena-dev Mailingliste, sollte man Datentypen so erzeugen:
		// siehe http://groups.yahoo.com/group/jena-dev/message/14052
		
		// String dtURI = tmpProp.getRange().getURI();
		// RDFDatatype dt = TypeMapper.getInstance().getTypeByName(dtURI);
		// Literal tmpLit = tmpModel.createTypedLiteral("123", dt );
		
		// leider f�hrt das dann dazu das "test"^^xsd:funky equal zu "test" ist,
		// da dann xsd:funky ein unknown data type ist und
		// somit "test"^^xsd:funky genau wie ein plain literal behandelt wird.
		
		// die erste DatenTyp URI
		// URI testA = URIUtils.createURI("test://somedata-A");
		String strTestA = new String("test://somedata-A");
		// die zweite DatenTyp URI
		// URI testB = URIUtils.createURI("test://somedata-B");
		String strTestB = new String("test://somedata-B");
		
		// der erste BaseDatatype wird von der ersten DatenTyp URI erzeugt
		RDFDatatype DTtestA1 = TypeMapper.getInstance().getTypeByName(strTestA);
		// der zweite BaseDatatype ebenso
		RDFDatatype DTtestA2 = TypeMapper.getInstance().getTypeByName(strTestA);
		// f�r den dritten BaseDatatype nehmen wir eine neue Datentyp URI
		RDFDatatype DTtestB = TypeMapper.getInstance().getTypeByName(strTestB);
		
		com.hp.hpl.jena.rdf.model.Model model = ModelFactory.createDefaultModel();
		
		Literal litA11 = model.createTypedLiteral("teststring", DTtestA1);
		Literal litA12 = model.createTypedLiteral("teststring", DTtestA1);
		Literal litA2 = model.createTypedLiteral("teststring", DTtestA2);
		@SuppressWarnings("unused")
		Literal litB = model.createTypedLiteral("teststring", DTtestB);
		
		// alle Literals haben den gleichen Wert !
		
		// dann wollen wir mal schauen was passiert:
		
		// reflexivit�t: A == A , passt
		assertTrue(litA11.equals(litA11));
		// gleicher Inhalt, in zwei versch. Objekten, passt auch
		assertTrue(litA11.equals(litA12));
		// zwei Objekte, mit untersch. BaseDatatypes, von der gleichen Datatype
		// URI:
		
		assertTrue(litA11.equals(litA2));
		// und zur sicherheit: 2 versch Datentyp URIs:
		
		// -> das sollte eigentlich nicht sein
		// TODO jena bug assertFalse(litA11.equals(litB));
		
	}
	
	@Test
	public void testDataTypesWithUnknownType() throws Exception {
		
		// siehe dazu com.hp.hpl.jena.graph.test.TestTypedLiterals.java,
		// Funktion testUnknown()
		// das ganze funktioniert sowohl mit Jena2.2 als auch mit dem jena aus
		// dem cvs
		
		// das ganze Problem scheint wohl zu sein das Jena ziemlich abgefahrene
		// Sachen machen kann
		// mit daten typen und der valdierung und solchen advanced topics.
		// die Erwaeaehnte Test Datei zeigt das ziemlich eindrucksvoll.
		
		// Dieser gesamte Test testet direkt die Funktion von Jena, nicht von
		// rdf2go. (SG)
		
		// die erste DatenTyp URI
		String strTestA = new String("test://somedata-A");
		// die zweite DatenTyp URI
		String strTestB = new String("test://somedata-B");
		
		com.hp.hpl.jena.rdf.model.Model model = ModelFactory.createDefaultModel();
		
		// das hier scheint alles zu sein was notwendig ist damit Jena die data
		// typed literals
		// semantisch so behandelt wie wir es wollen
		// Behold !!
		JenaParameters.enableSilentAcceptanceOfUnknownDatatypes = true;
		
		Literal litA1 = model.createTypedLiteral("teststring", strTestA);
		Literal litA2 = model.createTypedLiteral("teststring", strTestA);
		Literal litB = model.createTypedLiteral("teststring", strTestB);
		
		// dann wollen wir mal schauen was passiert:
		
		// reflexivit�t: A == A , passt
		assertTrue(litA1.equals(litA1));
		// gleicher Inhalt, in zwei versch. Objekten, passt auch
		assertTrue(litA1.equals(litA2));
		// und zur sicherheit: 2 versch Datentyp URIs: nein
		assertFalse(litA1.equals(litB));
		
		// und nochmal der negativ Test:
		assertTrue(litB.equals(litB));
		assertFalse(litB.equals(litA1));
		assertFalse(litB.equals(litA2));
		assertEquals("Extract Datatype URI", litA1.getDatatypeURI(), strTestA);
		assertEquals("Extract value", "teststring", litA1.getLexicalForm());
		
		// im jena cvs geht auch folgendes, damit kann man das Object des Daten
		// Typs besser manipulieren
		// assertEquals("Extract value", l1.getValue(), new
		// BaseDatatype.TypedValue("foo", typeURI));
		
	}
	
	@Test
	public void testNewToJenaNode() throws ModelRuntimeException {
		com.hp.hpl.jena.rdf.model.Model model = ModelFactory.createDefaultModel();
		
		DatatypeLiteralImpl l1 = new DatatypeLiteralImpl("test", new URIImpl("test:funky", false));
		DatatypeLiteralImpl l2 = new DatatypeLiteralImpl("test", new URIImpl("test:funky", false));
		
		Node n1 = TypeConversion.toJenaNode(l1, model);
		Node n2 = TypeConversion.toJenaNode(l2, model);
		
		assertTrue(n1.equals(n2));
		
		Object o1 = TypeConversion.toRDF2Go(n1);
		Object o2 = TypeConversion.toRDF2Go(n2);
		
		assertTrue(o1 instanceof DatatypeLiteral);
		assertTrue(o2 instanceof DatatypeLiteral);
		
		DatatypeLiteralImpl new1 = (DatatypeLiteralImpl)o1;
		DatatypeLiteralImpl new2 = (DatatypeLiteralImpl)o2;
		
		assertTrue(new1.getValue().equals("test"));
		assertTrue(new2.getValue().equals("test"));
		assertTrue(new1.getDatatype().equals(new URIImpl("test:funky", false)));
		assertTrue(new2.getDatatype().equals(new URIImpl("test:funky", false)));
	}
	
}