1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package interpreter
import java.net.URL
import java.lang.reflect
import java.util.concurrent.ConcurrentHashMap
import scala.concurrent.DelayedLazyVal
import scala.util.NameTransformer.{ decode, encode }
import PackageCompletion._
/** Completion among all known packages. It examines the jars in a
* separate thread so as not to slow down startup. If it arrives at
* an object, it delegates to StaticCompletion for that object.
*/
class PackageCompletion(classpath: List[URL]) extends CompletionAware {
// it takes a little while to look through the jars so we use a future and a concurrent map
class CompletionAgent {
val dottedPaths: ConcurrentHashMap[String, List[CompletionInfo]] = new ConcurrentHashMap[String, List[CompletionInfo]]
val topLevelPackages = new DelayedLazyVal(
() => enumToList(dottedPaths.keys) filterNot (_ contains '.'),
getDottedPaths(dottedPaths, classpath)
)
}
val agent = new CompletionAgent
import agent._
def completions() = topLevelPackages()
override def follow(id: String) =
if (dottedPaths containsKey id) Some(new SubCompletor(id))
else None
class SubCompletor(root: String) extends CompletionAware {
private def infos = dottedPaths get root
def completions() = infos map (_.visibleName)
override def follow(segment: String): Option[CompletionAware] = {
PackageCompletion.this.follow(root + "." + segment) orElse {
for (CompletionInfo(`segment`, className, _) <- infos) {
return Some(new StaticCompletion(className))
}
None
}
}
}
}
object PackageCompletion {
import java.io.File
import java.util.jar.{ JarEntry, JarFile }
import scala.tools.nsc.io.Streamable
def enumToList[T](e: java.util.Enumeration[T]): List[T] = enumToListInternal(e, Nil)
private def enumToListInternal[T](e: java.util.Enumeration[T], xs: List[T]): List[T] =
if (e == null || !e.hasMoreElements) xs else enumToListInternal(e, e.nextElement :: xs)
def getClassFiles(path: String): List[String] = {
def exists(path: String) = { new File(path) exists }
if (!exists(path)) return Nil
(enumToList(new JarFile(path).entries) map (_.getName)) .
partialMap { case x: String if x endsWith ".class" => x dropRight 6 } .
filterNot { ReflectionCompletion.shouldHide }
}
case class CompletionInfo(visibleName: String, className: String, jar: String) {
lazy val jarfile = new JarFile(jar)
lazy val entry = jarfile getEntry className
override def hashCode = visibleName.hashCode
override def equals(other: Any) = other match {
case x: CompletionInfo => visibleName == x.visibleName
case _ => false
}
def getBytes(): Array[Byte] = {
if (entry == null) Array() else {
val x = new Streamable.Bytes { def inputStream() = jarfile getInputStream entry }
x.toByteArray()
}
}
}
// all the dotted path to classfiles we can find by poking through the jars
def getDottedPaths(map: ConcurrentHashMap[String, List[CompletionInfo]], classpath: List[URL]): Unit = {
val cp = classpath map (_.getPath)
val jars = cp.removeDuplicates filter (_ endsWith ".jar")
// for e.g. foo.bar.baz.C, returns (foo -> bar), (foo.bar -> baz), (foo.bar.baz -> C)
// and scala.Range$BigInt needs to go scala -> Range -> BigInt
def subpaths(s: String): List[(String, String)] = {
val segs = decode(s).split("""[/.]""")
val components = segs dropRight 1
(1 to components.length).toList flatMap { i =>
val k = components take i mkString "."
if (segs(i) contains "$") {
val dollarsegs = segs(i).split("$").toList
for (j <- 1 to (dollarsegs.length - 1) toList) yield {
val newk = k + "." + (dollarsegs take j mkString ".")
(k -> dollarsegs(j))
}
}
else List(k -> segs(i))
}
}
def oneJar(jar: String): Unit = {
val classfiles = getClassFiles(jar)
for (cl <- classfiles.removeDuplicates ; (k, _v) <- subpaths(cl)) {
val v = CompletionInfo(_v, cl, jar)
if (map containsKey k) {
val vs = map.get(k)
if (vs contains v) ()
else map.put(k, v :: vs)
}
else map.put(k, List(v))
}
}
jars foreach oneJar
}
}
|