main

square/leakcanary

Last updated at: 29/12/2023 09:40

ProguardMappingReader.kt

TLDR

This file contains the implementation of the ProguardMappingReader class, which is responsible for reading and parsing ProGuard mapping files.

Methods

readProguardMapping

Reads the ProGuard mapping file and returns a ProguardMapping object containing the parsed mapping information.

Classes

ProguardMappingReader

This class is responsible for reading and parsing ProGuard mapping files. It has the following methods:

  • readProguardMapping: Reads the ProGuard mapping file and returns a ProguardMapping object containing the parsed mapping information.

That's it! There are no further classes or methods in this file.

package shark

import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
import java.text.ParseException

class ProguardMappingReader(
  private val proguardMappingInputStream: InputStream
) {

  @Throws(FileNotFoundException::class, IOException::class, ParseException::class)
  fun readProguardMapping(): ProguardMapping {
    val proguardMapping = ProguardMapping()
    proguardMappingInputStream.bufferedReader(Charsets.UTF_8).use { bufferedReader ->

      var currentClassName: String? = null
      while (true) {
        val line = bufferedReader.readLine()?.trim() ?: break

        if (line.isEmpty() || line.startsWith(HASH_SYMBOL)) {
          // empty line or comment
          continue
        }

        if (line.endsWith(COLON_SYMBOL)) {
          currentClassName = parseClassMapping(line, proguardMapping)
        } else if (currentClassName != null) {
          val isMethodMapping = line.contains(OPENING_PAREN_SYMBOL)
          if (!isMethodMapping) {
            parseClassField(line, currentClassName, proguardMapping)
          }
        }
      }
    }
    return proguardMapping
  }

  // classes are stored as "clearName -> obfuscatedName:"
  private fun parseClassMapping(
    line: String,
    proguardMapping: ProguardMapping
  ): String? {
    val arrowPosition = line.indexOf(ARROW_SYMBOL)
    if (arrowPosition == -1) {
      return null
    }

    val colonPosition = line.indexOf(COLON_SYMBOL, arrowPosition + ARROW_SYMBOL.length)
    if (colonPosition == -1) {
      return null
    }

    val clearClassName = line.substring(0, arrowPosition).trim()
    val obfuscatedClassName =
      line.substring(arrowPosition + ARROW_SYMBOL.length, colonPosition).trim()

    proguardMapping.addMapping(obfuscatedClassName, clearClassName)

    return obfuscatedClassName
  }

  // fields are stored as "typeName clearFieldName -> obfuscatedFieldName"
  private fun parseClassField(
    line: String,
    currentClassName: String,
    proguardMapping: ProguardMapping
  ) {
    val spacePosition = line.indexOf(SPACE_SYMBOL)
    if (spacePosition == -1) {
      return
    }

    val arrowPosition = line.indexOf(ARROW_SYMBOL, spacePosition + SPACE_SYMBOL.length)
    if (arrowPosition == -1) {
      return
    }

    val clearFieldName = line.substring(spacePosition + SPACE_SYMBOL.length, arrowPosition).trim()
    val obfuscatedFieldName = line.substring(arrowPosition + ARROW_SYMBOL.length).trim()

    proguardMapping.addMapping("$currentClassName.$obfuscatedFieldName", clearFieldName)
  }

  companion object {
    private const val HASH_SYMBOL = "#"
    private const val ARROW_SYMBOL = "->"
    private const val COLON_SYMBOL = ":"
    private const val SPACE_SYMBOL = " "
    private const val OPENING_PAREN_SYMBOL = "("
  }
}