Skip to content

Commit

Permalink
initial ImmutableScaladinItem (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
Henri Kerola committed Aug 28, 2015
1 parent 7d8fabd commit bfd55ec
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 9 deletions.
60 changes: 60 additions & 0 deletions addon/src/main/scala/vaadin/scala/ImmutableScaladinItem.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package vaadin.scala

import scala.reflect.runtime._
import scala.reflect.runtime.universe._
import com.vaadin.data.util.{ PropertysetItem => VaadinPropertysetItem }
import scala.reflect.ClassTag

/**
*
* @author Henri Kerola / Vaadin
*/
object ImmutableScaladinItem {

def apply[T: TypeTag](bean: T): ImmutableScaladinItem[T] = {
new ImmutableScaladinItem(bean, ScaladinItem.getPropertyDescriptors(bean, true))
}
}

class ImmutableScaladinItem[T: TypeTag](var bean: T, propertyDescriptors: Iterable[PropertyDescriptor[T]])
extends PropertysetItem(new VaadinPropertysetItem) {

propertyDescriptors foreach { pd =>
addItemProperty(pd.name, pd.createProperty(bean))
}

def commit(): T = {
implicit val classTag = ClassTag[T](bean.getClass)

val newValues: Map[String, Any] = propertyDescriptors.map(pd => pd.name -> getProperty(pd.name).value.orNull).toMap

bean = copy(bean, newValues)

bean
}

private class Empty

private def copy[R: ClassTag](r: R, newValues: Map[String, Any]): R = {
val mirror = universe.runtimeMirror(r.getClass.getClassLoader)

val instanceMirror = mirror.reflect(r)
val decl = instanceMirror.symbol.asType.toType
val members = decl.members.map(method => transformMethod(method, newValues, instanceMirror)).filter {
case _: Empty => false
case _ => true
}.toArray.reverse
val copyMethod = decl.declaration(newTermName("copy")).asMethod
val copyMethodInstance = instanceMirror.reflectMethod(copyMethod)
copyMethodInstance(members: _*).asInstanceOf[R]
}

private def transformMethod(method: Symbol, newValues: Map[String, Any], instanceMirror: InstanceMirror): Any = {
val term = method.asTerm
if (term.isAccessor) {
if (newValues.contains(term.name.toString)) {
newValues.apply(term.name.toString)
} else instanceMirror.reflectField(term).get
} else new Empty
}
}
16 changes: 10 additions & 6 deletions addon/src/main/scala/vaadin/scala/ScaladinItem.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import scala.reflect.ClassTag

object ScaladinItem {

protected def getPropertyDescriptors[T: TypeTag](b: T): Iterable[ScaladinPropertyDescriptor[T]] = {
def getPropertyDescriptors[T: TypeTag](b: T, immutable: Boolean): Iterable[PropertyDescriptor[T]] = {
implicit val classTag = ClassTag[T](b.getClass)
lazy val rm = runtimeMirror(b.getClass.getClassLoader)
val getters = typeOf[T].members filter { m =>
Expand All @@ -31,16 +31,20 @@ object ScaladinItem {
}
val setterMirror = setterSymbol map { s => rm.reflect(b).reflectMethod(s.asMethod) }

new ScaladinPropertyDescriptor[T](m.name.decoded, clazz, getterMirror, setterMirror)
if (immutable) {
new ImmutableScaladinPropertyDescriptor[T](m.name.decoded, clazz, getterMirror.apply())
} else {
new ScaladinPropertyDescriptor[T](m.name.decoded, clazz, getterMirror, setterMirror)
}
}
}

def apply[T: TypeTag](bean: T): ScaladinItem[T] = {
new ScaladinItem(bean, getPropertyDescriptors(bean))
new ScaladinItem(bean, getPropertyDescriptors(bean, false))
}

def apply[T: TypeTag](bean: T, propertyIds: Iterable[String]): ScaladinItem[T] = {
val propertyDescriptorsMap = getPropertyDescriptors(bean) map { pd => (pd.name, pd) } toMap
val propertyDescriptorsMap = getPropertyDescriptors(bean, false) map { pd => (pd.name, pd) } toMap
val propertyDescriptors = propertyIds flatMap { propertyDescriptorsMap.get(_) }
new ScaladinItem(bean, propertyDescriptors)
}
Expand All @@ -49,10 +53,10 @@ object ScaladinItem {
/**
* @author Henri Kerola / Vaadin
*/
class ScaladinItem[T: TypeTag](bean: T, propertyDescriptors: Iterable[ScaladinPropertyDescriptor[T]])
class ScaladinItem[T: TypeTag](bean: T, propertyDescriptors: Iterable[PropertyDescriptor[T]])
extends PropertysetItem(new VaadinPropertysetItem) {

propertyDescriptors foreach { pd =>
addItemProperty(pd.name, pd.createProperty(bean))
}
}
}
10 changes: 10 additions & 0 deletions addon/src/main/scala/vaadin/scala/ScaladinProperty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,13 @@ class ScaladinProperty[T](

override def readOnly_=(ro: Boolean) { _readOnly = ro }
}

class ImmutableScaladinProperty[T](
val propertyType: Class[_ <: T],
val instance: T,
val getterMirror: MethodMirror) extends Property[T] {

val p = new com.vaadin.data.Property[T] with DelegatingPropertyMixin[T]
p.wrapper = this

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ package mixins {
}
}

trait PropertyDescriptor[T] {

def name: String

def createProperty[T](bean: T): Property[_]
}

/**
*
* @author Henri Kerola / Vaadin
Expand All @@ -26,7 +33,7 @@ class ScaladinPropertyDescriptor[T](
val propertyType: Class[_],
val getterMirror: MethodMirror,
val setterMirror: Option[MethodMirror])
extends Wrapper {
extends PropertyDescriptor[T] with Wrapper {

val p = new VaadinPropertyDescriptor[T] with DelegatingVaadinPropertyDescriptor[T]
p.wrapper = this
Expand All @@ -35,3 +42,11 @@ class ScaladinPropertyDescriptor[T](
new ScaladinProperty[T](propertyType.asInstanceOf[Class[_ <: T]], bean, getterMirror, setterMirror)
}
}

class ImmutableScaladinPropertyDescriptor[T](val name: String, val propertyType: Class[_], val initialValue: Any) extends PropertyDescriptor[T] with Wrapper {

val p = new VaadinPropertyDescriptor[T] with DelegatingVaadinPropertyDescriptor[T]
p.wrapper = this

override def createProperty[T](bean: T): Property[_] = new ObjectProperty(initialValue, propertyType.asInstanceOf[Class[Any]])
}
3 changes: 1 addition & 2 deletions addon/src/test/scala/vaadin/scala/tests/ConverterTests.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package vaadin.scala.tests

import vaadin.scala._
import vaadin.scala.converter.{DateToSqlDateConverter, DefaultConverterFactory, DateToLongConverter, Converter}
import vaadin.scala.converter.{ DateToSqlDateConverter, DefaultConverterFactory, DateToLongConverter, Converter }
import org.scalatest.FunSuite
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
Expand Down

0 comments on commit bfd55ec

Please sign in to comment.