Skip to content

Instantly share code, notes, and snippets.

@b-studios
Created December 30, 2013 17:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save b-studios/8184976 to your computer and use it in GitHub Desktop.
Save b-studios/8184976 to your computer and use it in GitHub Desktop.
Strongly Typed Version of Sizeable
package vaadin.scala
package server
import com.vaadin.{ server => orig }
import scala.language.implicitConversions
import scala.PartialFunction.condOpt
import apigen.annotations.accessors
trait Sizeable {
object Units extends Enumeration {
import orig.Sizeable.Unit._
type Units = Value
trait Extractor { self: Units.Units =>
def unapply[U <: Units.Units](m: Measure[U]): Option[Measure[this.type]] =
if (m.unit eq this) Some(m.asInstanceOf[Measure[this.type]]) else None
}
// Relative Units
object em extends Val(EM.ordinal, "em") with Extractor
object rem extends Val(REM.ordinal, "rem") with Extractor
object ex extends Val(EX.ordinal, "ex") with Extractor
// Absolute Units (pixel)
object px extends Val(PIXELS.ordinal, "px") with Extractor
object pt extends Val(POINTS.ordinal, "pt") with Extractor
object pc extends Val(PICAS.ordinal, "pc") with Extractor
// Absolute Units (physical)
object mm extends Val(MM.ordinal, "mm") with Extractor
object cm extends Val(CM.ordinal, "cm") with Extractor
object in extends Val(INCH.ordinal, "in") with Extractor
// Percentage Units
object pct extends Val(PERCENTAGE.ordinal, "%") with Extractor
// trigger registration, since objects are evaluated lazily
{em; rem; ex; px; pt; pc; mm; cm; in; pct}
}
implicit def toJavaEnum[U <: Units.Units](u: U): orig.Sizeable.Unit =
orig.Sizeable.Unit.getUnitFromSymbol(u.toString)
/**
* Since complexity is high enough due to typeclasses for units we restrict
* Measure to be double instead of numeric
*/
case class Measure[U <: Units.Units](value: Double, unit: U) extends Ordered[Measure[U]] {
override def toString = value.toString + unit.toString
def +[S <: Units.Units](m: Measure[S])(implicit conv: CanConvert[S, U]) = Measure(value + conv(m.value), unit)
def -[S <: Units.Units](m: Measure[S])(implicit conv: CanConvert[S, U]) = Measure(value - conv(m.value), unit)
def *[N](n: N)(implicit num: Numeric[N]) = Measure(value * num.toDouble(n), unit)
def /[N](n: N)(implicit num: Numeric[N]) = Measure(value / num.toDouble(n), unit)
def map(f: Double => Double): Measure[U] = Measure(f(value), unit)
def compare(that: Measure[U]) = this.value compare that.value
def to[To <: Units.Units](u: To)(implicit conv: CanConvert[U, To]) = Measure(conv(value), u)
}
object Measure {
def apply[U <: Units.Units](value: Float, unit: U) = new Measure[U](value, unit)
def apply[N, U <: Units.Units](value: N, unit: U)(implicit num: Numeric[N]) =
new Measure[U](num.toDouble(value), unit)
// Factory method taking java values as arguments
def apply(value: java.lang.Float, unit: orig.Sizeable.Unit) =
new Measure[Units.Units](value.toDouble, Units(unit.ordinal))
//def unapply[U <: Units.Units](m: Measure[U]): Option[(Double, U)] = Some((m.value, m.unit))
// TODO this does not belong in class `Measure`
def optional(value: java.lang.Float, unit: orig.Sizeable.Unit) = condOpt(value) {
case _ if value != null && value >= 0 => Measure(value.toDouble, Units(unit.ordinal))
}
def optional(value: Double, unit: orig.Sizeable.Unit) = condOpt(value) {
case _ if value >= 0 => Measure(value, Units(unit.ordinal))
}
}
/**
* Implicit conversion to allow unit suffixes
*/
implicit class NumericWrapper[N: Numeric](self: N) {
def px = Measure(self, Units.px)
def pt = Measure(self, Units.pt)
def pc = Measure(self, Units.pc)
def em = Measure(self, Units.em)
def rem = Measure(self, Units.rem)
def ex = Measure(self, Units.ex)
def mm = Measure(self, Units.mm)
def cm = Measure(self, Units.cm)
def in = Measure(self, Units.in)
def pct = Measure(self, Units.pct)
}
implicit class SizeableWrapper(@accessors self: orig.Sizeable) {
def height: Option[Measure[Units.Units]] = Measure.optional(self.getHeight, self.getHeightUnits)
def height_=(m: Measure[Units.Units]) = self.setHeight(m.toString)
def height_=(m: Option[Measure[Units.Units]]) = self.setHeight(m map(_.toString) getOrElse null)
def width: Option[Measure[Units.Units]] = Measure.optional(self.getWidth, self.getWidthUnits)
def width_=(m: Measure[Units.Units]) = self.setWidth(m.toString)
def width_=(m: Option[Measure[Units.Units]]) = self.setWidth(m map(_.toString) getOrElse null)
def sizeFull = self.setSizeFull
def sizeUndefined = self.setSizeUndefined
}
/**
* Typeclasses to allow conversion between different units.
*/
// Base of AbsoluteUnit is Points. `apply` always converts to base unit
sealed case class AbsoluteUnit[U](convert: Double => Double) {
def apply(value: Double): Double = convert(value)
}
object AbsoluteUnit {
implicit val ptIsAbsolute = AbsoluteUnit[Units.pt.type](x => x)
implicit val pxIsAbsolute = AbsoluteUnit[Units.px.type](_ * 0.75)
implicit val pcIsAbsolute = AbsoluteUnit[Units.pc.type](_ * 12)
implicit val inIsAbsolute = AbsoluteUnit[Units.in.type](_ * 72)
implicit val cmIsAbsolute = AbsoluteUnit[Units.cm.type](_ * 72 / 2.54)
implicit val mmIsAbsolute = AbsoluteUnit[Units.mm.type](_ * 72 / 2.54 / 100)
// Apply a polymorphic function to a yet unkown `Unit`
// ... TODO continue
//def apply()
}
private case class CanConvert[From, To](impl: Double => Double) {
def apply(value: Double): Double = impl(value)
}
private trait LowPriorityCanConvert {
implicit def absToPt[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.pt.type] =
CanConvert(from => conv(from))
implicit def absToPx[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.px.type] =
CanConvert(from => conv(from) / 0.75)
implicit def absToPc[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.pc.type] =
CanConvert(from => conv(from) / 12)
implicit def absToIn[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.in.type] =
CanConvert(from => conv(from) / 72)
implicit def absToCm[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.cm.type] =
CanConvert(from => conv(from) / 72 * 2.54)
implicit def absToMm[From](implicit conv: AbsoluteUnit[From]): CanConvert[From, Units.mm.type] =
CanConvert(from => conv(from) / 72 * 2.54 * 100)
}
private object CanConvert extends LowPriorityCanConvert {
implicit def id[U] = CanConvert[U, U](n => n)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment