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

com.mchange.feedletter.style.AllUntemplates.scala Maven / Gradle / Ivy

package com.mchange.feedletter.style

import com.mchange.feedletter.*

import untemplate.Untemplate

import scala.collection.mutable
import scala.annotation.tailrec

// We define this mutable(!) registry, rather than using IndexedUntemplates directly,
// because we may in future wish to define "binary" distributions that nevertheless
// permit the definition of new untemplates.
//
// Those distributions could index the untemplates under whatever name users like,
// but would need to add them to this registry at app startup.

object AllUntemplates:
  //MT: protected by this' lock
  private val _untemplates = mutable.Map.from( IndexedUntemplates )

  //MT: protected by this' lock
  private var _cache : LazyCache = new LazyCache

  class LazyCache:
    lazy val ComposeUntemplates             = AllUntemplates.all.filter( (k,v) => isCompose(v) )             map ( (k,v) => (k, v.asInstanceOf[Untemplate[ComposeInfo.Universal,Nothing]]) )
    lazy val ComposeUntemplatesSingle       = ComposeUntemplates.filter( (k,v) => canComposeSingle(v) )      map ( (k,v) => (k, v.asInstanceOf[Untemplate[ComposeInfo.Single,Nothing]])    )
    lazy val ComposeUntemplatesMultiple     = ComposeUntemplates.filter( (k,v) => canComposeMultiple(v) )    map ( (k,v) => (k, v.asInstanceOf[Untemplate[ComposeInfo.Multiple,Nothing]])  )
    lazy val ConfirmUntemplates             = AllUntemplates.all.filter( (k,v) => isConfirm(v) )             map ( (k,v) => (k, v.asInstanceOf[Untemplate[ConfirmInfo,Nothing]]) )
    lazy val StatusChangeUntemplates        = AllUntemplates.all.filter( (k,v) => isStatusChange(v) )        map ( (k,v) => (k, v.asInstanceOf[Untemplate[StatusChangeInfo,Nothing]]) )
    lazy val RemovalNotificationUntemplates = AllUntemplates.all.filter( (k,v) => isRemovalNotification(v) ) map ( (k,v) => (k, v.asInstanceOf[Untemplate[RemovalNotificationInfo,Nothing]]) )

  def cache : LazyCache = this.synchronized( _cache )

  def compose             = cache.ComposeUntemplates
  def composeSingle       = cache.ComposeUntemplatesSingle
  def composeMultiple     = cache.ComposeUntemplatesMultiple
  def confirm             = cache.ConfirmUntemplates
  def statusChange        = cache.StatusChangeUntemplates
  def removalNotification = cache.RemovalNotificationUntemplates

  def add( more : IterableOnce[(String,Untemplate.AnyUntemplate)] ) : Unit = this.synchronized:
    _untemplates.addAll(more)
    _cache = new LazyCache

  def all : Map[String,Untemplate.AnyUntemplate] = this.synchronized:
    Map.from( _untemplates )

  def findComposeUntemplateSingle( fqn : String ) : untemplate.Untemplate[ComposeInfo.Single,Nothing] =
    findComposeUntemplate(fqn, single=true).asInstanceOf[untemplate.Untemplate[ComposeInfo.Single,Nothing]]

  def findComposeUntemplateMultiple( fqn : String ) : untemplate.Untemplate[ComposeInfo.Multiple,Nothing] =
    findComposeUntemplate(fqn, single=false).asInstanceOf[untemplate.Untemplate[ComposeInfo.Multiple,Nothing]]

  def findConfirmUntemplate( fqn : String ) : untemplate.Untemplate[ConfirmInfo,Nothing] =
    findXxxUntemplate( this.confirm, fqn, "confirm")

  def findStatusChangeUntemplate( fqn : String ) : untemplate.Untemplate[StatusChangeInfo,Nothing] =
    findXxxUntemplate( this.statusChange, fqn, "status-change")

  def findRemovalNotificationUntemplate( fqn : String ) : untemplate.Untemplate[RemovalNotificationInfo,Nothing] =
    findXxxUntemplate( this.removalNotification, fqn, "removal notification")

  private def findComposeUntemplate( fqn : String, single : Boolean ) : Untemplate.AnyUntemplate =
    val snapshot = this.cache
    val (expectedDesc, otherDesc, expectedLoc, otherLoc) =
      if single then
        ("single","multiple",snapshot.ComposeUntemplatesSingle,snapshot.ComposeUntemplatesMultiple)
      else
        ("multiple","single",snapshot.ComposeUntemplatesMultiple,snapshot.ComposeUntemplatesSingle)
    expectedLoc.get( fqn ).getOrElse:
      val isCrosswise = otherLoc.contains(fqn)
      if isCrosswise then
        throw new UnsuitableUntemplate( s"Compose untemplate '$fqn' is a ${otherDesc}-item-accepting untemplate, not available in contexts that render a ${expectedDesc} item." )
      else
        this.all.get( fqn ).fold( throw new UntemplateNotFound( s"Compose untemplate '$fqn' does not appear to be defined." ) ): ut =>
          throw new UnsuitableUntemplate( s"'$fqn' appears not to be a compose untemplate. (input type: ${untemplateInputType(ut)})" )

  private def findXxxUntemplate[T]( uts : Map[String,Untemplate[T,Nothing]], fqn : String, utypeLowerCased : String ) : Untemplate[T,Nothing] =
    uts.get( fqn ).getOrElse:
      this.all.get( fqn ).fold( throw new UntemplateNotFound( s"${utypeLowerCased.capitalize} untemplate '$fqn' does not appear to be defined." ) ): ut =>
        throw new UnsuitableUntemplate( s"'$fqn' appears not to be a ${utypeLowerCased} untemplate. (input type: ${untemplateInputType(ut)})" )

  private def canComposeSingle( candidate : Untemplate.AnyUntemplate ) : Boolean = isComposeSingle( candidate ) || isComposeUniversal( candidate )

  private def canComposeMultiple( candidate : Untemplate.AnyUntemplate ) : Boolean = isComposeMultiple( candidate ) || isComposeUniversal( candidate )

  private def suffixes(fqn : String) : Set[String] =
    val elements = fqn.split('.').toList

    @tailrec
    def build( from : List[String], accum : Set[List[String]] ) : Set[List[String]] =
      from match
        case head :: tail => build( tail, accum + from )
        case Nil          => accum
    build( elements, Set.empty ).map( _.mkString(".") )
  end suffixes

  private def isByInputTypeFqn( candidate : Untemplate.AnyUntemplate, fqn : String, suffixes : Set[String] ) : Boolean =
    candidate.UntemplateInputTypeCanonical match
      case Some( `fqn` ) => true
      case Some( _ )     => false
      case None          => suffixes.contains( candidate.UntemplateInputTypeDeclared )

  private def visibleType( clz : Class[?] ) = clz.getName.replace('$','.')

  private val FqnComposeInfoSingle       = visibleType(classOf[ComposeInfo.Single])
  private val FqnComposeInfoMultiple     = visibleType(classOf[ComposeInfo.Multiple])
  private val FqnComposeInfoUniversal    = visibleType(classOf[ComposeInfo.Universal])
  private val FqnConfirmInfo             = visibleType(classOf[ConfirmInfo])
  private val FqnStatusChangeInfo        = visibleType(classOf[StatusChangeInfo])
  private val FqnRemovalNotificationInfo = visibleType(classOf[RemovalNotificationInfo])
  
  private val SuffixesComposeInfoSingle       = suffixes( FqnComposeInfoSingle )
  private val SuffixesComposeInfoMultiple     = suffixes( FqnComposeInfoMultiple )
  private val SuffixesComposeInfoUniversal    = suffixes( FqnComposeInfoUniversal )
  private val SuffixesConfirmInfo             = suffixes( FqnConfirmInfo )
  private val SuffixesStatusChangeInfo        = suffixes( FqnStatusChangeInfo )
  private val SuffixesRemovalNotificationInfo = suffixes( FqnRemovalNotificationInfo )

  private def isComposeSingle( candidate : Untemplate.AnyUntemplate )       : Boolean = isByInputTypeFqn( candidate, FqnComposeInfoSingle, SuffixesComposeInfoSingle )
  private def isComposeMultiple( candidate : Untemplate.AnyUntemplate )     : Boolean = isByInputTypeFqn( candidate, FqnComposeInfoMultiple, SuffixesComposeInfoMultiple )
  private def isComposeUniversal( candidate : Untemplate.AnyUntemplate )    : Boolean = isByInputTypeFqn( candidate, FqnComposeInfoUniversal, SuffixesComposeInfoUniversal )
  private def isConfirm( candidate : Untemplate.AnyUntemplate )             : Boolean = isByInputTypeFqn( candidate, FqnConfirmInfo, SuffixesConfirmInfo )
  private def isStatusChange( candidate : Untemplate.AnyUntemplate )        : Boolean = isByInputTypeFqn( candidate, FqnStatusChangeInfo, SuffixesStatusChangeInfo )
  private def isRemovalNotification( candidate : Untemplate.AnyUntemplate ) : Boolean = isByInputTypeFqn( candidate, FqnRemovalNotificationInfo, SuffixesRemovalNotificationInfo )

  private def isCompose( candidate : Untemplate.AnyUntemplate ) = isComposeSingle( candidate ) || isComposeMultiple( candidate ) || isComposeUniversal( candidate )





© 2015 - 2025 Weber Informatics LLC | Privacy Policy