summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/util/ClassPath.scala
blob: cb67f55c6058353b8b5dffad3078218c5ee44706 (plain) (blame)
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/* NSC -- new Scala compiler
 * Copyright 2006-2007 LAMP/EPFL
 * @author  Martin Odersky
 */

// $Id$

package scala.tools.nsc.util

import java.io.File
import java.net.URL
import java.util.StringTokenizer

import scala.collection.mutable.ArrayBuffer
import scala.tools.nsc.io.AbstractFile

/** <p>
 *    This module provides star expansion of '-classpath' option arguments.
 *  </p>
 *
 *  @author Stepan Koltsov
 */
object ClassPath {
  /** Expand single path entry */
  private def expandStar(pattern: String): List[String] = {
    def nameMatchesStar(name: String) = name.toLowerCase().endsWith(".jar")

    /** Get all jars in directory */
    def lsJars(f: File) = {
      val list = f.listFiles()
      if (list eq null) Nil
      else list.filter(f => f.isFile() && nameMatchesStar(f.getName())).map(_.getPath()).toList
    }

    val suffix = File.separator + "*"

    if (pattern == "*") lsJars(new File("."))
    else if (pattern endsWith suffix) lsJars(new File(pattern.substring(0, pattern.length - suffix.length)))
    else pattern :: Nil
  }

  /** Split path using platform-dependent path separator */
  def splitPath(path: String): List[String] = {
    val strtok = new StringTokenizer(path, File.pathSeparator)
    val buf = new ListBuffer[String]
    while (strtok.hasMoreTokens()) {
      buf + strtok.nextToken()
    }
    buf.toList
  }

  /** Expand path with expanding stars */
  def expandPath(path: String): List[String] = splitPath(path).flatMap(expandStar(_))

  def expandPath(path: String, expandStar: Boolean): List[String] =
    if (expandStar) expandPath(path)
    else splitPath(path)

}

/** <p>
 *    Richer classpath abstraction than files.
 *  </p>
 *  <p>
 *    Roughly based on Eclipse's classpath abstractions.
 *  </p>
 *
 *  @author Sean McDirmid
 */
class ClassPath(onlyPresentation: Boolean) {

  def this() = this(false)

  class Source(val location: AbstractFile, val compile: Boolean) {
    // assert(location           != null, "cannot find source location")
    // assert(location.getFile() != null, "cannot find source location " + " " + location + " " + location.getClass())
    override def toString(): String = "" + location + " " + compile
  }

  abstract class Entry(val location: AbstractFile) {
    // assert(location           != null, "cannot find classpath location")
    // assert(location.getFile() != null, "cannot find classpath location " + " " + location + " " + location.getClass())
    def source: Source
    override def toString() =
      (if (location == null) "<none>" else location.toString) +
        (if (source == null) "" else " source=" + source)
  }

  class Output(location0: AbstractFile, val sourceFile: AbstractFile) extends Entry(location0) {
    def source = if (sourceFile ne null) new Source(sourceFile, true) else null
  }

  class Library(location0: AbstractFile) extends Entry(location0) {
    def doc: AbstractFile = null
    def sourceFile: AbstractFile = null
    def source = if (sourceFile eq null) null else new Source(sourceFile, false)
  }

  class Context(val entries: List[Entry]) {
    def find(name: String, isDir: Boolean): Context = if (isPackage) {
      def find0(entries: List[Entry]): Context = {
        if (entries.isEmpty) new Context(Nil)
        else {
          val ret = find0(entries.tail)
          val head = entries.head;
          val name0 = name + (if (!isDir) ".class" else "")
          val clazz = if (head.location eq null) null
                      else head.location.lookupPath(name0, isDir)

          val source0 =
            if (head.source eq null) null
            else if ((clazz eq null) || isDir) {
              val source1 = head.source.location.lookupPath(
                name + (if (isDir) "" else ".scala"), isDir)
              if ((source1 eq null) && !isDir && (clazz ne null)) head.source.location
              else source1
            }
            else head.source.location

          if ((clazz eq null) && (source0 eq null)) ret
          else {
            val entry = new Entry(clazz) {
              override def source =
                if (source0 eq null) null
                else new Source(source0, head.source.compile)
            }
            try {
              //Console.err.println("this=" + this + "\nclazz=" + clazz + "\nsource0=" + source0 + "\n")

              if (!isDir) new Context(entry :: Nil)
              else new Context(entry :: ret.entries)
            } catch {
              case e: Error =>
              throw e
            }
          }
        }
      }

      val ret = find0(entries)
      if (ret.entries.isEmpty) {
        //Console.err.println("BAD_FILE: " + name + " in " + this)
        null
      } else ret
    } else null

    def isPackage: Boolean =
      if (entries.isEmpty) false
      else if (entries.head.location ne null) entries.head.location.isDirectory
      else entries.head.source.location.isDirectory

    def name =
      if (entries.isEmpty) "<none>"
      else {
        val head = entries.head
        val name = if (head.location ne null) head.location.name
                   else head.source.location.name
        if (isPackage) name
        else name.substring(0, name.length() - (".class").length())
      }

    override def toString(): String = toString(entries)

    def toString(entry: Entry): String =
      ((if (entry.location eq null) "<none>"
        else entry.location.toString()) +
       (if (entry.source eq null) ""
        else " with_source=" + entry.source.location.toString()))

    def toString(entries0: List[Entry]): String =
      if (entries0.isEmpty) ""
      else toString(entries0.head) + ":::" + toString(entries0.tail)

    def isSourceFile = {
      def head = entries.head
      def clazz = head.location
      def source = if (head.source eq null) null else head.source.location
      def isPredef = source.name.equals("Predef.scala") ||
                     source.path.startsWith("scala/runtime")

      if (entries.isEmpty || entries.isEmpty || (source eq null)) false
      else if (!onlyPresentation && !head.source.compile) false
      else if (source.isDirectory) false
      else if (clazz eq null) true
      else if (onlyPresentation && !isPredef) true
      else if (source.lastModified > clazz.lastModified) true
      else false
    }

    def sourceFile = if ((entries.head.source ne null) && !entries.head.source.location.isDirectory)
      entries.head.source.location else null

    def classFile = if (!isSourceFile) entries.head.location else null

    {
      val sourcePath0 = sourcePath
      if (sourcePath0 ne null) {
        if (!sourcePath0.isDirectory) {
          Console.err.println(""+sourcePath0 + " should be a directory")
          assert(false)
        }
      }
    }

    def sourcePath =
      if (!isSourceFile && !entries.isEmpty && (entries.head.source ne null)) {
        val ret = entries.head.source.location
        ret
      }
      else null

    def validPackage(name: String): Boolean =
      ! (name.equals("META-INF") || name.startsWith("."))
  }

  class Build {
    val entries = new ArrayBuffer[Entry]

    def root = new Context(entries.toList)

    def this(classpath: String) {
      this()
      addFilesInPath(classpath)
    }

    def this(source: String, output: String) {
      this()
      addDirsInPath(source, output)
    }

    def this(classpath: String, source: String, output: String,
             boot: String, extdirs: String, codebase: String) {
      this()
      addFilesInPath(boot)
      addArchivesInExtDirPath(extdirs)
      addDirsInPath(source, output)
      addFilesInPath(classpath)
      addURLsInPath(codebase)
    }

    /**
     *  @param path  ...
     *  @param isDir ...
     *  @return      ...
     */
    def lookupPath(path: String, isDir: Boolean): AbstractFile = {
      val ctx = root.find(path, isDir)
      if (ctx eq null) null
      else if (ctx.entries.isEmpty) null
      else if (ctx.entries.head eq null) null
      else ctx.entries.head.location
    }

    /**
     *  @param classes where the class files come from and are written to
     *  @param sources where the source files come from
     */
    def output(classes : String, sources : String) = {
      assert(classes ne null)
      assert(sources ne null)
      val location = AbstractFile.getDirectory(classes)
      val sources0 = AbstractFile.getDirectory(sources)
      class Output0 extends Output(location, sources0)
      entries += new Output0()
    }
    /**
     *  @param classes where the class files come from
     *  @param sources optional source file attachment, otherwise null
     */
    def library(classes: String, sources: String) {
      assert(classes ne null)
      val location = AbstractFile.getDirectory(classes)
      var sourceFile0 =
        if (sources ne null) AbstractFile.getDirectory(sources)
        else null
      if (sourceFile0 ne null) {
        val file00 = sourceFile0.lookupPath("src", true)
        if ((file00 ne null) && file00.isDirectory) {
          assert(true)
          sourceFile0 = file00
        }
      }

      class Library0 extends Library(location) {
        override def sourceFile = sourceFile0
      }
      entries += new Library0()
    }

    private def addFilesInPath(path: String) {
      for (fileName <- ClassPath.expandPath(path)) {
        val file = AbstractFile.getDirectory(fileName)
        if (file ne null) entries += (new Library(file))
      }
    }

    private def addArchivesInExtDirPath(path: String) {
      for (fileName <- ClassPath.expandPath(path)) {
        val file = AbstractFile.getDirectory(fileName)
        if (file ne null) {
          for (file0 <- file) {
            val name = file0.name
            if (name.endsWith(".jar") || name.endsWith(".zip") || file0.isDirectory) {
              val archive = AbstractFile.getDirectory(new File(file.file, name))
              if (archive ne null) entries += (new Library(archive))
            }
          }
        }
      }
    }

    private def addDirsInPath(source: String, output: String) {
      val clazzes = AbstractFile.getDirectory(output)
      if (clazzes eq null)
        throw new FatalError("Output location \"" + output + "\" not found")
      val strtok = new StringTokenizer(source, File.pathSeparator)
      if (!strtok.hasMoreTokens()) {
        val output0 = (new Output(clazzes, null))
        entries += output0
      }
      else while (strtok.hasMoreTokens()) {
        val sources = AbstractFile.getDirectory(strtok.nextToken())
        val output0 = (new Output(clazzes, sources))
        entries += output0
      }
    }

    private val urlSeparator = " "
    private def addURLsInPath(codebase: String) {
      val strtok = new StringTokenizer(codebase, urlSeparator)
      while (strtok.hasMoreTokens()) {
        try {
          val url = new URL(strtok.nextToken())
          val archive = AbstractFile.getURL(url)
          if (archive ne null) entries += (new Library(archive))
        }
        catch {
          case e =>
            Console.println("error in addURLsInPath: " + e.getMessage)//debug
            throw e
        }
      }
    }

    override def toString() =
      entries.toList.mkString("", File.pathSeparator, "")
  } // class Build

}