/** * The contents of this file are subject to the Open Software License * Version 3.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.opensource.org/licenses/osl-3.0.txt * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. */ package org.mulgara.query.filter.arithmetic; import java.net.URI; import org.jrdf.graph.Literal; import org.jrdf.graph.Node; import org.mulgara.query.QueryException; import org.mulgara.query.filter.TestContext; import org.mulgara.query.filter.TestContextOwner; import org.mulgara.query.rdf.BlankNodeImpl; import org.mulgara.query.rdf.LiteralImpl; import org.mulgara.query.rdf.URIReferenceImpl; import org.mulgara.query.filter.value.NumericExpression; import org.mulgara.query.filter.value.NumericLiteral; import org.mulgara.query.filter.value.SimpleLiteral; import org.mulgara.query.filter.value.TypedLiteral; import org.mulgara.query.filter.value.ValueLiteral; import org.mulgara.query.filter.value.Var; import static org.mulgara.query.rdf.XSD.NAMESPACE; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; /** * Tests the numeric operation classes. * This uses a convoluted mechanism to let the compiler determine the correct return types * in each of the implementing classes. * * @created Apr 10, 2008 * @author Paula Gearon * @copyright © 2008 <a href="http://www.topazproject.org/">The Topaz Project</a> * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a> */ public abstract class AbstractOperationUnitTest extends TestCase { URI xsdInt = URI.create(NAMESPACE + "int"); URI xsdLong = URI.create(NAMESPACE + "long"); URI xsdFloat = URI.create(NAMESPACE + "float"); URI xsdDouble = URI.create(NAMESPACE + "double"); /** The first operand to use for this test */ abstract Number op1(); /** The second operand to use for this test */ abstract Number op2(); /** Get an instance of the operation for this test */ abstract BinaryOperation getOperation(NumericExpression e1, NumericExpression e2); /** perform the operation using native data types which are passed in two literals */ abstract ValueLiteral doOperation(Literal l1, Literal l2) throws QueryException; NumericLiteral doOperation(NumericLiteral l1, NumericLiteral l2) throws QueryException { return (NumericLiteral)doOperation(new LiteralImpl(l1.getLexical(), l1.getType().getValue()), new LiteralImpl(l2.getLexical(), l2.getType().getValue())); } enum NumberType { tInt, tLong, tFloat, tDouble }; /** Gets the type of a URI as one of the defined number types */ NumberType getType(Literal l) { URI dt = l.getDatatypeURI(); if (dt.equals(xsdInt)) return NumberType.tInt; if (dt.equals(xsdLong)) return NumberType.tLong; if (dt.equals(xsdFloat)) return NumberType.tFloat; if (dt.equals(xsdDouble)) return NumberType.tDouble; throw new Error("Unknown number type: " + dt); } /** request that the implementing class do the operation and creates a literal */ ValueLiteral getLiteralResult(Node n1, Node n2) throws QueryException { Literal l1 = (Literal)n1; Literal l2 = (Literal)n2; return doOperation(l1, l2); } // use compiler dispatch and autoboxing to convert a value to a typed literal ValueLiteral newLiteral(int x) throws QueryException { return TypedLiteral.newLiteral(x); } ValueLiteral newLiteral(long x) throws QueryException { return TypedLiteral.newLiteral(x); } ValueLiteral newLiteral(float x) throws QueryException { return TypedLiteral.newLiteral(x); } ValueLiteral newLiteral(double x) throws QueryException { return TypedLiteral.newLiteral(x); } // convert a literal to a basic type, explicitly int getInt(Literal l) { return Integer.parseInt(l.getLexicalForm()); } long getLong(Literal l) { return Long.parseLong(l.getLexicalForm()); } float getFloat(Literal l) { return Float.parseFloat(l.getLexicalForm()); } double getDouble(Literal l) { return Double.parseDouble(l.getLexicalForm()); } /** * Build the unit test. * @param name The name of the test */ public AbstractOperationUnitTest(String name) { super(name); } public static Test suite() { return new TestSuite(); } public void testLiteral() throws Exception { NumericLiteral op1i = new NumericLiteral(op1().intValue()); NumericLiteral op2i = new NumericLiteral(op2().intValue()); NumericLiteral op1l = new NumericLiteral(op1().longValue()); NumericLiteral op2l = new NumericLiteral(op2().longValue()); NumericLiteral op1f = new NumericLiteral(op1().floatValue()); NumericLiteral op2f = new NumericLiteral(op2().floatValue()); NumericLiteral op1d = new NumericLiteral(op1().doubleValue()); NumericLiteral op2d = new NumericLiteral(op2().doubleValue()); basicTest(op1i, op2i, doOperation(op1i, op2i)); basicTest(op1i, op2l, doOperation(op1i, op2l)); basicTest(op1i, op2f, doOperation(op1i, op2f)); basicTest(op1i, op2d, doOperation(op1i, op2d)); basicTest(op1l, op2l, doOperation(op1l, op2l)); basicTest(op1l, op2f, doOperation(op1l, op2f)); basicTest(op1l, op2d, doOperation(op1l, op2d)); basicTest(op1f, op2f, doOperation(op1f, op2f)); basicTest(op1f, op2d, doOperation(op1f, op2d)); basicTest(op1d, op2d, doOperation(op1d, op2d)); } private void basicTest(NumericLiteral literal1, NumericLiteral literal2, NumericLiteral literalResult) throws Exception { BinaryOperation op = getOperation(literal1, literal2); assertTrue(op.equals(literalResult)); assertFalse(op.isBlank()); assertFalse(op.isIRI()); assertTrue(op.isLiteral()); assertFalse(op.isURI()); assertTrue(literalResult.getType().equals(op.getType())); assertEquals(SimpleLiteral.EMPTY, op.getLang()); } public void testVar() throws Exception { Var x = new Var("x"); Var y = new Var("y"); BinaryOperation fn = getOperation(x, y); URI fooBar = URI.create("foo:bar"); Literal iop1 = new LiteralImpl("" + op1().intValue(), xsdInt); Literal iop2 = new LiteralImpl("" + op2().intValue(), xsdInt); Literal lop1 = new LiteralImpl("" + op1().longValue(), xsdLong); Literal lop2 = new LiteralImpl("" + op2().longValue(), xsdLong); Literal fop1 = new LiteralImpl("" + op1().floatValue(), xsdFloat); Literal fop2 = new LiteralImpl("" + op2().floatValue(), xsdFloat); Literal dop1 = new LiteralImpl("" + op1().doubleValue(), xsdDouble); Literal dop2 = new LiteralImpl("" + op2().doubleValue(), xsdDouble); Node[][] rows = { new Node[] {iop1, iop2}, new Node[] {iop1, lop2}, new Node[] {iop1, fop2}, new Node[] {iop1, dop2}, new Node[] {lop1, lop2}, new Node[] {lop1, fop2}, new Node[] {lop1, dop2}, new Node[] {fop1, fop2}, new Node[] {fop1, dop2}, new Node[] {dop1, dop2}, // The following are to fail new Node[] {new LiteralImpl("foo", "en"), iop2}, new Node[] {new LiteralImpl("foo", fooBar), iop2}, new Node[] {new URIReferenceImpl(fooBar), iop2}, new Node[] {new BlankNodeImpl(), iop2}, new Node[] {null, iop2} }; TestContext c = new TestContext(new String[] {"x", "y"}, rows); c.beforeFirst(); fn.setContextOwner(new TestContextOwner(c)); // check the context setting fn.setCurrentContext(c); for (int r = 0; r < 10; r++) { assertTrue(c.next()); assertTrue(getLiteralResult(rows[r][0], rows[r][1]).equals(fn)); } assertTrue(c.next()); try { fn.getValue(); fail("Applied operation to a language string and an integer"); } catch (QueryException qe) { } assertTrue(c.next()); try { fn.getValue(); fail("Applied operation to an unknown typed literal and an integer"); } catch (QueryException qe) { } assertTrue(c.next()); try { fn.getValue(); fail("Applied operation to a uri and an integer"); } catch (QueryException qe) { } assertTrue(c.next()); try { fn.getValue(); fail("Applied operation to a blank node and an integer"); } catch (QueryException qe) { } assertTrue(c.next()); try { fn.getValue(); fail("Applied operation to an unbound and an integer"); } catch (QueryException qe) { } assertFalse(c.next()); } }