Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dotty update #3 #62

Merged
merged 18 commits into from
Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ scalacOptions in (Compile, console) --= Seq(
val scala211 = "2.11.12"
val scala212 = "2.12.10"
val scala213 = "2.13.1"
val dotty = "0.20.0-RC1"
val dotty = "0.21.0-RC1"

val versionsBase = Seq(scala211, scala212, scala213)
val versionsJVM = versionsBase //:+ dotty
val versionsJVM = versionsBase :+ dotty
val versionsJS = versionsBase
val versionsNative = Seq(scala211)

Expand Down Expand Up @@ -127,6 +127,24 @@ lazy val sconfig = crossProject(JVMPlatform, NativePlatform, JSPlatform)
"-g",
"-Xlint:unchecked"
),
// Dotty is missing serializable support
// Can Filter based on Test name but not method name with "erializ"
// so exclude the Tests with the 19 that cannot pass
// 530 - 19 = 511 Only 346 get run this way so we lose coverage
Test / testOptions := {
if (isDotty.value)
Seq(
Tests.Exclude(
Seq(
"org.ekrich.config.impl.ValidationTest",
"org.ekrich.config.impl.PublicApiTest",
"org.ekrich.config.impl.ConfigValueTest",
"org.ekrich.config.impl.ConfigTest"
)
)
)
else Seq(Tests.Exclude(Seq()))
},
// because we test some global state such as singleton caches,
// we have to run tests in serial.
Test / parallelExecution := false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import java.time.Duration
import scala.reflect.ClassTag
import scala.jdk.CollectionConverters._
import scala.util.control.Breaks._
import scala.util.Try
import org.ekrich.config.Config
import org.ekrich.config.ConfigObject
import org.ekrich.config.ConfigList
Expand Down Expand Up @@ -327,17 +328,17 @@ object ConfigBeanImpl {
else null

private def hasAtLeastOneBeanProperty(clazz: Class[_]): Boolean = {
var beanInfo: BeanInfo = null
try beanInfo = Introspector.getBeanInfo(clazz)
catch {
case e: IntrospectionException =>
return false
}
for (beanProp <- beanInfo.getPropertyDescriptors) {
if (beanProp.getReadMethod != null && beanProp.getWriteMethod != null)
return true
val beanInfoOpt = Try(Introspector.getBeanInfo(clazz)).toOption
beanInfoOpt match {
case None => false
case Some(beanInfo) =>
beanInfo
.getPropertyDescriptors()
.exists(
beanProp =>
beanProp.getReadMethod != null && beanProp.getWriteMethod != null
)
}
false
}

private def isOptionalProperty(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import java.{lang => jl}
import java.{util => ju}
import ju.Collections

import scala.jdk.CollectionConverters._

import org.ekrich.config.ConfigException
import org.ekrich.config.ConfigMergeable
import org.ekrich.config.ConfigObject
Expand Down Expand Up @@ -64,19 +66,13 @@ object AbstractConfigValue {
def hasDescendantInList(
list: ju.List[AbstractConfigValue],
descendant: AbstractConfigValue
): Boolean = {
import scala.jdk.CollectionConverters._
for (v <- list.asScala) {
if (v == descendant) return true
}
// now the expensive traversal
for (v <- list.asScala) {
if (v.isInstanceOf[Container] && v
.asInstanceOf[Container]
.hasDescendant(descendant)) return true
}
false
}
): Boolean =
list.asScala.exists(_ == descendant) ||
// now the expensive traversal
list.asScala.exists { v =>
v.isInstanceOf[Container] &&
v.asInstanceOf[Container].hasDescendant(descendant)
}

def indent(
sb: jl.StringBuilder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,8 @@ object BadMap {
983, 991, 997, 1009, /* now we start skipping some, this is arbitrary */
2053, 3079, 4057, 7103, 10949, 16069, 32609, 65867, 104729)

private def nextPrime(i: Int): Int = {
for (p <- primes) {
if (p > i) return p
}
/* oh well */
primes(primes.length - 1)
}
private def nextPrime(i: Int): Int =
primes.find(p => p > i).getOrElse(primes(primes.length - 1))
}

final class BadMap[K, V] private (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ package org.ekrich.config.impl

import java.{lang => jl}
import java.{util => ju}

import scala.annotation.tailrec
import scala.jdk.CollectionConverters._
import scala.util.control.Breaks._

import org.ekrich.config.ConfigException
import org.ekrich.config.ConfigList
import org.ekrich.config.ConfigMergeable
Expand Down Expand Up @@ -165,6 +167,9 @@ final class ConfigDelayedMergeObject(
override def keySet = throw ConfigDelayedMergeObject.notResolved
override def size = throw ConfigDelayedMergeObject.notResolved
override def values = throw ConfigDelayedMergeObject.notResolved

// exercised in ValidationTest.validationFailedSerializable
// and ConfigTest.test01Serializable
override def attemptPeekWithPartialResolve(
key: String
): AbstractConfigValue = {
Expand All @@ -179,77 +184,89 @@ final class ConfigDelayedMergeObject(
// spirit.
// we'll be able to return a key if we have a value that ignores
// fallbacks, prior to any unmergeable values.
for (layer <- stack.asScala) {
breakable {
if (layer.isInstanceOf[AbstractConfigObject]) {
val objectLayer =
layer.asInstanceOf[AbstractConfigObject]
val v =
objectLayer.attemptPeekWithPartialResolve(key)
if (v != null) {
if (v.ignoresFallbacks) {
// we know we won't need to merge anything in to this value
return v
} else {
// we can't return this value because we know there are
// unmergeable values later in the stack that may
// contain values that need to be merged with this
// value. we'll throw the exception when we get to those
// unmergeable values, so continue here.
break // continue
}
} else if (layer.isInstanceOf[Unmergeable]) {
// an unmergeable object (which would be another
// ConfigDelayedMergeObject) can't know that a key is
// missing, so it can't return null; it can only return a
// value or throw NotPossibleToResolve
throw new ConfigException.BugOrBroken(
"should not be reached: unmergeable object returned null value"
@tailrec
def loop(
layers: List[AbstractConfigValue]
): Either[ConfigException, AbstractConfigValue] =
layers match {
case Nil =>
// If we get here, then we never found anything unresolved which means
// the ConfigDelayedMergeObject should not have existed. some
// invariant was violated.
Left(
new ConfigException.BugOrBroken(
"Delayed merge stack does not contain any unmergeable values"
)
} else {
// a non-unmergeable AbstractConfigObject that returned null
// for the key in question is not relevant, we can keep
// looking for a value.
break // continue
}
} else if (layer.isInstanceOf[Unmergeable]) {
throw new ConfigException.NotResolved(
s"Key '$key' is not available at '${origin.description}' because value at '${layer.origin.description}'" +
s" has not been resolved and may turn out to contain or hide '$key'." +
" Be sure to Config#resolve() before using a config object."
)
} else if (layer.resolveStatus eq ResolveStatus.UNRESOLVED) {
// if the layer is not an object, and not a substitution or merge,
// then it's something that's unresolved because it _contains_
// an unresolved object... i.e. it's an array
if (!layer.isInstanceOf[ConfigList]) {
throw new ConfigException.BugOrBroken(
"Expecting a list here, not " + layer
)
}
// all later objects will be hidden so we can say we won't find
// the key
return null
} else {
// non-object, but resolved, like an integer or something.
// has no children so the one we're after won't be in it.
// we would only have this in the stack in case something
// else "looks back" to it due to a cycle.
// anyway at this point we know we can't find the key anymore.
if (!layer.ignoresFallbacks) {
throw new ConfigException.BugOrBroken(
"resolved non-object should ignore fallbacks"
)
case head :: tl =>
head match {
case layer: AbstractConfigObject => {
layer.attemptPeekWithPartialResolve(key) match {
case v if v != null =>
if (v.ignoresFallbacks) {
// we know we won't need to merge anything in to this value
Right(v)
} else {
// we can't return this value because we know there are
// unmergeable values later in the stack that may
// contain values that need to be merged with this
// value. we'll throw the exception when we get to those
// unmergeable values, so continue here.
loop(tl)
}
case _: Unmergeable =>
// an unmergeable object (which would be another
// ConfigDelayedMergeObject) can't know that a key is
// missing, so it can't return null; it can only return a
// value or throw NotPossibleToResolve
throw new ConfigException.BugOrBroken(
"should not be reached: unmergeable object returned null value"
)
case _ =>
// a non-unmergeable AbstractConfigObject that returned null
// for the key in question is not relevant, we can keep
// looking for a value.
loop(tl)
}
}
case _: Unmergeable =>
throw new ConfigException.NotResolved(
s"Key '$key' is not available at '${origin.description}' because value at '${head.origin.description}'" +
s" has not been resolved and may turn out to contain or hide '$key'." +
" Be sure to Config#resolve() before using a config object."
)
case layer if (layer.resolveStatus eq ResolveStatus.UNRESOLVED) =>
// if the layer is not an object, and not a substitution or merge,
// then it's something that's unresolved because it _contains_
// an unresolved object... i.e. it's an array
if (!layer.isInstanceOf[ConfigList]) {
throw new ConfigException.BugOrBroken(
"Expecting a list here, not " + layer
)
}
// all later objects will be hidden so we can say we won't find
// the key
Right(null) // can I get null from this??
case _ =>
// non-object, but resolved, like an integer or something.
// has no children so the one we're after won't be in it.
// we would only have this in the stack in case something
// else "looks back" to it due to a cycle.
// anyway at this point we know we can't find the key anymore.
if (!head.ignoresFallbacks) {
throw new ConfigException.BugOrBroken(
"resolved non-object should ignore fallbacks"
)
}
Right(null)
}
return null
}
}
// run the logic
loop(stack.asScala.toList) match {
case Right(res) =>
res
case Left(ex) =>
throw ex
}
// If we get here, then we never found anything unresolved which means
// the ConfigDelayedMergeObject should not have existed. some
// invariant was violated.
throw new ConfigException.BugOrBroken(
"Delayed merge stack does not contain any unmergeable values"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class ConfigNodeField(_children: ju.Collection[AbstractConfigNode])
}
tokens
}

def replaceValue(newValue: AbstractConfigNodeValue): ConfigNodeField = {
val childrenCopy =
new ju.ArrayList[AbstractConfigNode](children)
Expand All @@ -31,6 +32,7 @@ final class ConfigNodeField(_children: ju.Collection[AbstractConfigNode])
}
throw new ConfigException.BugOrBroken("Field node doesn't have a value")
}

def value: AbstractConfigNodeValue = {
var i = 0
while (i < children.size) {
Expand All @@ -40,6 +42,7 @@ final class ConfigNodeField(_children: ju.Collection[AbstractConfigNode])
}
throw new ConfigException.BugOrBroken("Field node doesn't have a value")
}

def path: ConfigNodePath = {
var i = 0
while (i < children.size) {
Expand All @@ -49,16 +52,16 @@ final class ConfigNodeField(_children: ju.Collection[AbstractConfigNode])
}
throw new ConfigException.BugOrBroken("Field node doesn't have a path")
}
private[impl] def separator: Token = {
for (child <- children.asScala) {
if (child.isInstanceOf[ConfigNodeSingleToken]) {
val t = child.asInstanceOf[ConfigNodeSingleToken].token
if ((t eq Tokens.PLUS_EQUALS) || (t eq Tokens.COLON) || (t eq Tokens.EQUALS))
return t

private[impl] def separator: Token =
children.asScala.iterator
.filter(_.isInstanceOf[ConfigNodeSingleToken])
.map(_.asInstanceOf[ConfigNodeSingleToken].token)
.find { t =>
(t eq Tokens.PLUS_EQUALS) || (t eq Tokens.COLON) || (t eq Tokens.EQUALS)
}
}
null
}
.getOrElse(null)

private[impl] def comments: ju.List[String] = {
val comments = new ju.ArrayList[String]
for (child <- children.asScala) {
Expand Down
Loading