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

org.beangle.serializer.text.marshal.MarshallingContext.scala Maven / Gradle / Ivy

The newest version!
/*
 * Beangle, Agile Development Scaffold and Toolkits.
 *
 * Copyright © 2005, The Beangle Software.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package org.beangle.serializer.text.marshal

import org.beangle.commons.collection.{IdentityMap, IdentitySet}
import org.beangle.commons.lang.reflect.BeanInfos
import org.beangle.serializer.text.StreamSerializer
import org.beangle.serializer.text.io.{Path, StreamWriter}

import scala.collection.mutable
import scala.language.existentials

class MarshallingContext(val serializer: StreamSerializer, val writer: StreamWriter, val registry: MarshallerRegistry, val params: Map[String, Any]) {

  val references = new IdentityMap[AnyRef, Id]

  val currents = new IdentitySet[AnyRef]

  var elementType: Class[_] = _

  val propertyMap = new collection.mutable.HashMap[Class[_], List[String]]

  init()

  def init(): Unit = {
    val properties = params.getOrElse("properties", List.empty).asInstanceOf[Seq[(Class[_], List[String])]]
    if (serializer.hierarchical) {
      propertyMap ++= properties
    } else {
      properties foreach { tuple =>
        val getters = BeanInfos.get(tuple._1).readables
        val filted = new collection.mutable.ListBuffer[String]
        tuple._2 foreach { p =>
          getters.get(p) match {
            case Some(getter) => if (!isCollectionType(getter.clazz)) filted += p
            case None => throw new RuntimeException("cannot find property $p of class ${tuple2._1.getName}")
          }
        }
        propertyMap.put(tuple._1, filted.toList)
      }
    }
    params.get("elementType") match {
      case Some(cls) =>
        elementType = cls.asInstanceOf[Class[_]]
        //search bean type (clazz maybe interface,so cache it first,ready for concret class)
        getProperties(elementType)
      case None =>
    }
    if (properties.nonEmpty && null == elementType) elementType = properties.head._1
  }

  def getProperties(clazz: Class[_]): List[String] = {
    val result = propertyMap.get(clazz) match {
      case Some(p) => p
      case None =>
        if (registry.lookup(clazz).targetType == Type.Object) {
          val p = searchProperties(clazz)
          if (null == p) {
            val readables = BeanInfos.get(clazz).readables
            val bp =
              if (readables.contains("id") && null != elementType && elementType != clazz) {
                List("id")
              } else {
                if (serializer.hierarchical) {
                  readables.filter(!_._2.isTransient).keySet.toList
                } else {
                  readables.filter { g => !g._2.isTransient && !isCollectionType(g._2.clazz) }.keySet.toList
                }
              }
            propertyMap.put(clazz, bp)
            bp
          } else {
            p
          }
        } else {
          List()
        }
    }
    if (elementType == null && !isCollectionType(clazz)) elementType = clazz
    result
  }

  private def searchProperties(targetType: Class[_]): List[String] = {
    val interfaces = new mutable.LinkedHashSet[Class[_]]
    val classQueue = new mutable.Queue[Class[_]]
    classQueue += targetType
    while (classQueue.nonEmpty) {
      val currentClass = classQueue.dequeue()
      val props = propertyMap.get(currentClass).orNull
      if (props != null) return props
      val superClass = currentClass.getSuperclass
      if (superClass != null && superClass != classOf[AnyRef]) classQueue += superClass
      for (interfaceType <- currentClass.getInterfaces) addInterfaces(interfaceType, interfaces)
    }
    val iter = interfaces.iterator
    while (iter.hasNext) {
      val interfaceType = iter.next()
      val props = propertyMap.get(interfaceType).orNull
      if (props != null) return props
    }
    null
  }

  private def addInterfaces(interfaceType: Class[_], interfaces: mutable.Set[Class[_]]): Unit = {
    interfaces.add(interfaceType)
    for (inheritedInterface <- interfaceType.getInterfaces) addInterfaces(inheritedInterface, interfaces)
  }

  def marshal(item: Any, marshaller: Marshaller[Any] = null): Unit = {
    if (marshaller == null) {
      serializer.marshal(item, registry.lookup(item.getClass.asInstanceOf[Class[Any]]), this)
    } else {
      serializer.marshal(item, marshaller, this)
    }
  }

  def marshalNull(item: Object, property: String): Unit = {
    serializer.marshalNull(item, property, this)
  }

  def lookupReference(item: Object): Id = {
    references.get(item)
  }

  private def isCollectionType(clazz: Class[_]): Boolean = {
    clazz.isArray || classOf[java.util.Collection[_]].isAssignableFrom(clazz) || classOf[Iterable[_]].isAssignableFrom(clazz)
  }

}

class Id(val key: AnyRef, val path: Path) {
  override def toString: String = {
    path.toString()
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy