All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.dotc.classpath.FileUtils.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2014 Contributor. All rights reserved.
 */
package dotty.tools
package dotc.classpath

import scala.language.unsafeNulls

import java.io.{File => JFile, FileFilter}
import java.net.URL
import dotty.tools.io.AbstractFile

/**
 * Common methods related to Java files and abstract files used in the context of classpath
 */
object FileUtils {
  extension (file: AbstractFile) {
    def isPackage: Boolean = file.isDirectory && mayBeValidPackage(file.name)

    def isClass: Boolean = !file.isDirectory && hasClassExtension

    def hasClassExtension: Boolean = file.ext.isClass

    def hasTastyExtension: Boolean = file.ext.isTasty

    def hasBetastyExtension: Boolean = file.ext.isBetasty

    def isTasty: Boolean = !file.isDirectory && hasTastyExtension

    def isBestEffortTasty: Boolean = !file.isDirectory && hasBetastyExtension

    def isScalaBinary: Boolean = file.isClass || file.isTasty

    def isScalaOrJavaSource: Boolean = !file.isDirectory && file.ext.isScalaOrJava

    // TODO do we need to check also other files using ZipMagicNumber like in scala.tools.nsc.io.Jar.isJarOrZip?
    def isJarOrZip: Boolean = file.ext.isJarOrZip

    /**
     * Safe method returning a sequence containing one URL representing this file, when underlying file exists,
     * and returning given default value in other case
     */
    def toURLs(default: => Seq[URL] = Seq.empty): Seq[URL] = if (file.file == null) default else Seq(file.toURL)

    /**
     * Returns if there is an existing sibling `.tasty` file.
     */
    def hasSiblingTasty: Boolean =
      assert(file.hasClassExtension, s"non-class: $file")
      file.resolveSibling(classNameToTasty(file.name)) != null
  }

  extension (file: JFile) {
    def isPackage: Boolean = file.isDirectory && mayBeValidPackage(file.getName)

    def isClass: Boolean = file.isFile && hasClassExtension

    def hasClassExtension: Boolean = file.getName.endsWith(SUFFIX_CLASS)

    def isTasty: Boolean = file.isFile && file.getName.endsWith(SUFFIX_TASTY)

    def isBestEffortTasty: Boolean = file.isFile && file.getName.endsWith(SUFFIX_BETASTY)


    /**
     * Returns if there is an existing sibling `.tasty` file.
     */
    def hasSiblingTasty: Boolean =
      assert(file.hasClassExtension, s"non-class: $file")
      val path = file.toPath
      val tastyPath = path.resolveSibling(classNameToTasty(file.getName))
      java.nio.file.Files.exists(tastyPath)

  }

  private val SUFFIX_CLASS = ".class"
  private val SUFFIX_SCALA = ".scala"
  private val SUFFIX_TASTY = ".tasty"
  private val SUFFIX_BETASTY = ".betasty"
  private val SUFFIX_JAVA = ".java"
  private val SUFFIX_SIG = ".sig"

  def stripSourceExtension(fileName: String): String =
    if (endsScala(fileName)) stripClassExtension(fileName)
    else if (endsJava(fileName)) stripJavaExtension(fileName)
    else throw new FatalError("Unexpected source file ending: " + fileName)

  def dirPath(forPackage: String): String = forPackage.replace('.', JFile.separatorChar)

  def dirPathInJar(forPackage: String): String = forPackage.replace('.', '/')

  inline private def ends (filename:String, suffix:String) = filename.endsWith(suffix) && filename.length > suffix.length

  def endsClass(fileName: String): Boolean =
    ends (fileName, SUFFIX_CLASS) || fileName.endsWith(SUFFIX_SIG)

  def endsScalaOrJava(fileName: String): Boolean =
    endsScala(fileName) || endsJava(fileName)

  def endsJava(fileName: String): Boolean =
    ends (fileName, SUFFIX_JAVA)

  def endsScala(fileName: String): Boolean =
    ends (fileName, SUFFIX_SCALA)

  def stripClassExtension(fileName: String): String =
    fileName.substring(0, fileName.lastIndexOf('.'))

  def stripJavaExtension(fileName: String): String =
    fileName.substring(0, fileName.length - 5) // equivalent of fileName.length - SUFFIX_JAVA.length

  // probably it should match a pattern like [a-z_]{1}[a-z0-9_]* but it cannot be changed
  // because then some tests in partest don't pass
  def mayBeValidPackage(dirName: String): Boolean =
    (dirName != "META-INF") && (dirName != "") && (dirName.charAt(0) != '.')

  def mkFileFilter(f: JFile => Boolean): FileFilter = new FileFilter {
    def accept(pathname: JFile): Boolean = f(pathname)
  }

  /** Transforms a .class file name to a .tasty file name */
  private def classNameToTasty(fileName: String): String =
    val classOrModuleName = fileName.stripSuffix(".class")
    val className =
      if classOrModuleName.endsWith("$")
        && classOrModuleName != "Null$" // scala.runtime.Null$
        && classOrModuleName != "Nothing$" // scala.runtime.Nothing$
        // Special case for `object $` in Amonite.
        // This is an ad-hoc workaround for Amonite `object $`. See issue #19702
        // This definition is not valid Scala.
        && classOrModuleName != "$"
      then classOrModuleName.stripSuffix("$")
      else classOrModuleName
    className + SUFFIX_TASTY
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy