scala/lift/pgsql-inet-model.scala
changeset 4 5ef63a5d98b2
equal deleted inserted replaced
3:d45ef9f0c1ae 4:5ef63a5d98b2
       
     1 /*
       
     2  * Copyright ...
       
     3  */
       
     4 package example
       
     5 
       
     6 import java.net.InetAddress
       
     7 import net.liftweb.common._
       
     8 import net.liftweb.mapper._
       
     9 import net.liftweb.util._
       
    10 import net.liftweb.util.Helpers._
       
    11 
       
    12 object InetAddressHelpers extends InetAddressHelpers
       
    13 
       
    14 trait InetAddressHelpers {
       
    15   def normalize(inet: String): Box[String] = tryo {
       
    16     InetAddress.getByName(inet).getHostAddress
       
    17   }
       
    18 
       
    19   def valid_?(inet: String): Boolean = (inet != null) && (tryo {
       
    20     InetAddress.getByName(inet)
       
    21   } isDefined ) && (inet.length > 0)
       
    22 
       
    23   def toBigInt(inet: InetAddress): BigInt = BigInt(1, inet.getAddress)
       
    24 
       
    25   def toInet(s: String): Box[InetAddress] = s match {
       
    26     case null | "" => Empty
       
    27     case _ => tryo { InetAddress.getByName(s) }
       
    28   }
       
    29 }
       
    30 
       
    31 /**
       
    32  * Postgres specific <code>InetAddress</code> mapping
       
    33  * (maps to ''inet'' data type).
       
    34  * Always use <code>asInet</code> to get <code>InetAddress</code> instance.
       
    35  */
       
    36 abstract class MappedInetAddress[T<:Mapper[T]](owner: T) extends
       
    37   MappedString[T](owner, 128) with InetAddressHelpers {
       
    38 
       
    39   override def setFilter = normalizeFilter _ :: super.setFilter
       
    40 
       
    41   override def validate = (valid_?(i_is_!) match {
       
    42     case true => Nil
       
    43     case false => List(FieldError(this, "Invalid IP address"))
       
    44   }) ::: super.validate
       
    45 
       
    46   def normalizeFilter(inet: String): String = inet match {
       
    47     case null | "" => null
       
    48     case s => normalize(s) openOr null
       
    49   }
       
    50 
       
    51   def asInet: Box[InetAddress] = toInet(is)
       
    52 
       
    53   override def targetSQLType = Types.OTHER
       
    54 
       
    55   override def fieldCreatorString(d: DriverType, colName: String) = d match {
       
    56     case _d: BasePostgreSQLDriver => colName + " inet"
       
    57     case _ => super.fieldCreatorString(d, colName)
       
    58   }
       
    59 }
       
    60 
       
    61 /**
       
    62  * Caution: getters may return <code>null</code> value.
       
    63  * Always use <code>asInet</code>.
       
    64  */
       
    65 abstract class MappedNullableInetAddress[T<:Mapper[T]](owner: T) extends
       
    66   MappedInetAddress[T](owner) {
       
    67 
       
    68   override def defaultValue = null
       
    69 
       
    70   override def validate = i_is_! match {
       
    71     case null | "" => Nil
       
    72     case _ => super.validate
       
    73   }
       
    74 
       
    75   override def dbNotNull_? = false
       
    76 }
       
    77 
       
    78 /**
       
    79  * IP network mapper via its start/end fields.
       
    80  * Implemented validation of IP overlaps, parent network, start, end.
       
    81  * Maintains network relationships (sub-net/super-net) and updates
       
    82  * parent field accordingly.
       
    83  */
       
    84 object ExampleNetwork extends ExampleNetwork with
       
    85   LongKeyedMetaMapper[ExampleNetwork] with LongCRUDify[ExampleNetwork] {
       
    86 
       
    87   override def validation: List[ExampleNetwork => List[FieldError]] = 
       
    88     startLtEnd _ :: checkOverlap _ :: super.validation
       
    89 
       
    90   def startLtEnd(n: ExampleNetwork): List[FieldError] = (for {
       
    91     s <- n.start.asInet
       
    92     e <- n.end.asInet
       
    93   } yield (toBigInt(s) < toBigInt(e))) match {
       
    94     case Full(true) => Nil
       
    95     case Full(false) =>
       
    96       List(FieldError(n.start, "Start must be below end"))
       
    97     case _ =>
       
    98       List(FieldError(n.start, S ? "Can't compare start/end IP"))
       
    99   }
       
   100 
       
   101   def checkOverlap(net: ExampleNetwork): List[FieldError] = {
       
   102     def run(f: MappedInetAddress[ExampleNetwork]) = {
       
   103       findAll(inNet(f):_*) filter {!_.comparePrimaryKeys(net)} filter { n =>
       
   104         net.isSubnetOf(n) match {
       
   105           case f: Failure => true
       
   106           case _ => false
       
   107       }} map { n =>
       
   108         FieldError(f, "Overlaps with: " + n.start + " - " + n.end)
       
   109       }
       
   110     }
       
   111     run(net.start) ::: run(net.end)
       
   112   }
       
   113 
       
   114   override def beforeSave = List(n => n.parent(findParent(a)))
       
   115 
       
   116   override def afterSave = List(n => children(n.parent.obj) foreach { child =>
       
   117     child.parent(findParent(child)).save
       
   118   })
       
   119 
       
   120   override def afterDelete = List(n => children(Full(n)) foreach { child =>
       
   121     child.parent(findParent(child)).save
       
   122   })
       
   123 
       
   124   def findParent(net: ExampleNetwork): Box[ExampleNetwork] =
       
   125   findAll(inNet(net):_*) filter {!_.comparePrimaryKeys(net)} sort {
       
   126     (n1, n2) => (for {
       
   127       s1 <- n1.size
       
   128       s2 <- n2.size
       
   129   } yield s1 < s2) openOr true } firstOption
       
   130 
       
   131   import OprEnum._
       
   132   def inNet(inet: MappedInetAddress[ExampleNetwork]) = List(
       
   133     Cmp(start, <=, Full(inet.is), Empty, Empty),
       
   134     Cmp(end, >=, Full(inet.is), Empty, Empty)
       
   135   )
       
   136 
       
   137   def inNet(net: ExampleNetwork): List[QueryParam[ExampleNetwork]] =
       
   138     inNet(net.start) ::: inNet(net.end)
       
   139 
       
   140   def getByNet(s: String, e: String) = Box(findAll(By(start, s), By(end, e)))
       
   141 
       
   142   def children(n: Box[ExampleNetwork]) = findAll(By(parent, n))
       
   143 }
       
   144 
       
   145 class ExampleNetwork extends LongKeyedMapper[ExampleNetwork] with IdPK
       
   146   with InetAddressHelpers {
       
   147 
       
   148   override def getSingleton = ExampleNetwork
       
   149 
       
   150   object start extends MappedInetAddress(this)
       
   151 
       
   152   object end extends MappedInetAddress(this)
       
   153 
       
   154   object parent extends LongMappedMapper(this, ExampleNetwork) {
       
   155     override def dbIncludeInForm_? = false
       
   156   }
       
   157 
       
   158   def size: Box[BigInt] = for {
       
   159     s <- start.asInet
       
   160     e <- end.asInet
       
   161   } yield toBigInt(e) - toBigInt(s) + 1
       
   162 
       
   163   /**
       
   164    * Checks if this network is subnet of the <code>other</code>.
       
   165    * @return true if this is subnet of the other
       
   166    *         false if this is supernet
       
   167    *         empty not related at all
       
   168    *         failure if can't compare or network overlap
       
   169    */
       
   170   def isSubnetOf(other: ExampleNetwork): Box[Boolean] = {
       
   171     def inside(what: BigInt, from: BigInt, to: BigInt): Boolean =
       
   172       (from <= what) && (what <= to)
       
   173 
       
   174     (for {
       
   175       si <- start.asInet
       
   176       ei <- end.asInet
       
   177       osi <- other.start.asInet
       
   178       oei <- other.end.asInet
       
   179     } yield {
       
   180       val s = toBigInt(si)
       
   181       val e = toBigInt(ei)
       
   182       val os = toBigInt(osi)
       
   183       val oe = toBigInt(oei)
       
   184       val s_inside = inside(s, os, oe)
       
   185       val e_inside = inside(e, os, oe)
       
   186       val os_inside = inside(os, s, e)
       
   187       val oe_inside = inside(oe, s, e)
       
   188       (s_inside, e_inside, os_inside, oe_inside) match {
       
   189         case (true, true, _, _) => Full(true)
       
   190         case (_, _, true, true) => Full(false)
       
   191         case (false, false, false, false) => Empty
       
   192         case (_, _, _, _) => Failure("IP address overlap with: " + other)
       
   193       }
       
   194     }).openOr(Failure("Failed to obtain inet addresses: this: " + this +
       
   195       ", other: " + other))
       
   196   }
       
   197 }
       
   198 
       
   199 // vim: set ts=2 sw=2 et: