summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/com/google/gimd/query/Node.scala44
-rw-r--r--src/main/scala/com/google/gimd/query/NodeOps.scala75
-rw-r--r--src/main/scala/com/google/gimd/query/Query.scala61
-rw-r--r--src/test/scala/com/google/gimd/query/QueryASTTestCase.scala87
4 files changed, 267 insertions, 0 deletions
diff --git a/src/main/scala/com/google/gimd/query/Node.scala b/src/main/scala/com/google/gimd/query/Node.scala
new file mode 100644
index 0000000..32aad1a
--- /dev/null
+++ b/src/main/scala/com/google/gimd/query/Node.scala
@@ -0,0 +1,44 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gimd.query
+
+import com.google.gimd.FieldSpecOne
+
+/**
+ * Base class for all nodes in Query AST.
+ *
+ * NOTE: As for now Node does only track the type (T) of the value that comes from
+ * evaluating expression held by Node given specific instance defined by UserType.
+ * However, Node does not track the type of UserType so it's possible that Node's
+ * expression would be applied to wrong instance. This will be fixed in a future.
+ */
+abstract class Node[T]
+
+/**
+ * Node that stores FieldSpec. This node acts as placeholder in query for a given field of specific instance
+ * defined by UserType.
+ */
+final case class FieldSpecOneNode[T](val fieldSpecOne: FieldSpecOne[_, T]) extends Node[T]
+
+/**
+ * Node that holds a constant value of type T.
+ */
+final case class ConstNode[T](val value: T) extends Node[T]
+
+/**
+ * Node that holds Predicate which (in turn) holds arbitrary function T => Boolean where T is a type
+ * of instance that UserType is bound to.
+ */
+final case class PredicateNode[T](p: Predicate[T]) extends Node[Boolean]
diff --git a/src/main/scala/com/google/gimd/query/NodeOps.scala b/src/main/scala/com/google/gimd/query/NodeOps.scala
new file mode 100644
index 0000000..41953ab
--- /dev/null
+++ b/src/main/scala/com/google/gimd/query/NodeOps.scala
@@ -0,0 +1,75 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gimd.query
+
+/**
+ * Base trait for all traits defining operations on given Node[T] stored in leftOperand.
+ */
+trait NodeOps[T] {
+ val leftOperand: Node[T]
+}
+
+/**
+ * Operations applicable to all Node[T] instances.
+ *
+ * Since all primitive data types used in Gimd have equality and (natural) total order defined
+ * we can provide operators related to equality and ordering here.
+ */
+trait AllNodeOps[T] extends NodeOps[T] {
+ import AllNodeOps._
+ def is(right: Node[T]) = Is(leftOperand, right)
+ def ===(right: Node[T]) = is(right)
+ def isNot(right: Node[T]) = !is(right)
+ def !==(right: Node[T]) = isNot(right)
+
+ def <(right: Node[T]) = Relational("<", leftOperand, right)
+ def >(right: Node[T]) = !(<(right)) && !==(right)
+ def <=(right: Node[T]) = <(right) || ===(right)
+ def >=(right: Node[T]) = !(<(right))
+}
+
+object AllNodeOps {
+ case class Is[T](left: Node[T], right: Node[T]) extends Node[Boolean] with BooleanNodeOps {
+ val leftOperand = this
+ }
+
+ case class Relational[T](name: String, left: Node[T], right: Node[T]) extends Node[Boolean] with BooleanNodeOps {
+ val leftOperand = this
+ }
+}
+
+/**
+ * This trait defines boolean operations thus is applicable to Node[Boolean]
+ */
+trait BooleanNodeOps extends AllNodeOps[Boolean] {
+ import BooleanNodeOps._
+ def && (right: Node[Boolean]) = And(leftOperand, right)
+ def || (right: Node[Boolean]) = Or(leftOperand, right)
+ def unary_! = Not(leftOperand)
+}
+
+object BooleanNodeOps {
+ case class And(left: Node[Boolean], right: Node[Boolean]) extends Node[Boolean] with BooleanNodeOps {
+ val leftOperand = this
+ }
+
+ case class Or(left: Node[Boolean], right: Node[Boolean]) extends Node[Boolean] with BooleanNodeOps {
+ val leftOperand = this
+ }
+
+ case class Not(left: Node[Boolean]) extends Node[Boolean] with BooleanNodeOps {
+ val leftOperand = this
+ }
+}
diff --git a/src/main/scala/com/google/gimd/query/Query.scala b/src/main/scala/com/google/gimd/query/Query.scala
new file mode 100644
index 0000000..4251b53
--- /dev/null
+++ b/src/main/scala/com/google/gimd/query/Query.scala
@@ -0,0 +1,61 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gimd.query
+
+import com.google.gimd.{FieldSpecOne, UserType}
+
+/**
+ * Class that holds a Query AST defined for specific UserType.
+ */
+final class Query[U <: UserType[_]](val ut: U, val cond: List[Node[Boolean]]) {
+
+ def where(f: U => Node[Boolean]) = new Query[U](ut, f(ut) :: cond)
+
+}
+
+object Query {
+
+ //this alias is needed due to bug in Scala 2.7.x. It's been fixed in 2.8.0 so once we switch to it
+ //this can be removed
+ type UserType_ = UserType[_]
+
+ implicit def userType2Query[U <: UserType_](ut: U) = new Query[U](ut, Nil)
+
+ //a few conversions to Node[T]
+ implicit def fieldSpecOne2Node[T](fs: FieldSpecOne[_,T]) = FieldSpecOneNode(fs)
+ implicit def nodeOps2Node[T](ops: NodeOps[T]) = ops.leftOperand
+ implicit def const2Node[T](x: T) = ConstNode(x)
+ implicit def arbitraryPredicate2Node[T](p: Predicate[T]) = PredicateNode(p)
+
+ //a few lifts to traits that define operations one can apply to given Nodes.
+ implicit def fieldSpecOne2BooleanNodeOps(fs: FieldSpecOne[_,Boolean]) = new BooleanNodeOps {
+ val leftOperand = FieldSpecOneNode(fs)
+ }
+ implicit def fieldSpecOne2AllNodeOps[T](fs: FieldSpecOne[_,T]) = new AllNodeOps[T] {
+ val leftOperand = FieldSpecOneNode(fs)
+ }
+
+ implicit def booleanConst2BooleanNodeOps(x: Boolean) = new BooleanNodeOps {
+ val leftOperand = ConstNode(x)
+ }
+ implicit def const2AllNodeOps[T](x: T) = new AllNodeOps[T] {
+ val leftOperand = ConstNode(x)
+ }
+
+ implicit def predicate2BooleanNodeOps(p: Predicate[_]) = new BooleanNodeOps {
+ val leftOperand = PredicateNode(p)
+ }
+
+}
diff --git a/src/test/scala/com/google/gimd/query/QueryASTTestCase.scala b/src/test/scala/com/google/gimd/query/QueryASTTestCase.scala
new file mode 100644
index 0000000..68047a1
--- /dev/null
+++ b/src/test/scala/com/google/gimd/query/QueryASTTestCase.scala
@@ -0,0 +1,87 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gimd.query
+
+import com.google.gimd._
+import com.google.gimd.UserType._
+import com.google.gimd.query.AllNodeOps._
+import com.google.gimd.query.BooleanNodeOps._
+import com.google.gimd.query.Query._
+import org.junit.Test
+import org.junit.Assert._
+
+class QueryASTTestCase {
+
+ case class TreeNode(id: Int, name: String)
+ object TreeNodes extends UserType[TreeNode] {
+ val id = FieldSpecOne("id", IntField, _.id)
+ val name = FieldSpecOne("name", StringField, _.name)
+ def fields = id :: name
+ override def children = Seq(new NestedMember("node", TreeNodes))
+ def toUserObject(m: Message) = new TreeNode(id(m), name(m))
+ }
+
+ @Test
+ def equalityOperators = {
+ val q1 = TreeNodes where { _.name is "Joe" }
+ val q2 = TreeNodes where { _.name === "Joe" }
+ val expected = Is(FieldSpecOneNode(TreeNodes.name), ConstNode("Joe")) :: Nil
+ assertEquals(expected, q1.cond)
+ assertEquals(expected, q2.cond)
+ }
+
+ @Test
+ def nonEqualityOperators {
+ val q1 = TreeNodes where { _.name !== "Joe" }
+ val q2 = TreeNodes where { _.name isNot "Joe" }
+ val expected = Not(Is(FieldSpecOneNode(TreeNodes.name), ConstNode("Joe"))) :: Nil
+ assertEquals(expected, q1.cond)
+ assertEquals(expected, q2.cond)
+ }
+
+ @Test
+ def booleanOperators {
+ val left = TreeNodes.name is "Joe"
+ val right = ConstNode(false)
+ val qAnd = TreeNodes where { x => (x.name is "Joe") && false }
+ val qOr = TreeNodes where { x => (x.name is "Joe") || false }
+ val qNot = TreeNodes where { x => !(x.name is "Joe") }
+ assertEquals(And(left, right) :: Nil, qAnd.cond)
+ assertEquals(Or(left, right) :: Nil, qOr.cond)
+ assertEquals(Not(left) :: Nil, qNot.cond)
+ }
+
+ @Test
+ def orderingOperators {
+ val q1 = TreeNodes where { _.name < "Joe" }
+ val q2 = TreeNodes where { _.name <= "Joe" }
+ val q3 = TreeNodes where { _.name > "Joe" }
+ val q4 = TreeNodes where { _.name >= "Joe" }
+ val lt = Relational("<", FieldSpecOneNode(TreeNodes.name), ConstNode("Joe"))
+ val is = Is(FieldSpecOneNode(TreeNodes.name), ConstNode("Joe"))
+ assertEquals(lt :: Nil, q1.cond)
+ assertEquals(Or(lt, is) :: Nil, q2.cond)
+ assertEquals(And(Not(lt), Not(is)) :: Nil, q3.cond)
+ assertEquals(Not(lt) :: Nil, q4.cond)
+ }
+
+ @Test
+ def predicateNode {
+ val p = Predicate[TreeNode](_.name == "Joe")
+ val q = TreeNodes where { x => p && true }
+ assertEquals(And(PredicateNode(p), ConstNode(true)) :: Nil, q.cond)
+ }
+
+}