Created
December 30, 2013 17:15
-
-
Save b-studios/8184976 to your computer and use it in GitHub Desktop.
Strongly Typed Version of Sizeable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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