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

com.fasterxml.jackson.module.scala.introspect.ScalaPropertiesCollector.scala Maven / Gradle / Ivy

The newest version!
package com.fasterxml.jackson.module.scala.introspect

import com.fasterxml.jackson.databind.{PropertyNamingStrategy, PropertyName, AnnotationIntrospector, JavaType}
import com.fasterxml.jackson.databind.cfg.MapperConfig
import com.fasterxml.jackson.databind.introspect._

import com.fasterxml.jackson.module.scala.util.Implicits._

import scala.collection.JavaConverters._
import scala.collection.mutable

import java.util
import com.fasterxml.jackson.databind.util.BeanUtil

class ScalaPropertiesCollector(config: MapperConfig[_],
                               forSerialization: Boolean,
                               `type`: JavaType,
                               classDef: AnnotatedClass,
                               mutatorPrefix: String)
  extends POJOPropertiesCollector(config, forSerialization, `type`, classDef, mutatorPrefix) {

  private [this] val _descriptor = BeanIntrospector(`type`.getRawClass)

  private [this] val _propertyIntrospector =
    Option(_annotationIntrospector).map(ai => new ScalaPropertyIntrospector(ai))

  private [this] val _getPropertyName: (PropertyDescriptor) => Option[PropertyName] =
    (_propertyIntrospector, _forSerialization) match {
      case (None, _) => (m) => None
      case (Some(pi), true) => (pi.findNameForSerialization _)
      case (Some(pi), false) => (pi.findNameForDeserialization _)
    }

  private [this] val _hasIgnoreMarker: (AnnotatedMember) => Boolean =
    Option(_annotationIntrospector).map(ai => (m: AnnotatedMember) => ai.hasIgnoreMarker(m)).getOrElse((x: AnnotatedMember) => false)

  private [this] lazy val anyGetters: mutable.Buffer[AnnotatedMember] = {
    if (_anyGetters == null) {
      _anyGetters = new util.LinkedList[AnnotatedMember]()
    }
    _anyGetters.asScala
  }

  private [this] lazy val anySetters: mutable.Buffer[AnnotatedMethod] = {
    if (_anySetters == null) {
      _anySetters = new util.LinkedList[AnnotatedMethod]()
    }
    _anySetters.asScala
  }

  private [this] lazy val jsonValueGetters: mutable.Buffer[AnnotatedMethod] = {
    if (_jsonValueGetters == null) {
      _jsonValueGetters = new util.LinkedList[AnnotatedMethod]()
    }
    _jsonValueGetters.asScala
  }

  private [this] lazy val creatorProperties: mutable.Buffer[POJOPropertyBuilder] = {
    if (_creatorProperties == null) {
      _creatorProperties = new util.LinkedList[POJOPropertyBuilder]()
    }
    _creatorProperties.asScala
  }


  override def _addCreators() {
    val ctors = classDef.getConstructors.asScala
    val ignoredCtors = ctors.filter { ctor =>
      Stream.range(0, ctor.getParameterCount).view.map(ctor.getParameter).exists(_hasIgnoreMarker)
    }

    _descriptor.properties.view.filter(_.param.isDefined).foreach { pd =>
      val name = pd.name
      val pn = _getPropertyName(pd)
      val explName = pn.optMap(_.getSimpleName).map(_.orIfEmpty(name))

      //call to pd.param.get is safe due to filter above
      val cp =  pd.param.get
      ctors.find(_.getAnnotated == cp.constructor).foreach { annotatedConstructor =>
        val param = annotatedConstructor.getParameter(cp.index)
        if (_hasIgnoreMarker(param)) {
          _properties.remove(name)
        }
        else if (!ignoredCtors.contains(annotatedConstructor)) {
          _addFieldCtor(name, annotatedConstructor.getParameter(cp.index), explName)
        }
      }
    }
  }

  override def _addFields() {
    val fields = classDef.fields().asScala
    _descriptor.properties.view.filter(_.field.isDefined).foreach { pd =>
      val name = pd.name
      val explName = _getPropertyName(pd).optMap(_.getSimpleName).map(_.orIfEmpty(name))

      //add the field
      //call to pd.field.get is safe because of the above filter
      fields.find(_.getMember == pd.field.get).foreach { af =>
        _addField(name, explName, af)
      }
    }
  }

  private def _addField(implName: String, explName: Option[String], field: AnnotatedField) {
    val visible = explName.isDefined || _visibilityChecker.isFieldVisible(field)
    val ignored = _hasIgnoreMarker(field)
    val prop = _property(implName)
    prop.addField(field, explName.orNull, visible, ignored)
  }

  private def _addFieldCtor(implName: String, param: AnnotatedParameter, explName: Option[String]) {
    val prop = _property(implName)

    // GH-83: Having both a setter and a creator causes some issues where Jackson assumes it can't
    // do certain things it should be able to, such as deserializing to an instance. For now, if the
    // property already has a setter, then don't add the creator.
    // GH-83: Update: Hiding the creator breaks valid constructon scenarios. Trying to work on fixing
    // the underlying databind issue.

    // GH-90: Don't add the creatorProp if it was ignored; Jackson doesn't expect this on creator
    // properties and won't filter it.
    if (/* !prop.hasSetter && */ !prop.anyIgnorals()) {
      prop.addCtor(param, explName.orNull, true, _hasIgnoreMarker(param))
      creatorProperties += prop
    }
  }

  private def _findSerializationName(am: AnnotatedMethod) = {
    Option(_annotationIntrospector)
      .optMap { _.findNameForSerialization(am) }
      .map { _.getSimpleName }
      .orElse { Option(BeanUtil.okNameForGetter(am)) }
  }

  private def _findDeserializationName(am: AnnotatedMethod) = {
    Option(_annotationIntrospector)
      .optMap { _.findNameForDeserialization(am) }
      .map { _.getSimpleName }
      .orElse { Option(BeanUtil.okNameForMutator(am, mutatorPrefix)) }
  }

  private def _isPropertyHandled(m: AnnotatedMethod): Boolean = {
    val name = if (forSerialization) _findSerializationName(m) else _findDeserializationName(m)
    val matched = name.flatMap { n => _properties.asScala.get(n) } exists { _.anyVisible() }

    matched || _properties.values().asScala.exists {
      case p => m.equals(p.getGetter) || m.equals(p.getSetter) || m.equals(p.getMutator)
    }
  }

  override def _addMethods() {
    val ai = Option(_annotationIntrospector)
    val methods = classDef.memberMethods().asScala.toSet
    val seen = mutable.Set.empty[AnnotatedMethod]

    _descriptor.properties.view.filter(_.getter.isDefined).foreach { pd =>
      val name = pd.name
      val explName = _getPropertyName(pd).optMap(_.getSimpleName).map(_.orIfEmpty(name))
      //call to pd.getter.get is safe because of the above filter
      methods.find(_.getMember == pd.getter.get).foreach { am =>
        _addGetterMethod(name, explName, am)
        seen += am
      }
      pd.setter.foreach { setter =>
        methods.find(_.getMember == setter).foreach { am =>
          _addSetterMethod(name, explName, am)
          seen += am
        }
      }
    }

    // Any method we haven't dealt with yet, handle as the base class would
    // This should filter out any @BeanProperty generated methods for fields
    // we've already detected. TODO: Maybe fold those into PropertyDescriptor?
    // This additionally picks up any 'stray' methods that were not gathered up by the BeanIntrospector
    // which may have annotations on them which make them suitable accessing/setting properties
    methods &~ seen filterNot _isPropertyHandled foreach { m =>
      m.getParameterCount match {
        case 0 => _addGetterMethod(m, ai.orNull)
        case 1 => _addSetterMethod(m, ai.orNull)
        case 2 if ai.exists(_.hasAnySetterAnnotation(m)) => anySetters += m
        case _ => // do nothing
      }
    }
  }



  /*
   * This is essentially the base class function, except that we don't check
   * BeanUtils to see if the name is permissible as a setter method (Scala
   * has different rules there and ScalaBeans has already vetted the method).
   */
  private def _addGetterMethod(implName: String, explName: Option[String], m: AnnotatedMethod) {
    val ai = _annotationIntrospector
    if (ai != null) {
      if (ai.hasAnyGetterAnnotation(m)) {
        anyGetters += m
        return
      }
      if (ai.hasAsValueAnnotation(m)) {
        jsonValueGetters += m
        return
      }
    }

    val prop = _property(implName)
    val visible = explName.isDefined ||  _visibilityChecker.isGetterVisible(m)
    val ignore = _hasIgnoreMarker(m)
    prop.addGetter(m, explName.orNull, visible, ignore)
  }

  private def _addSetterMethod(implName: String, explName: Option[String], m: AnnotatedMethod) {
    val prop = _property(implName)
    val visible = explName.isDefined || _visibilityChecker.isSetterVisible(m)
    val ignore = _hasIgnoreMarker(m)
    prop.addSetter(m, explName.orNull, visible, ignore)
  }

  /**
   * Similar to the base implementation, but checks for annotations in a different
   * order due to Scala's annotation application rules.
   * @param naming The PropertyNamingStrategy to query for names
   */
  override protected def _renameUsing(naming: PropertyNamingStrategy) {
    val props = _properties.asScala.values.toArray
    _properties.clear()

    for (prop <- props) {
      var name = prop.getName
      if (prop.hasConstructorParameter) {
        name = naming.nameForConstructorParameter(_config, prop.getConstructorParameter, name)
      } else if (prop.hasField) {
        name = naming.nameForField(_config, prop.getField, name)
      } else if (_forSerialization && prop.hasGetter) {
        name = naming.nameForGetterMethod(_config, prop.getGetter, name)
      } else if (!_forSerialization && prop.hasSetter) {
        name = naming.nameForSetterMethod(_config, prop.getSetter, name)
      }
      val newProp = if (name != prop.getName) prop.withName(name) else prop
      val old = _properties.get(name)
      if (old == null) {
        _properties.put(name, newProp)
      }
      else {
        old.addAll(newProp)
      }
      if (newProp != prop) {
        val idx = if (_creatorProperties != null) creatorProperties.indexOf(prop) else -1
        if (idx != -1) {
          creatorProperties(idx) = newProp
        }
      }
    }
  }


  class ScalaPropertyIntrospector(ai: AnnotationIntrospector) {

    require(ai != null, "Argument ai must be non-null")

    private [this] val _ctors = classDef.getConstructors.asScala
    private [this] val _fields = classDef.fields().asScala
    private [this] val _methods = classDef.memberMethods().asScala

    def findNameForSerialization(prop: PropertyDescriptor): Option[PropertyName] = {
      prop match {
        case PropertyDescriptor(name, optParam, Some(f), _, _) => {
          val annotatedParam = optParam.flatMap { cp =>
            _ctors.find(_.getAnnotated == cp.constructor).map(_.getParameter(cp.index))
          }
          val annotatedField = _fields.find(_.getMember == f)
          val paramName = annotatedParam.optMap(ai.findNameForDeserialization(_))
          val fieldName = annotatedField.optMap(ai.findNameForSerialization(_))
          fieldName orElse paramName
        }

        case PropertyDescriptor(name, _, None, Some(g), _) =>
          _methods.find(_.getMember == g).optMap(ai.findNameForSerialization(_))

        case _ => None
      }
    }

    def findNameForDeserialization(prop: PropertyDescriptor): Option[PropertyName] = {
      prop match {
        case PropertyDescriptor(name, optParam, Some(f), _, _) => {
          val annotatedParam = optParam.flatMap { cp =>
            _ctors.find(_.getAnnotated == cp.constructor).map(_.getParameter(cp.index))
          }
          val annotatedField = _fields.find(_.getMember == f)
          val paramName = annotatedParam.optMap(ai.findNameForDeserialization(_))
          val fieldName = annotatedField.optMap(ai.findNameForDeserialization(_))
          fieldName orElse paramName
        }

        case PropertyDescriptor(name, _, None, _, Some(s)) =>
          _methods.find(_.getMember == s).optMap(ai.findNameForDeserialization(_))

        case _ => None
      }
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy