Reflection on a Scala case class - reflection

I'm trying to write a trait (in Scala 2.8) that can be mixed in to a case class, allowing its fields to be inspected at runtime, for a particular debugging purpose. I want to get them back in the order that they were declared in the source file, and I'd like to omit any other fields inside the case class. For example:
trait CaseClassReflector extends Product {
def getFields: List[(String, Any)] = {
var fieldValueToName: Map[Any, String] = Map()
for (field <- getClass.getDeclaredFields) {
field.setAccessible(true)
fieldValueToName += (field.get(this) -> field.getName)
}
productIterator.toList map { value => fieldValueToName(value) -> value }
}
}
case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
val other: Int = 42
}
scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)
scala> val fields = c.getFields
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))
The above implementation is clearly flawed because it guesses the relationship between a field's position in the Product and its name by equality of the value on those field, so that the following, say, will not work:
Colour(0, 0, 0).getFields
Is there any way this can be implemented?

Look in trunk and you'll find this. Listen to the comment, this is not supported: but since I also needed those names...
/** private[scala] so nobody gets the idea this is a supported interface.
*/
private[scala] def caseParamNames(path: String): Option[List[String]] = {
val (outer, inner) = (path indexOf '$') match {
case -1 => (path, "")
case x => (path take x, path drop (x + 1))
}
for {
clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
ssig <- ScalaSigParser.parse(clazz)
}
yield {
val f: PartialFunction[Symbol, List[String]] =
if (inner.isEmpty) {
case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
}
else {
case x: ClassSymbol if x.name == inner =>
val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
xs.toList map (_.name dropRight 1)
}
(ssig.symbols partialMap f).flatten toList
}
}

Here's a short and working version, based on the example above
trait CaseClassReflector extends Product {
def getFields = getClass.getDeclaredFields.map(field => {
field setAccessible true
field.getName -> field.get(this)
})
}

In every example I've seen the fields are in reverse order: the last item in the getFields array is the first one listed in the case class. If you use case classes "nicely", then you should just be able to map productElement(n) onto getDeclaredFields()( getDeclaredFields.length-n-1).
But this is rather dangerous, as I don't know of anything in the spec that insists that it must be that way, and if you override a val in the case class, it won't even appear in getDeclaredFields (it'll appear in the fields of that superclass).
You might change your code to assume things are this way, but check that the getter method with that name and the productIterator return the same value and throw an exception if they don't (which means that you don't actually know what corresponds to what).

You can also use the ProductCompletion from the interpreter package to get to attribute names and values of case classes:
import tools.nsc.interpreter.ProductCompletion
// get attribute names
new ProductCompletion(Colour(1, 2, 3)).caseNames
// returns: List(red, green, blue)
// get attribute values
new ProductCompletion(Colour(1, 2, 3)).caseFields
Edit: hints by roland and virtualeyes
It is necessary to include the scalap library which is part of the scala-lang collection.
Thanks for your hints, roland and virtualeyes.

Related

How to add new item in specific index?

I new in kotlin , i want to update an item in lists.
I use this code:
var index: Int
for (record in recordList)
if (record.id == updatedHeader?.id) {
index = recordList.indexOf(record)
recordList.add(index, updatedHeader)
}
but it cant do this, because of ConcurrentModificationException
Assuming that recordList is a MutableList and val (so, you'd like to modify the records in place), you can use forEachIndexed to find the records you care about and replace them.
This did not cause a ConcurrentModificationException:
recordList.forEachIndexed { index, record ->
if(record.id == updatedHeader?.id) recordList[index] = updatedHeader
}
On the other hand, if you redefine recordList as a non-mutable list, and a var, you could rewrite the entire list using map:
recordList = recordList.map { if(it.id == updatedHeader?.id) updatedHeader else it }
Of course, you could call .toMutableList() on the end of that if you wanted to turn your List into a MutableList.
If there's a single record with the given id in the list, you can find its index and add the header at that index:
val index = recordList.indexOfFirst { it.id == updatedHeader.id }
if (index >= 0)
recordList.add(index, updatedHeader)
If there are multiple records with the given id and you want to prepend header before each of them, you can use get listIterator and use its methods to modify the list during the iteration without getting ConcurrentModificationException:
val iterator = recordList.listIterator()
for (record in iterator) {
if (record.id == updatedHeader.id) {
iterator.previous() // move to the position before the record
iterator.add(updatedHeader) // prepend header
iterator.next() // move next, back to the record
}
}

Create nested map from key in groovy

I'm relatively new to groovy and am using it in the context of a gradle build. So please don't be harsh if there is an easy out-of-the-box solution for this.
Basically I'm trying to accomplish the reverse of Return Nested Key in Groovy. That is, I have some keys read from the System.properties map for example user.home and corresponding values like C:\User\dpr. Now I want to create a map that reflects this structure to use it in a groovy.text.SimpleTemplateEngine as bindings:
[user : [home : 'C:\Users\dpr']]
The keys may define an arbitrary deep hierarchy. For example java.vm.specification.vendor=Oracle Corporation should become:
[java : [vm : [spec : [vendor : 'Oracle Corporation']]]]
Additionally there are properties with the same parents such as user.name=dpr and user.country=US:
[
user: [
name: 'dpr',
country: 'US'
]
]
Edit: While ConfigSlurper is really nice, it is somewhat too defensive with creating the nested maps as it stops nesting at the minimum depth of a certain key.
I currently ended up using this
def bindings = [:]
System.properties.sort().each {
def map = bindings
def split = it.key.split("\\.")
for (int i = 0; i < split.length; i++) {
def part = split[i];
// There is already a property value with the same parent
if (!(map instanceof Map)) {
println "Skipping property ${it.key}"
break;
}
if (!map.containsKey(part)) {
map[part] = [:]
}
if (i == split.length - 1) {
map[part] = it.value
} else {
map = map[part]
}
}
map = it.value
}
With this solution the properties file.encoding.pkg, java.vendor.url and java.vendor.url.bug are discarded, which is not nice but something I can cope with.
However the above code is not very groovyish.
You can use a ConfigSlurper :
def conf = new ConfigSlurper().parse(System.properties)
println conf.java.specification.version

Groovy: Transforming a String into a Multimap

So I have a String which looks a little something like this:
text = "foo/bar;baz/qux"
My end goal is to split this String into a Multimap like this:
["level1" : ["foo", "baz"], "level2" : ["bar", "qux"]]
I also added Multimap-support to LinkedHashMap's metaClass:
LinkedHashMap.metaClass.multiPut << { key, value ->
delegate[key] = delegate[key] ?: []; delegate[key] += value
}
The String needs to be split at semi-colon and then again at forwardslash. Currently I'm populating my Multimap within a nested for-loop but obviously there's a Groovier way of doing this. Thus I was wondering what my options are?
I'm thinking something along the lines of:
def final myMap = text.split(';')
.collectEntries { it.split('/')
.eachWithIndex { entry, index -> ["level${index + 1}" : entry] }}
You can use withDefault on your returned Map to get rid of the ternary:
def text = "foo/bar;baz/qux;foo/bar/woo"
def result = text.split(';')*.split('/').inject([:].withDefault {[]}) { map, value ->
value.eachWithIndex { element, idx ->
map["level${idx+1}"] << element
}
map
}
assert result == [level1:['foo', 'baz', 'foo'], level2:['bar', 'qux', 'bar'], level3:['woo']]
If you don't want duplicates in your results, then you can use a Set in your withDefault (then convert back to a List afterwards):
def text = "foo/bar;baz/qux;foo/bar/woo"
def result = text.split(';')*.split('/').inject([:].withDefault {[] as Set}) { map, value ->
value.eachWithIndex { element, idx ->
map["level${idx+1}"] << element
}
map
}.collectEntries { key, value -> [key, value as List] }
assert result == [level1:['foo', 'baz'], level2:['bar', 'qux'], level3:['woo']]
My take on it, I wouldn't consider it very clever but I find it much easier to read:
def myMap = [:]
text.split(';').eachWithIndex{ entry, index ->
myMap << ["level${index + 1}": entry.split('/')]
}
If you are using Groovy 2.4.0 or above, you could use the withIndex() method which has been added to java.lang.Iterable:
def myMap = text.split(';').withIndex().collect{ entry, index ->
["level${index + 1}": entry.split('/')]
}

remove duplicates from collection

I want to get a list of all Locales that have a different language, where the ISO3 code is used to identify the language of a Locale. I thought the following should work
class ISO3LangComparator implements Comparator<Locale> {
int compare(Locale locale1, Locale locale2) {
locale1.ISO3Language <=> locale2.ISO3Language
}
}
def allLocales = Locale.getAvailableLocales().toList()
def uniqueLocales = allLocales.unique {new ISO3LangComparator()}
// Test how many locales there are with iso3 code 'ara'
def arabicLocaleCount = uniqueLocales.findAll {it.ISO3Language == 'ara'}.size()
// This assertion fails
assert arabicLocaleCount <= 1
You are using the wrong syntax: you are using Collection.unique(Closure closure):
allLocales.unique {new ISO3LangComparator()}
You should use Collection.unique(Comparator comparator)
allLocales.unique (new ISO3LangComparator())
So simply use () instead of {}, and your problem is solved.
what Adam said.
or...
allLocales.unique{it.ISO3Language}
and you forget about the comparator

Dynamic properties in Scala

Does Scala support something like dynamic properties? Example:
val dog = new Dynamic // Dynamic does not define 'name' nor 'speak'.
dog.name = "Rex" // New property.
dog.speak = { "woof" } // New method.
val cat = new Dynamic
cat.name = "Fluffy"
cat.speak = { "meow" }
val rock = new Dynamic
rock.name = "Topaz"
// rock doesn't speak.
def test(val animal: Any) = {
animal.name + " is telling " + animal.speak()
}
test(dog) // "Rex is telling woof"
test(cat) // "Fluffy is telling meow"
test(rock) // "Topaz is telling null"
What is the closest thing from it we can get in Scala? If there's something like "addProperty" which allows using the added property like an ordinary field, it would be sufficient.
I'm not interested in structural type declarations ("type safe duck typing"). What I really need is to add new properties and methods at runtime, so that the object can be used by a method/code that expects the added elements to exist.
Scala 2.9 will have a specially handled Dynamic trait that may be what you are looking for.
This blog has a big about it: http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html
I would guess that in the invokeDynamic method you will need to check for "name_=", "speak_=", "name" and "speak", and you could store values in a private map.
I can not think of a reason to really need to add/create methods/properties dynamically at run-time unless dynamic identifiers are also allowed -and/or- a magical binding to an external dynamic source (JRuby or JSON are two good examples).
Otherwise the example posted can be implemented entirely using the existing static typing in Scala via "anonymous" types and structural typing. Anyway, not saying that "dynamic" wouldn't be convenient (and as 0__ pointed out, is coming -- feel free to "go edge" ;-).
Consider:
val dog = new {
val name = "Rex"
def speak = { "woof" }
}
val cat = new {
val name = "Fluffy"
def speak = { "meow" }
}
// Rock not shown here -- because it doesn't speak it won't compile
// with the following unless it stubs in. In both cases it's an error:
// the issue is when/where the error occurs.
def test(animal: { val name: String; def speak: String }) = {
animal.name + " is telling " + animal.speak
}
// However, we can take in the more general type { val name: String } and try to
// invoke the possibly non-existent property, albeit in a hackish sort of way.
// Unfortunately pattern matching does not work with structural types AFAIK :(
val rock = new {
val name = "Topaz"
}
def test2(animal: { val name: String }) = {
animal.name + " is telling " + (try {
animal.asInstanceOf[{ def speak: String }).speak
} catch { case _ => "{very silently}" })
}
test(dog)
test(cat)
// test(rock) -- no! will not compile (a good thing)
test2(dog)
test2(cat)
test2(rock)
However, this method can quickly get cumbersome (to "add" a new attribute one would need to create a new type and copy over the current data into it) and is partially exploiting the simplicity of the example code. That is, it's not practically possible to create true "open" objects this way; in the case for "open" data a Map of sorts is likely a better/feasible approach in the current Scala (2.8) implementation.
Happy coding.
First off, as #pst pointed out, your example can be entirely implemented using static typing, it doesn't require dynamic typing.
Secondly, if you want to program in a dynamically typed language, program in a dynamically typed language.
That being said, you can actually do something like that in Scala. Here is a simplistic example:
class Dict[V](args: (String, V)*) extends Dynamic {
import scala.collection.mutable.Map
private val backingStore = Map[String, V](args:_*)
def typed[T] = throw new UnsupportedOperationException()
def applyDynamic(name: String)(args: Any*) = {
val k = if (name.endsWith("_=")) name.dropRight(2) else name
if (name.endsWith("_=")) backingStore(k) = args.first.asInstanceOf[V]
backingStore.get(k)
}
override def toString() = "Dict(" + backingStore.mkString(", ") + ")"
}
object Dict {
def apply[V](args: (String, V)*) = new Dict(args:_*)
}
val t1 = Dict[Any]()
t1.bar_=("quux")
val t2 = new Dict("foo" -> "bar", "baz" -> "quux")
val t3 = Dict("foo" -> "bar", "baz" -> "quux")
t1.bar // => Some(quux)
t2.baz // => Some(quux)
t3.baz // => Some(quux)
As you can see, you were pretty close, actually. Your main mistake was that Dynamic is a trait, not a class, so you can't instantiate it, you have to mix it in. And you obviously have to actually define what you want it to do, i.e. implement typed and applyDynamic.
If you want your example to work, there are a couple of complications. In particular, you need something like a type-safe heterogenous map as a backing store. Also, there are some syntactic considerations. For example, foo.bar = baz is only translated into foo.bar_=(baz) if foo.bar_= exists, which it doesn't, because foo is a Dynamic object.

Resources