diff --git a/jOOQ-scala/pom.xml b/jOOQ-scala/pom.xml index 0b9d8c27c8..a4274b8782 100644 --- a/jOOQ-scala/pom.xml +++ b/jOOQ-scala/pom.xml @@ -154,6 +154,13 @@ jar test + + org.scalatest + scalatest_2.9.0 + 1.8 + jar + test + \ No newline at end of file diff --git a/jOOQ-scala/src/main/scala/org/jooq/scala/Conversions.scala b/jOOQ-scala/src/main/scala/org/jooq/scala/Conversions.scala index 1a89467f5a..4374358577 100644 --- a/jOOQ-scala/src/main/scala/org/jooq/scala/Conversions.scala +++ b/jOOQ-scala/src/main/scala/org/jooq/scala/Conversions.scala @@ -1,59 +1,201 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ package org.jooq.scala import org.jooq._ import org.jooq.impl._ import org.jooq.impl.Factory._ +/** + * jOOQ type conversions used to enhance the jOOQ Java API with Scala Traits + *

+ * Import this object and all of its attributes to profit from an enhanced jOOQ + * API in Scala client code. Here is an example: + *

+ * import java.sql.DriverManager
+ * import org.jooq._
+ * import org.jooq.impl._
+ * import org.jooq.scala.example.h2.Tables._
+ * import collection.JavaConversions._
+ * import org.jooq.scala.Conversions._
+ *
+ * object Test {
+ *   def main(args: Array[String]): Unit = {
+ *     val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
+ *     val f = new Factory(c, SQLDialect.H2);
+ *
+ *     for (
+ *       val r <- f
+ *         select (
+ *           T_BOOK.ID * T_BOOK.AUTHOR_ID,
+ *           T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4,
+ *           T_BOOK.TITLE || " abc" || " xy")
+ *           from T_BOOK
+ *           where (T_BOOK.ID === 3) fetch
+ *     ) {
+ *
+ *       println(r)
+ *     }
+ *   }
+ * }
+ * 
+ * + * @author Lukas Eder + */ object Conversions { - import org.{ jooq => j } - import j. { impl => ji } - - case class JFieldWrapper[T](val underlying : j.Field[T]) - extends ji.CustomField[T] (underlying.getName(), underlying.getDataType()) - with Field[T] { + + /** + * A Scala-esque representation of {@link org.jooq.Field}, implementing + * overloaded operators for common jOOQ operations + */ + case class JFieldWrapper[T](val underlying: Field[T]) + extends CustomField[T] (underlying.getName(), underlying.getDataType()) + with SField[T] { + // ------------------------------------------------------------------------ + // QueryPart API + // ------------------------------------------------------------------------ + def toSQL(context : RenderContext) = underlying.toSQL(context) - def bind(context : BindContext) = underlying.bind(context) - - def *(value : Number) = underlying.mul(value) - def *(value : Field[_ <: Number]) = underlying.mul(value) - - def +(value : Number) = underlying.add(value) + def bind (context : BindContext) = underlying.bind(context) + + // ------------------------------------------------------------------------ + // Arithmetic operations + // ------------------------------------------------------------------------ + + def +(value : Number) = underlying.add(value) def +(value : Field[_ <: Number]) = underlying.add(value) - - def ||(value : String) = underlying.concat(value) + + def -(value : Number) = underlying.sub(value) + def -(value : Field[_ <: Number]) = underlying.sub(value) + + def *(value : Number) = underlying.mul(value) + def *(value : Field[_ <: Number]) = underlying.mul(value) + + def /(value : Number) = underlying.div(value) + def /(value : Field[_ <: Number]) = underlying.div(value) + + def %(value : Number) = underlying.mod(value) + def %(value : Field[_ <: Number]) = underlying.mod(value) + + + def ||(value : String) = underlying.concat(value) //def ||(value : Field[_]) : underlying.concat(value) - def ===(value : T) : Condition = underlying.equal(value) + // ------------------------------------------------------------------------ + // Comparison predicates + // ------------------------------------------------------------------------ + + def ===(value : T) : Condition = underlying.equal(value) def ===(value : Field[T]) : Condition = underlying.equal(value) - } - - case class SFieldWrapper[T](underlying : Field[T]) - extends ji.CustomField[T] (underlying.getName(), underlying.getDataType()) { - def toSQL(context : RenderContext) = underlying.toSQL(context) - def bind(context : BindContext) = underlying.bind(context) + + def !==(value : T) : Condition = underlying.notEqual(value) + def !==(value : Field[T]) : Condition = underlying.notEqual(value) + + def <>(value : T) : Condition = underlying.notEqual(value) + def <>(value : Field[T]) : Condition = underlying.notEqual(value) + + def >(value : T) : Condition = underlying.greaterThan(value) + def >(value : Field[T]) : Condition = underlying.greaterThan(value) + + def >=(value : T) : Condition = underlying.greaterOrEqual(value) + def >=(value : Field[T]) : Condition = underlying.greaterOrEqual(value) + + def <(value : T) : Condition = underlying.lessThan(value) + def <(value : Field[T]) : Condition = underlying.lessThan(value) + + def <=(value : T) : Condition = underlying.lessOrEqual(value) + def <=(value : Field[T]) : Condition = underlying.lessOrEqual(value) } - implicit def asScalaField[T](f : j.Field[T]): Field[T] = f match { + /** + * A Scala-esque representation of {@link org.jooq.Field}, adding overloaded + * operators for common jOOQ operations + */ + trait SField[T] extends QueryPartInternal { + + // ------------------------------------------------------------------------ + // Arithmetic operations + // ------------------------------------------------------------------------ + + def +(value : Number) : Field[T] + def +(value : Field[_ <: Number]) : Field[T] + + def -(value : Number) : Field[T] + def -(value : Field[_ <: Number]) : Field[T] + + def *(value : Number) : Field[T] + def *(value : Field[_ <: Number]) : Field[T] + + def /(value : Number) : Field[T] + def /(value : Field[_ <: Number]) : Field[T] + + def %(value : Number) : Field[T] + def %(value : Field[_ <: Number]) : Field[T] + + def ||(value : String) : Field[String] + //def ||(value : Field[_]) : Field[String] + + // ------------------------------------------------------------------------ + // Comparison predicates + // ------------------------------------------------------------------------ + + def ===(value : T) : Condition + def ===(value : Field[T]) : Condition + + def !==(value : T) : Condition + def !==(value : Field[T]) : Condition + + def <>(value : T) : Condition + def <>(value : Field[T]) : Condition + + def >(value : T) : Condition + def >(value : Field[T]) : Condition + + def >=(value : T) : Condition + def >=(value : Field[T]) : Condition + + def <(value : T) : Condition + def <(value : Field[T]) : Condition + + def <=(value : T) : Condition + def <=(value : Field[T]) : Condition + } + + implicit def asScalaField[T](f : Field[T]): SField[T] = f match { case JFieldWrapper(f) => f case _ => new JFieldWrapper(f) } - - implicit def asJavaField[T](f : Field[T]): j.Field[T] = f match { - case JFieldWrapper(f) => f - case _ => new SFieldWrapper(f) - } - - trait Field[T] extends QueryPartInternal { - def *(value : Number) : Field[T] - def *(value : Field[_ <: Number]) : Field[T] - - def +(value : Number) : Field[T] - def +(value : Field[_ <: Number]) : Field[T] - - def ||(value : String) : Field[String] - //def ||(value : Field[_]) : Field[String] - - def ===(value : T) : Condition - def ===(value : Field[T]) : Condition - } } diff --git a/jOOQ-scala/src/test/scala/Test.scala b/jOOQ-scala/src/test/scala/Test.scala index bc79df5cf7..51c6d96579 100644 --- a/jOOQ-scala/src/test/scala/Test.scala +++ b/jOOQ-scala/src/test/scala/Test.scala @@ -1,8 +1,46 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +import collection.JavaConversions._ + import java.sql.DriverManager + import org.jooq._ import org.jooq.impl._ import org.jooq.scala.example.h2.Tables._ -import collection.JavaConversions._ import org.jooq.scala.Conversions._ object Test { @@ -10,14 +48,13 @@ object Test { val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", ""); val f = new Factory(c, SQLDialect.H2); - for ( - val r <- f + for (r <- f select ( T_BOOK.ID * T_BOOK.AUTHOR_ID, T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4, T_BOOK.TITLE || " abc" || " xy") from T_BOOK - where (T_BOOK.ID === 3) fetch + where (T_BOOK.ID <> 3) fetch ) { println(r) diff --git a/jOOQ-scala/src/test/scala/jooq-settings.xml b/jOOQ-scala/src/test/scala/jooq-settings.xml new file mode 100644 index 0000000000..98e27b8827 --- /dev/null +++ b/jOOQ-scala/src/test/scala/jooq-settings.xml @@ -0,0 +1,11 @@ + + + false + LOWER + UPPER + false + PREPARED_STATEMENT + true + false + true + \ No newline at end of file diff --git a/jOOQ-scala/src/test/scala/org/jooq/scala/test/OperatorOverloadingTest.scala b/jOOQ-scala/src/test/scala/org/jooq/scala/test/OperatorOverloadingTest.scala new file mode 100644 index 0000000000..faacb3e8f9 --- /dev/null +++ b/jOOQ-scala/src/test/scala/org/jooq/scala/test/OperatorOverloadingTest.scala @@ -0,0 +1,45 @@ +package org.jooq.scala.test + +import collection.JavaConversions._ +import org.scalatest.FunSuite +import org.jooq._ +import org.jooq.impl._ +import org.jooq.scala.example.h2.Tables._ +import org.jooq.scala.Conversions._ +import org.jooq.conf.Settings +import javax.xml.bind.JAXB +import org.jooq.conf.SettingsTools + +class OperatorOverloadingTest extends FunSuite { + + test("arithmetic") { + val add1 = T_BOOK.ID + T_BOOK.AUTHOR_ID + val add2 = T_BOOK.ID + 2 + val sub1 = T_BOOK.ID - T_BOOK.AUTHOR_ID + val sub2 = T_BOOK.ID - 2 + val mul1 = T_BOOK.ID * T_BOOK.AUTHOR_ID + val mul2 = T_BOOK.ID * 2 + val div1 = T_BOOK.ID / T_BOOK.AUTHOR_ID + val div2 = T_BOOK.ID / 2 + val mod1 = T_BOOK.ID % T_BOOK.AUTHOR_ID + val mod2 = T_BOOK.ID % 2 + + assert("(t_book.id + t_book.author_id)" == add1.toString(), add1.toString()) + assert("(t_book.id + 2)" == add2.toString(), add2.toString()) + assert("(t_book.id - t_book.author_id)" == sub1.toString(), sub1.toString()) + assert("(t_book.id - 2)" == sub2.toString(), sub2.toString()) + assert("(t_book.id * t_book.author_id)" == mul1.toString(), mul1.toString()) + assert("(t_book.id * 2)" == mul2.toString(), mul2.toString()) + assert("(t_book.id / t_book.author_id)" == div1.toString(), div1.toString()) + assert("(t_book.id / 2)" == div2.toString(), div2.toString()) + assert("mod(t_book.id, t_book.author_id)" == mod1.toString(), mod1.toString()) + assert("mod(t_book.id, 2)" == mod2.toString(), mod2.toString()) + + // Check for the correct application of operator precedence + val combined1 = T_BOOK.ID + T_BOOK.AUTHOR_ID * 2 + val combined2 = T_BOOK.ID * T_BOOK.AUTHOR_ID + 2 + + assert("(t_book.id + (t_book.author_id * 2))" == combined1.toString(), combined1.toString()) + assert("((t_book.id * t_book.author_id) + 2)" == combined2.toString(), combined2.toString()) + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 65f4cac7b8..a27de1106e 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,7 @@ jOOQ-console jOOQ-meta + jOOQ-scala @@ -153,6 +154,11 @@ jooq-meta ${project.version} + + org.jooq + jooq-scala + ${project.version} +