summaryrefslogtreecommitdiff
path: root/examples/scala-js/tools/shared/src/main/scala/scala/scalajs/tools/classpath/builder/JarTraverser.scala
blob: bbf527049f5a9ce650bcfbf9a8773294df2ee117 (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
/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js tools             **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package scala.scalajs.tools.classpath.builder

import scala.collection.mutable
import scala.annotation.tailrec

import java.util.zip._
import java.io.{InputStream, InputStreamReader, Reader}

import scala.scalajs.tools.io._
import scala.scalajs.tools.jsdep.JSDependencyManifest

trait JarTraverser extends ClasspathContentHandler with FileSystem {

  private val jsFiles = mutable.Map.empty[String, MemVirtualJSFile]

  /** Traverse a Jar and return a version */
  protected def traverseJar(jar: File): String = {
    val zipStream = new ZipInputStream(toInputStream(jar))

    try readEntries(zipStream, getAbsolutePath(jar))
    finally zipStream.close()

    for {
      (_, jsFile) <- jsFiles
      if jsFile.content != "" // drop if this is just a lone sourcemap
    } handleJS(jsFile)

    getGlobalVersion(jar)
  }

  private def getOrCreateJSFile(relPath: String, fullPath: String, fn: String) =
    jsFiles.getOrElseUpdate(relPath, new MemVirtualJSFile(fullPath) {
      override val name = fn
    })

  @tailrec
  private def readEntries(in: ZipInputStream, jarPath: String): Unit = {
    val entry = in.getNextEntry()
    if (entry != null) {
      readEntry(entry, in, jarPath)
      readEntries(in, jarPath)
    }
  }

  private def readEntry(entry: ZipEntry, in: InputStream, jarPath: String) = {
    val longName = entry.getName
    val shortName = VirtualFile.nameFromPath(longName)
    val fullPath = jarPath + "#" + longName

    def entryReader: Reader = new InputStreamReader(in, "UTF-8")
    def entryContent: String = IO.readInputStreamToString(in)
    def entryBinaryContent: Array[Byte] = IO.readInputStreamToByteArray(in)
    def entryVersion: Option[String] = Some(entry.getTime.toString)

    if (longName == JSDependencyManifest.ManifestFileName)
      handleDepManifest(JSDependencyManifest.read(entryReader))
    else if (longName.endsWith(".js")) {
      val relPath = longName
      getOrCreateJSFile(relPath, fullPath, shortName)
        .withContent(entryContent)
        .withVersion(entryVersion)
    } else if (longName.endsWith(".js.map")) {
      // assume the source map of a JS file
      val relPath = longName.dropRight(".map".length)
      getOrCreateJSFile(relPath, fullPath, shortName)
        .withSourceMap(Some(entryContent))
    } else if (longName.endsWith(".sjsir")) {
      val vf = new MemVirtualSerializedScalaJSIRFile(fullPath) {
        override val name = shortName
      }.withContent(entryBinaryContent)
       .withVersion(entryVersion)

      handleIR(longName, vf)
    }
  }
}