Skip to content

Commit

Permalink
Merge pull request #1949 from lift/msf_issue_1912
Browse files Browse the repository at this point in the history
Minimize surprise NPEs in Loc before SiteMap assignment
  • Loading branch information
farmdawgnation authored May 15, 2018
2 parents de91b3a + 52b2e91 commit 033f78b
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 23 deletions.
57 changes: 35 additions & 22 deletions web/webkit/src/main/scala/net/liftweb/sitemap/Loc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ trait Loc[T] {
* By default, this lazy val looks for the MenuCssClass LocParam and
* uses it.
*/
protected lazy val cacheCssClassForMenuItem: Box[() => String] =
protected lazy val cacheCssClassForMenuItem: Box[() => String] =
allParams.flatMap {
case a: Loc.MenuCssClass => List(a)
case _ => Nil
Expand All @@ -78,7 +78,7 @@ trait Loc[T] {


def defaultValue: Box[T]

/**
* The value of the Loc based on params (either Loc.Value or Loc.CalcValue)
*/
Expand All @@ -90,7 +90,7 @@ trait Loc[T] {
v.asInstanceOf[T]
}
}

private lazy val calcValue: Box[() => Box[T]] = {
params.collectFirst {
case Loc.CalcValue(f: Function0[_]) =>
Expand All @@ -102,13 +102,13 @@ trait Loc[T] {
* Calculate the Query parameters
*/
def queryParameters(what: Box[T]): List[(String, String)] =
addlQueryParams.flatMap(_()) :::
addlQueryParams.flatMap(_()) :::
calcQueryParams.flatMap(_(what))

protected def appendQueryParams(what: T)(nodeSeq: NodeSeq): NodeSeq =
protected def appendQueryParams(what: T)(nodeSeq: NodeSeq): NodeSeq =
Text(appendQueryParameters(nodeSeq.text, Full(what)))

protected def appendQueryParameters(in: String, what: Box[T]) =
protected def appendQueryParameters(in: String, what: Box[T]) =
Helpers.appendQueryParameters(in, queryParameters(what))

private lazy val addlQueryParams: List[() => List[(String, String)]] =
Expand All @@ -127,16 +127,22 @@ trait Loc[T] {

def params: List[Loc.LocParam[T]]

def allParams: List[Loc.AnyLocParam] =
(params.asInstanceOf[List[Loc.AnyLocParam]]) :::
parentParams :::
siteMap.globalParams
def allParams: List[Loc.AnyLocParam] = {
if (_menu == null) {
// Params are impossible without a menu
Nil
} else {
(params.asInstanceOf[List[Loc.AnyLocParam]]) :::
parentParams :::
siteMap.globalParams
}
}

private def parentParams: List[Loc.AnyLocParam] =
private def parentParams: List[Loc.AnyLocParam] =
_menu match {
case null => Nil
case menu => menu._parent match {
case Full(parentMenu: Menu) =>
case Full(parentMenu: Menu) =>
if (!params.collect{case i: Loc.UseParentParams => true}.isEmpty) {
parentMenu.loc.allParams.asInstanceOf[List[Loc.LocParam[Any]]]
} else {
Expand All @@ -154,9 +160,12 @@ trait Loc[T] {

def hideIfNoKids_? = placeHolder_? || _hideIfNoKids_?

def siteMap: SiteMap = _menu.siteMap
// This implementation is less than ideal, but changing the type would
// have been a breaking API change. This at least avoids this method
// throwing an NPE
def siteMap: SiteMap = Option(_menu).map(_.siteMap).getOrElse(null)

def createDefaultLink: Option[NodeSeq] =
def createDefaultLink: Option[NodeSeq] =
currentValue.flatMap(p => link.createLink(p)).toOption.
map(ns => Text(appendQueryParameters(ns.text, currentValue)))

Expand Down Expand Up @@ -204,7 +213,7 @@ trait Loc[T] {
* Is the Loc marked as Stateless (this will force rendering of
* the page into stateless mode)
*/
def stateless_? : Boolean =
def stateless_? : Boolean =
if (Props.devMode) (calcStateless() || reqCalcStateless())
else (_frozenStateless || reqCalcStateless())

Expand Down Expand Up @@ -401,7 +410,13 @@ trait Loc[T] {
)
}

def breadCrumbs: List[Loc[_]] = _menu.breadCrumbs ::: List(this)
def breadCrumbs: List[Loc[_]] = {
if (_menu != null) {
_menu.breadCrumbs ::: List(this)
} else {
List(this)
}
}

def buildKidMenuItems(kids: Seq[Menu]): List[MenuItem] = {
kids.toList.flatMap(_.loc.buildItem(Nil, false, false)) ::: supplementalKidMenuItems
Expand Down Expand Up @@ -463,7 +478,7 @@ trait Loc[T] {

trait ConvertableLoc[T] {
self: Loc[T] =>

/**
* Converts the String to T that can then be sent to
* the Loc in createLink
Expand Down Expand Up @@ -513,7 +528,6 @@ object Loc {
init()
}


/**
* Algebraic data type for parameters that modify handling of a Loc
* in a SiteMap
Expand Down Expand Up @@ -630,13 +644,13 @@ object Loc {
* the groups can be specified and recalled at the top level
*/
case class LocGroup(group: String*) extends AnyLocParam

/**
* Calculate the value for the Loc. This is useful for parameterized
* menus. It allows you to calculate the value of the Loc.
*/
case class CalcValue[T](func: () => Box[T]) extends LocParam[T]

/**
* The value of Loc
*/
Expand Down Expand Up @@ -685,7 +699,7 @@ object Loc {
*/
class Snippet(val name: String, _func: => NodeSeq => NodeSeq) extends ValueSnippets[Any] with AnyLocParam {
/**
* The NodeSeq => NodeSeq function
* The NodeSeq => NodeSeq function
*/
def func: NodeSeq => NodeSeq = _func

Expand Down Expand Up @@ -959,4 +973,3 @@ case class MenuItem(text: NodeSeq, uri: NodeSeq, kids: Seq[MenuItem],
else this :: kids.toList.flatMap(_.breadCrumbs)
}
}

11 changes: 10 additions & 1 deletion web/webkit/src/test/scala/net/liftweb/sitemap/LocSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,15 @@ object LocSpec extends Specification {
}
}
}

"not throw Exceptions on param methods before SiteMap assignment" in {
val testMenu = Menu.param[Param]("Test", "Test", s => Empty, p => "bacon") / "foo" / "bar" >> Loc.MatchWithoutCurrentValue
val testLoc = testMenu.toLoc

testLoc.allParams must not(throwA[Exception])
testLoc.currentValue must not(throwA[Exception])
testLoc.siteMap must not(throwA[Exception])
testLoc.breadCrumbs must not(throwA[Exception])
}
}
}

0 comments on commit 033f78b

Please sign in to comment.