/
ProjectResources.scala
287 lines (243 loc) · 9.47 KB
/
ProjectResources.scala
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
package org.netbeans.modules.scala.core
import java.io.File
import java.io.IOException
import java.net.MalformedURLException
import java.net.URISyntaxException
import java.net.URL
import java.util.logging.Logger
import org.netbeans.api.java.classpath.ClassPath
import org.netbeans.api.project.FileOwnerQuery
import org.netbeans.api.project.Project
import org.netbeans.api.project.ProjectUtils
import org.netbeans.api.project.SourceGroup
import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation
import org.openide.filesystems.FileObject
import org.openide.filesystems.FileStateInvalidException
import org.openide.filesystems.FileUtil
import org.openide.modules.InstalledFileLocator
import org.openide.util.Exceptions
import scala.collection.mutable
import scala.reflect.io.AbstractFile
/**
*
* @author Caoyuan Deng
*/
object ProjectResources {
private val log = Logger.getLogger(getClass.getName)
/** @see org.netbeans.api.java.project.JavaProjectConstants */
val SOURCES_TYPE_JAVA = "java"
/** a source group type for separate scala source roots, as seen in maven projects for example */
val SOURCES_TYPE_SCALA = "scala"
private val projectToResources = new mutable.WeakHashMap[Project, ProjectResource]
class ProjectResource {
var srcToOut: Map[FileObject, FileObject] = Map()
var testToOut: Map[FileObject, FileObject] = Map()
def srcOutDirsPath = toDirPaths(srcToOut)
def testSrcOutDirsPath = toDirPaths(testToOut)
def scalaSrcToOut: Map[AbstractFile, AbstractFile] = toScalaDirs(srcToOut)
def scalaTestToOut: Map[AbstractFile, AbstractFile] = toScalaDirs(testToOut)
private def toDirPaths(dirs: Map[FileObject, FileObject]): Map[String, String] = {
for ((src, out) <- dirs) yield (toDirPath(src), toDirPath(out))
}
private def toScalaDirs(dirs: Map[FileObject, FileObject]): Map[AbstractFile, AbstractFile] = {
for ((src, out) <- dirs) yield (toScalaDir(src), toScalaDir(out))
}
private def toDirPath(fo: FileObject) = FileUtil.toFile(fo).getAbsolutePath
private def toScalaDir(fo: FileObject) = AbstractFile.getDirectory(FileUtil.toFile(fo))
}
def getResourceOf(fo: FileObject, refresh: Boolean): Map[FileObject, FileObject] = {
val project = FileOwnerQuery.getOwner(fo)
if (project eq null) {
// * it may be a standalone file, or file in standard lib
return Map()
}
val resource = if (refresh ) {
findProjectResource(project)
} else {
projectToResources.getOrElseUpdate(project, findProjectResource(project))
}
if (isForTest(resource, fo)) resource.testToOut else resource.srcToOut
}
def getSrcFileObjects(fo: FileObject, refresh: Boolean): Array[FileObject] = {
val resource = getResourceOf(fo, refresh)
val srcPaths = for ((src, out) <- resource) yield src
srcPaths.toArray
}
def getOutFileObject(fo: FileObject, refresh: Boolean): Option[FileObject] = {
val resource = getResourceOf(fo, refresh)
for ((src, out) <- resource) {
try {
val file = FileUtil.toFile(out)
if (!file.exists) file.mkdirs
} catch {
case _: Throwable =>
}
return Some(out)
}
None
}
/** is this `fo` under test source? */
def isForTest(resource: ProjectResource, fo: FileObject) = {
resource.testToOut exists {case (src, _) => src.equals(fo) || FileUtil.isParentOf(src, fo)}
}
def findAllSourcesOf(mimeType: String, result: mutable.ListBuffer[FileObject])(dirFo: FileObject) {
dirFo.getChildren foreach {x =>
if (x.isFolder) {
findAllSourcesOf(mimeType, result)(x)
} else if (x.getMIMEType == mimeType) {
result += x
}
}
}
def getScalaJavaSourceGroups(project: Project): Array[SourceGroup] = {
val sources = ProjectUtils.getSources(project)
val scalaSgs = sources.getSourceGroups(SOURCES_TYPE_SCALA)
val javaSgs = sources.getSourceGroups(SOURCES_TYPE_JAVA)
scalaSgs ++ javaSgs
}
def findProjectResource(project: Project): ProjectResource = {
val resource = new ProjectResource
val sources = ProjectUtils.getSources(project)
val scalaSgs = sources.getSourceGroups(SOURCES_TYPE_SCALA)
val javaSgs = sources.getSourceGroups(SOURCES_TYPE_JAVA)
log.fine((scalaSgs map (_.getRootFolder.getPath)).mkString("Project's src group[Scala] dir: [", ", ", "]"))
log.fine((javaSgs map (_.getRootFolder.getPath)).mkString("Project's src group[Java] dir: [", ", ", "]"))
List(scalaSgs, javaSgs) foreach {
case Array(srcSg) =>
val src = srcSg.getRootFolder
val out = findOutDir(project, src)
resource.srcToOut += (src -> out)
case Array(srcSg, testSg, _*) =>
val src = srcSg.getRootFolder
val out = findOutDir(project, src)
resource.srcToOut += (src -> out)
val test = testSg.getRootFolder
val testOut = findOutDir(project, test)
resource.testToOut += (test -> testOut)
case x =>
// @todo add other srcs
}
resource
}
def findOutDir(project: Project, srcRoot: FileObject): FileObject = {
val srcRootUrl: URL =
try {
// make sure the url is in same form of BinaryForSourceQueryImplementation
FileUtil.toFile(srcRoot).toURI.toURL
} catch {
case ex: MalformedURLException => Exceptions.printStackTrace(ex); null
}
var out: FileObject = null
val query = project.getLookup.lookup(classOf[BinaryForSourceQueryImplementation])
if ((query != null) && (srcRootUrl != null)) {
val result = query.findBinaryRoots(srcRootUrl)
if (result != null) {
var break = false
val itr = result.getRoots.iterator
while (itr.hasNext && !break) {
val url = itr.next
if (!FileUtil.isArchiveFile(url)) {
val uri = try {
url.toURI
} catch {case ex: URISyntaxException => Exceptions.printStackTrace(ex); null}
if (uri != null) {
val file = new File(uri)
break =
if (file != null) {
if (file.isDirectory) {
out = FileUtil.toFileObject(file)
true
} else if (file.exists) {
false
} else {
// * global requires an exist out path, so we should create
if (file.mkdirs) {
out = FileUtil.toFileObject(file)
true
} else false
}
} else false
}
}
}
}
}
if (out == null) {
val execCp = ClassPath.getClassPath(srcRoot, ClassPath.EXECUTE)
if (execCp != null) {
val candidates = execCp.getRoots filter {x => FileUtil.getArchiveFile(x) == null}
out = candidates find (_.getPath.endsWith("classes")) match {
case Some(x) => x
case None if candidates.length > 0 => candidates(0)
case _ => null
}
}
}
// global requires an exist out path, so we have to create a tmp folder
if (out == null) {
val projectDir = project.getProjectDirectory
if (projectDir != null && projectDir.isFolder) {
try {
val tmpClasses = "build.classes"
out = projectDir.getFileObject(tmpClasses) match {
case null => projectDir.createFolder(tmpClasses)
case x => x
}
} catch {case ex: IOException => Exceptions.printStackTrace(ex)}
}
}
out
}
def toClassPathString(cp: ClassPath): String = {
if (cp == null) "" else toClassPathString(List(cp))
}
def toClassPathString(cps: Iterable[ClassPath]): String = {
val cpStrs = new mutable.HashSet[String]()
for (cp <- cps) {
val entries = cp.entries.iterator
while (entries.hasNext) {
try {
entries.next.getRoot match {
case null => null
case entryRoot =>
// test if entryRoot is an ArchiveRoot, if yes, FileUtil.getArchiveFile(entryRoot) will return an ArchiveFile
val file = FileUtil.getArchiveFile(entryRoot) match {
case null => FileUtil.toFile(entryRoot) // a regular file
case archiveFile => FileUtil.toFile(archiveFile) // a archive file
}
cpStrs += file.getAbsolutePath
}
} catch {
case ex: FileStateInvalidException => Exceptions.printStackTrace(ex); null
}
}
}
cpStrs.mkString(File.pathSeparator)
}
def getPluginJarsDir = {
InstalledFileLocator.getDefault.locate("modules/ext/org.scala-lang.plugins", "org.netbeans.libs.scala.continuations", false)
}
def getExecuteClassPathString(project: Project): (String, String) = {
val bootCps = new mutable.HashSet[ClassPath]()
val compCps = new mutable.HashSet[ClassPath]()
val execCps = new mutable.HashSet[ClassPath]()
val resource = findProjectResource(project)
for ((src, out) <- resource.srcToOut) {
ClassPath.getClassPath(src, ClassPath.BOOT) match {
case null =>
case cp => bootCps += cp
}
ClassPath.getClassPath(src, ClassPath.COMPILE) match {
case null =>
case cp => compCps += cp
}
ClassPath.getClassPath(src, ClassPath.EXECUTE) match {
case null =>
case cp => execCps += cp
}
}
val bootCpStr = toClassPathString(bootCps)
val execCpStr = toClassPathString(if (execCps.isEmpty) compCps else execCps)
(bootCpStr, execCpStr)
}
}