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

com.spotify.scio.IsJava.scala Maven / Gradle / Ivy

/*
 * Copyright 2019 Spotify AB.
 *
 * 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.
 */

package com.spotify.scio

import scala.reflect.macros._

/** Proof that a type is implemented in Java */
sealed trait IsJavaBean[T]

object IsJavaBean {
  implicit def isJavaBean[T]: IsJavaBean[T] = macro IsJavaBean.isJavaBeanImpl[T]

  def apply[T](implicit i: IsJavaBean[T]): IsJavaBean[T] = i

  private def checkGetterAndSetters(c: blackbox.Context)(t: c.universe.Type): Unit = {
    val getters =
      t.decls.collect {
        case s if s.name.toString.startsWith("get") =>
          (s.name.toString.drop(3), s.asMethod.info.asInstanceOf[c.universe.MethodType])
      }

    val setters =
      t.decls.collect {
        case s if s.name.toString.startsWith("set") =>
          (s.name.toString.drop(3), s.asMethod.info.asInstanceOf[c.universe.MethodType])
      }.toMap

    if (getters.isEmpty) {
      val mess = s"""Class $t is not a Java bean since it does not have any getter"""
      c.abort(c.enclosingPosition, mess)
    }

    getters.foreach { case (name, info) =>
      val setter =
        setters
          .get(name)
          .getOrElse {
            val mess =
              s"""JavaBean contained a getter for field $name""" +
                """ but did not contain a matching setter."""
            c.abort(c.enclosingPosition, mess)
          }

      val resType = info.resultType
      val paramType = setter.params.head.asTerm.info

      if (resType != paramType) {
        val mess =
          s"""JavaBean contained setter for field $name that had a mismatching type.
                |  found:    $paramType
                |  expected: $resType""".stripMargin
        c.abort(c.enclosingPosition, mess)
      }
    }
  }

  def isJavaBeanImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
    import c.universe._
    val wtt = weakTypeOf[T]
    val sym = wtt.typeSymbol
    if (sym.isJava && sym.isClass) {
      checkGetterAndSetters(c)(wtt)
      q"null: _root_.com.spotify.scio.IsJavaBean[$wtt]"
    } else {
      c.abort(
        c.enclosingPosition,
        s"$wtt is not a Java class. (isJava: ${sym.isJava}, isClass: ${sym.isClass})"
      )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy