All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.msgpack.template.builder.ScalaJavaReflectionPropertyFinder.scala Maven / Gradle / Ivy

The newest version!
package org.msgpack.template.builder

//
// MessagePack for Scala
//
// Copyright (C) 2009-2011 FURUHASHI Sadayuki
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

import org.msgpack.template.FieldOption
import java.lang.reflect.{Modifier, Field, Method}
import collection.immutable.ListMap
import org.msgpack.annotation.{NotNullable, Optional, Index, Ignore}
import java.lang.annotation.{Annotation => JAnnotation}

/**
 *
 * User: takeshita
 * Create: 11/10/13 11:53
 */

trait ScalaJavaReflectionPropertyFinder {

  self: AbstractTemplateBuilder =>

  type Property = (Method, Method, Field)
  type PropertySet = (String, Property)

  override def toFieldEntries(targetClass: Class[_], from: FieldOption): Array[FieldEntry] = {
    val props = findPropertyMethods(targetClass) filter (!hasAnnotation(_, classOf[Ignore]))
    val indexed = indexing(props)
    indexed.map(convertToScalaFieldEntry(_))
  }

  def setter_?(method: Method): Boolean = {
    val name = method.getName
    Modifier.isPublic(method.getModifiers) &&
      method.getReturnType.getName == "void" &&
      !name.startsWith("_") &&
      name.endsWith("_$eq") &&
      method.getParameterTypes.length == 1
  }

  def getter_?(method: Method): Boolean = {
    !method.getName.startsWith("_") &&
      Modifier.isPublic(method.getModifiers) &&
      method.getReturnType.getName != "void" &&
      method.getParameterTypes.length == 0

  }


  def findPropertyMethods(targetClass: Class[_]): Map[String, Property] = {
    var getters: Map[String, Method] = ListMap.empty
    var setters: Map[String, Method] = ListMap.empty

    def extractName(n: String) = {
      n.substring(0, n.length - 4)
    }

    //Find getters and setters
    for (m <- targetClass.getMethods) {
      if (setter_?(m)) {
        val valType = m.getParameterTypes()(0).getName
        setters += ((extractName(m.getName) + ":" + valType) -> m)
      } else if (getter_?(m)) {
        val valType = m.getReturnType.getName
        getters += ((m.getName + ":" + valType) -> m)
      }
    }

    var props: Map[String, Property] = ListMap.empty

    def sameType_?(getter: Method, setter: Method) = {
      getter.getReturnType == setter.getParameterTypes()(0)
    }
    // order of methods changes depends on call order, NOT declaration.

    def getterAndSetter(name: String): Option[(Method, Method)] = {
      if (getters.contains(name) && setters.contains(name)) {
        val getter = getters(name)
        val setter = setters(name)
        if (getter.getReturnType == setter.getParameterTypes()(0)) {
          Some(getter -> setter)
        } else {
          None
        }
      } else None
    }
    def recursiveFind(clazz: Class[_]): Unit = {
      if (clazz.getSuperclass != classOf[Object]) {
        recursiveFind(clazz.getSuperclass)
      }
      for (f <- clazz.getDeclaredFields) {
        val name = f.getName
        getterAndSetter(name + ":" + f.getType().getName()) match {
          case Some((g, s)) => props += (name ->(g, s, f))
          case None => {
            // if filed starts with "_" exists, it's also valid field for message pack.
            // This rule is to support custom getter and setter.
            if (name.startsWith("_ph_")) {
              val sname = name.substring(4)
              getterAndSetter(sname + ":" + f.getType().getName()) match {
                case Some((g, s)) => props += (sname ->(g, s, f))
                case None =>
              }
            }
          }
        }
      }
    }
    recursiveFind(targetClass)

    props
  }

  def indexing(props: Map[String, Property]): Array[PropertySet] = {
    val indexed = new Array[PropertySet](props.size)

    var notIndexed: List[PropertySet] = Nil

    for (s <- props) {
      val i = getAnnotation(s, classOf[Index])
      if (i == null) {
        notIndexed = notIndexed :+ s
      } else {
        val index = i.value
        if (indexed(index) != null) {
          throw new TemplateBuildException("duplicated index: " + index);
        } else {
          try {
            indexed(index) = s
          } catch {
            case e: Exception => {
              throw new TemplateBuildException("invalid index: %s index must be 0 <= x < %s".format(index, indexed.length));
            }
          }
        }
      }
    }
    for (i <- 0 until indexed.length) {
      if (indexed(i) == null) {
        indexed(i) = notIndexed.head
        notIndexed = notIndexed.drop(1)
      }
    }


    indexed
  }

  def convertToScalaFieldEntry(propInfo: PropertySet) = {
    val entry = new ScalaFieldEntry(propInfo._1,
      readFieldOption(propInfo, FieldOption.OPTIONAL),
      readValueType(propInfo),
      readGenericType(propInfo),
      propInfo._2._1,
      propInfo._2._2
    )

    entry
  }

  def readFieldOption(prop: PropertySet, implicitOption: FieldOption) = {
    if (hasAnnotation(prop, classOf[Optional])) {
      FieldOption.OPTIONAL
    } else if (hasAnnotation(prop, classOf[NotNullable])) {
      FieldOption.NOTNULLABLE
    } else if (hasAnnotation(prop, classOf[Ignore])) {
      FieldOption.IGNORE
    } else {
      if (readValueType(prop).isPrimitive) {
        FieldOption.NOTNULLABLE
      } else {
        FieldOption.OPTIONAL
      }
    }

  }

  def readValueType(prop: PropertySet) = {
    prop._2._1.getReturnType
  }

  def readGenericType(prop: PropertySet) = {
    prop._2._1.getGenericReturnType
  }


  def hasAnnotation[T <: JAnnotation](prop: PropertySet, classOfAnno: Class[T]): Boolean = {
    val getter = prop._2._1
    val setter = prop._2._2
    val field = prop._2._3
    getter.getAnnotation(classOfAnno) != null ||
      setter.getAnnotation(classOfAnno) != null || {
      if (field != null) field.getAnnotation(classOfAnno) != null
      else false
    }
  }

  def getAnnotation[T <: JAnnotation](prop: PropertySet, classOfAnno: Class[T]): T = {
    val getter = prop._2._1
    val setter = prop._2._2
    val field = prop._2._3



    val a = getter.getAnnotation(classOfAnno)
    if (a != null) {
      a
    } else {
      val b = setter.getAnnotation(classOfAnno)
      if (b != null) {
        b
      } else if (field != null) {
        field.getAnnotation(classOfAnno)
      } else {
        null.asInstanceOf[T]
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy