--- a/directmapping/src/main/scala/DirectMapping.scala Sat Feb 12 23:57:45 2011 -0500
+++ b/directmapping/src/main/scala/DirectMapping.scala Sun Feb 13 00:29:29 2011 -0500
@@ -85,8 +85,8 @@
val s:Node =
r.pk match {
case Some(pk) =>
- /** Table has a primkary key. */
- NodeIRI(iri(r, pk, t.lexvalues(pk)))
+ /** Table has a primkary key. */
+ NodeIRI(tupleToIRI(r, pk, t.lexvalues(pk)))
case None =>
/** Table has no primkary key (but has some candidate keys). */
NodeBNode(freshbnode())
@@ -140,7 +140,7 @@
* a foreign key contribute to generating triples
*/
def referenceSemantics (r:Relation, nodes:NodeMap, t:Tuple, fk:ForeignKey):(Predicate, Object) = {
- val p = predicateSemantics (r, fk)
+ val p = referencePredicateSemantics(r, fk)
val cellvalues:List[CellValue] = t.cellvalues(fk)
val ForeignKey(as, target@Target(_, key)) = fk
val rel = target.r
@@ -159,7 +159,7 @@
*/
def lexicalValueSemantics(r:Relation, t:Tuple, a:AttrName):Option[(Predicate, Object)] = {
// a is implicitly promoted to an AttrList
- val p = predicateSemantics(r, a)
+ val p = lexicalValuePredicateSemantics(r, a)
val cellValue = t(a)
val datatype = r.header(a)
(cellValue, datatype) match {
@@ -176,9 +176,31 @@
}
- def predicateSemantics (r:Relation, as:AttrList) : IRI =
+ /**
+ * the generated IRI has to be "parsable" for a reverse mapping
+ */
+ def lexicalValuePredicateSemantics(r:Relation, a:AttrName):IRI =
+ IRI(UE(r) + "#" + a)
+
+ /**
+ * the generated IRI has to be "parsable" for a reverse mapping
+ */
+ def referencePredicateSemantics(r:Relation, as:AttrList):IRI =
IRI(UE(r) + "#" + as.attrs.mkString("_"))
+ /**
+ * the generated IRI has to be "parsable" for a reverse mapping
+ * this function must generate a different IRI for each tuple
+ * we know (not enforced by a type) that
+ * - as is actually a pk
+ * - the lexicalvalues are generated from pk
+ * hence, the zip operation is safe as both list have the same size
+ */
+ def tupleToIRI(rn:Relation, as:AttrList, ls:List[LexicalValue]):IRI = {
+ val pairs:List[String] = as.attrs zip ls map { case (attrName, lexicalValue) => UE(attrName) + "." + UE(lexicalValue.s) }
+ IRI(UE(rn) + "/" + pairs.mkString("_") + "#_")
+ }
+
// TODO: aren't they already part of the RDF model?
def datatypeSemantics (d:Datatype) : IRI =
d match {
@@ -197,11 +219,6 @@
def UE(r:Relation):String = UE(r.name.n)
def UE(a:AttrName):String = UE(a.n)
- def iri(rn:Relation, as:AttrList, ls:List[LexicalValue]):IRI = {
- val pairs:List[String] = as.attrs zip ls map { case (attrName, lexicalValue) => UE(attrName) + "." + UE(lexicalValue.s) }
- IRI(UE(rn) + "/" + pairs.mkString("_") + "#_")
- }
-
}
}
--- a/rdb/src/main/scala/RDB.scala Sat Feb 12 23:57:45 2011 -0500
+++ b/rdb/src/main/scala/RDB.scala Sun Feb 13 00:29:29 2011 -0500
@@ -84,7 +84,7 @@
* returns all the lexical values corresponding to the given as
* the order from the list is preserved
* we assume the values are restricted to non null cells
- * in particular, it's safe to call giving a primary key
+ * so it's safe to call only with a primary key
*/
def lexvalues (as:AttrList /* forall a in as, a is in this Tuple */):List[LexicalValue] =
as.attrs map {
@@ -94,9 +94,16 @@
}
}
+ /**
+ * returns all the lexical values corresponding to the given as
+ * the order from the list is preserved
+ */
def cellvalues (as:AttrList /* forall a in as, a is in this Tuple */):List[CellValue] =
as.attrs map { m(_) }
+ /**
+ * returns all foreign keys that have only not-null values in it
+ */
def references(r:Relation):Set[ForeignKey] = {
val nullAttributes:Set[AttrName] =
m collect { case (attrName, cellValue) if cellValue == ␀ => attrName } toSet
@@ -104,6 +111,10 @@
r.fks filter { case ForeignKey(as, _) => nullAttributes & as.toSet isEmpty }
}
+ /**
+ * returns all the not null attribute names
+ * such that they don't also define a unary foreign key
+ */
def scalars(r:Relation):Set[AttrName] = {
val notNullAttributes:Set[AttrName] =
m collect { case (attrName, cellValue) if cellValue != ␀ => attrName } toSet
@@ -117,10 +128,17 @@
Tuple(s map { case (name, cellValue) => (AttrName(name), cellValue) } toMap)
}
+ /**
+ * a cell value has either a lexical value or the NULL value
+ */
abstract class CellValue
case class LexicalValue(s:String) extends CellValue
case object ␀ extends CellValue
+ /**
+ * ForeignKeys abstracts a set of foreign keys
+ * the expected operation are available
+ */
case class ForeignKeys (private val fks:Set[ForeignKey]) {
def has(as:AttrList) = fks exists { _.attrs == as }
def getFK(as:AttrList):ForeignKey = fks find { _.attrs == as } get
@@ -135,15 +153,23 @@
ForeignKeys(fks map { case (keys, target) => ForeignKey(keys map { AttrName(_) }, target)} toSet)
}
+ trait AttrList {
+ val attrs:List[AttrName]
+ /**
+ * by definition, a key is unary if it has only one attribute
+ */
+ def isUnary:Boolean = attrs.length == 1
+ }
+
case class ForeignKey(attrs:List[AttrName], target:Target) extends AttrList
case class Target(rel:RelName, key:CandidateKey) {
+ // hack: this field will be updated by the Database when it will be created
+ // as the target is fully know at that moment
var db:Option[Database] = None
lazy val r:Relation = db.get(rel)
}
-
-
case class CandidateKey (attrs:List[AttrName]) extends AttrList
object CandidateKey {
@@ -151,16 +177,6 @@
CandidateKey(l map { AttrName(_) } toList)
}
- trait AttrList {
- val attrs:List[AttrName]
- def isUnary:Boolean = attrs.length == 1
- }
-
- object AttrList {
- // it's always ok to automatically promote a single AttrName to an AttrList
- implicit def promoteAttrsAsAttrList(a:AttrName) = new AttrList { val attrs = List[AttrName](a) }
- }
-
case class AttrName(n:String) {
override def toString = n
}
@@ -169,6 +185,7 @@
override def toString = "/* " + name + " */"
}
+ // TODO: use Enumeration so we can go through the datatypes?
object Datatype {
val CHAR = Datatype("Char")
val VARCHAR = Datatype("Varchar")