I've just discovered that return
s in following closure will return from function findPackage
def findPackage(name: String, suffix: Option[String] = None): Path = {
logger.debug("Looking for package {} with suffix {}", name, suffix)
val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
for (val path <- dirs) {
val matcher = packagePattern.matcher(path.getFileName.toString)
if (matcher.matches() && matcher.group(1).equals(name))
if (suffix.isDefined) {
if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
return path
} else
return path
}
throw new PackageNotFoundException(this, name, suffix)
}
logger.debug("Found package is {}", path)
path
}
Can I somehow do a local return please? Thank you.
Or you could get rid of your loop and replace it with what you're trying to do: "find"
def findPackage(name: String, suffix: Option[String] = None): Path = {
logger.debug("Looking for package {} with suffix {}", name, suffix)
def matching(path : Path) : Boolean = {
val matcher = packagePattern.matcher(path.getFileName.toString)
matcher.matches && matcher.group(1).equals(name) && (!suffix.isDefined || (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
}
val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
dirs find matching getOrElse {throw new PackageNotFoundException(this, name, suffix)}
}
logger.debug("Found package is {}", path)
path
}
I fully support James Iry's suggestion, but for the sake of demonstration:
def findPackage(name: String, suffix: Option[String] = None): Path = {
logger.debug("Looking for package {} with suffix {}", name, suffix)
val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
try {
for (val path <- dirs) {
val matcher = packagePattern.matcher(path.getFileName.toString)
if (matcher.matches() && matcher.group(1).equals(name))
if (suffix.isDefined) {
if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
return path
} else
return path
}
throw new PackageNotFoundException(this, name, suffix)
} catch { case e:scala.runtime.NonLocalReturnControl[Path] => e.value}
}
logger.debug("Found package is {}", path)
path
}
What changed?
I have added a try{}
block around the body of the anonymous function and then catch
expression at the end looking for scala.runtime.NonLocalReturnControl
exception, then I extract and pass on the return value.
Why it works?
Returning from a nested anonymous function raises scala.runtime.NonLocalReturnControl
exception which is caught by the host function or method.
Scala Language Spec, section 6.20 Return Expressions:
... Returning from a nested anonymous function is implemented by
throwing and catching a scala.runtime.NonLocalReturnException. Any
exception catches between the point of return and the enclosing
methods might see the exception. A key comparison makes sure that
these exceptions are only caught by the method instance which is
terminated by the return.
If the return expression is itself part of an anonymous function, it
is possible that the enclosing instance of f has already returned
before the return expression is executed. In that case, the thrown
scala.runtime.NonLocalReturnException will not be caught, and will
propagate up the call stack.
Yes, you can, by defining a local method:
def findPackage(name: String, suffix: Option[String] = None): Path = {
logger.debug("Looking for package {} with suffix {}", name, suffix)
def search(dirs: List[File]) = { // not sure what the type of dirs actually is
for (val path <- dirs) {
val matcher = packagePattern.matcher(path.getFileName.toString)
if (matcher.matches() && matcher.group(1).equals(name))
if (suffix.isDefined) {
if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
return path
} else
return path
}
throw new PackageNotFoundException(this, name, suffix)
}
val path: Path = using(Files.newDirectoryStream(appDir))(search _)
logger.debug("Found package is {}", path)
path
}
or by throwing some exception and catching it:
def findPackage(name: String, suffix: Option[String] = None): Path = {
logger.debug("Looking for package {} with suffix {}", name, suffix)
val path: Path = using(Files.newDirectoryStream(appDir)) {dirs =>
try {
for (val path <- dirs) {
val matcher = packagePattern.matcher(path.getFileName.toString)
if (matcher.matches() && matcher.group(1).equals(name))
if (suffix.isDefined) {
if (matcher.group(2) != null && matcher.group(2).equals(suffix.get))
throw new ReturnException(path)
} else
throw new ReturnException(path)
}
throw new PackageNotFoundException(this, name, suffix)
}
catch { case ReturnException(path) => path }
}
logger.debug("Found package is {}", path)
path
}
I had a similar trouble, and I solved it by propagating any ControlThrowable I might find, like it says here
def asJson: JsValue = {
val key = "classification.ws.endpoint"
configuration.getString(key).map{ url =>
try {
return WS.url(url).get().await.get.json
} catch {
case ce : scala.util.control.ControlThrowable => throw ce
case e => throw InvalidWebServiceResponseException("Error accessing '%s'".format(url), e)
}
}.getOrElse {
throw new MissingConfigurationException("No value found for '%s' configuration".format(key))
}
}
Note that in this case, just removing the "return" statement solves the problem...