While the relational data model and its commercial implementations have undergone considerable extensions to become object-relational, comparable advances from mainstream object-oriented programming languages seem to be rare. Complementing recent efforts to integrate SQL-like querying of memory-based collections into languages such as C# and Java, we specify a gentle extension of the object-oriented programming model with bidirectional relationships whose use in a program is independent of their multiplicities.
Because of the extent of the undertaking, it has been broken down into different parts, or phases.
Because of the dependence on traits as containers of relationships and associated behaviour, our host language is Scala.
In phase 1, we extend Scala with expressions that can evaluate to any number of, rather than just one, objects. For this, we introduce three annotations, option, one, and any, that can be used to declare the multiplicity of typed declared entities, that is, of variabels (fields, formal parameters, and other local variables) and methods. Like types, the delcared multiplicities propagate through expressions so that the compiler can check well-formedness of a program with respect to multiplicities, and can compile any multiplicities to collections.
The following is a simple sample program using multiplicities:
package orp.coc.test.codegeneration
import orp.coc._
import scala.collection.mutable.ListBuffer
class Person(val name: String) {
override def toString = "Person(\"" + name + "\")"
@any(classOf[ListBuffer[Person]]) var children: Person = _
@one var father: Person = _
@option var spouse: Person = _
def childrenNames: List[String] = children.name
// def childrenNamesWithoutType: List[Person] = children.name
def childrenNamesNative = childrenContainer.asBare map (p => p.name)
}
A usage of that class could look like this:
object PersonApp extends App {
val p = new Person("John")
val lisa = new Person("Lisa")
val dummy = new Person("Dummy")
println("p.children.getClass() : " + p.children.getClass())
println("classOf[List[Person]].isAssignableFrom(p.children.getClass()) : " +
classOf[List[Person]].isAssignableFrom(p.children.getClass()))
p.children_+=(lisa)
p.children_+=(dummy)
println("p.children : " + p.children.asBare)
println("remove (1) : " + p.childrenContainer(1))
p.children_-=(p.children()(1))
println("p.children : " + p.children.asBare)
val ben = new Person("Ben")
p.children_+=(ben)
println("add (Ben) : " + p.children()(1))
println("p.children = " + p.children.asBare)
println("childrenNamesNative : " + p.childrenNamesNative)
println("p.children.name : " + p.children.name)
println("childrenNames : " + p.childrenNames)
println("Select 1st Child")
val child1 = p.children.apply(1)
println("child 1 : " + child1)
println("child 1's children : " + child1.children().asBare)
println("p.children.children : " + p.children.children.asBare)
println("p.children.children.names : " + p.children.children.name)
println("adding to lisa")
lisa.children_+=(new Person("LisaChild1"))
lisa.children_+=(new Person("LisaChild2"))
println("p.children.children : " + p.children.children.asBare)
println("p.children.children.names : " + p.children.children.name)
println("adding to dummy")
ben.children_+=(new Person("BenChild1"))
println("p.children.children : " + p.children.children.asBare)
println("p.children.children.names : " + p.children.children.name)
println("p.childrenContainer.map(x => x.childrenContainer) : " +
p.childrenContainer.map(x => x.childrenContainer))
println("dummy.children : " + dummy.children().asBare)
println("get first child of p - should be Ben")
println("p.children()(1) " + p.children()(1))
val pChild1 = p.children()(1)
}
Another example is the implementation of the Observer Pattern:
package orp.coc.examples.observer
import orp.coc._
import scala.collection.mutable.ListBuffer
trait Subject {
@any(classOf[ListBuffer[Observer]]) var observers: Observer = _
def notifyObservers {
observers().update(this)
}
}
trait Observer {
def update(s: Subject): Unit
}
case class NamedObserver(val name: String) extends Observer {
override def update(s: Subject) {
println("update of " + name + " from " + s)
}
}
case class NamedSubject(val name: String) extends Subject
object ObserverApp extends App {
val subject1 = new NamedSubject("Subject1")
val subject2 = new NamedSubject("Subject2")
val observer1 = NamedObserver("observer1")
val observer2 = NamedObserver("observer2")
val observer3 = NamedObserver("observer3")
subject1.observers_+=(observer1)
subject1.observers_+=(observer2)
subject2.observers_+=(observer2)
subject2.observers_+=(observer3)
subject1.notifyObservers
subject2.notifyObservers
}
The current version of the Scala compiler can be downloaded here.
You need to have a installed and setted up Java Runtime Enviroment in the version <= Java 6.
The Zip should be extracted anywhere on your computer. The archive includes some
examples (contained in src/
) and some scripts to compile and run the included
or your own sources.
coc-compile-src
compiles all files within the source directory src/
cocc
compiles one distinct file
coc
runs a distinct class file — for example:
coc orp.coc.test.codegeneration.PersonApp
coc.bat orp.coc.examples.observer.ObserverApp
Alternativly you can call the compiler (and run the code) via
java -classpath lib\orp-coc-plugin-assembly-1.0.jar %MAIN% -classpath lib\orp-coc-plugin-assembly-1.0.jar -Xplugin-require:orp-coc -d target -sourcepath ../src -Xpluginsdir lib <args>
src
directory.
you can run the code via
java -classpath lib\orp-coc-plugin-assembly-1.0.jar;target <qualified class name>