core/src/iosMain/kotlin/licenseplist/LicensePlistParser.kt
changeset 25 a645b4ebb054
child 34 ce299aacc068
equal deleted inserted replaced
24:f07de07b90c4 25:a645b4ebb054
       
     1 /*
       
     2  * AboutOss is an utility library to retrieve and display
       
     3  * opensource licenses in Android applications.
       
     4  *
       
     5  * Copyright (C) 2023 by Frederic-Charles Barthelery.
       
     6  *
       
     7  * This file is part of AboutOss.
       
     8  *
       
     9  * AboutOss is free software: you can redistribute it and/or modify
       
    10  * it under the terms of the GNU General Public License as published by
       
    11  * the Free Software Foundation, either version 3 of the License, or
       
    12  * (at your option) any later version.
       
    13  *
       
    14  * AboutOss is distributed in the hope that it will be useful,
       
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    17  * GNU General Public License for more details.
       
    18  *
       
    19  * You should have received a copy of the GNU General Public License
       
    20  * along with AboutOss.  If not, see <http://www.gnu.org/licenses/>.
       
    21  */
       
    22 package com.geekorum.aboutoss.core.licenseplist
       
    23 
       
    24 import kotlinx.cinterop.BetaInteropApi
       
    25 import kotlinx.cinterop.ExperimentalForeignApi
       
    26 import kotlinx.cinterop.ObjCObjectVar
       
    27 import kotlinx.cinterop.alloc
       
    28 import kotlinx.cinterop.memScoped
       
    29 import kotlinx.cinterop.ptr
       
    30 import kotlinx.cinterop.value
       
    31 import platform.Foundation.NSBundle
       
    32 import platform.Foundation.NSData
       
    33 import platform.Foundation.NSError
       
    34 import platform.Foundation.NSPropertyListMutableContainers
       
    35 import platform.Foundation.NSPropertyListSerialization
       
    36 import platform.Foundation.NSURL
       
    37 import platform.Foundation.dataWithContentsOfURL
       
    38 import kotlin.coroutines.resume
       
    39 import kotlin.coroutines.resumeWithException
       
    40 import kotlin.coroutines.suspendCoroutine
       
    41 
       
    42 class LicensePlistParser {
       
    43 
       
    44     @Suppress("UNCHECKED_CAST")
       
    45     suspend fun parseLicenses(
       
    46         licensePlistInput: NSURL
       
    47     ): Map<String, String> {
       
    48         val fileContent = getContent(licensePlistInput)
       
    49         val plist = fileContent.toPropertyList() as Map<String, List<Map<String, String>>>
       
    50         val paneLists = plist["PreferenceSpecifiers"]!!
       
    51             .filter {
       
    52                 it["Type"] == "PSChildPaneSpecifier"
       
    53             }
       
    54         val directoryUrl = licensePlistInput.URLByDeletingLastPathComponent()!!
       
    55         return paneLists.associate { pane ->
       
    56             val paneFile = pane["File"]!!
       
    57             val libraryName = pane["Title"]!!
       
    58             val paneUrl = buildPaneUrl(directoryUrl, paneFile)
       
    59             val paneFileContent =  getContent(paneUrl)
       
    60             val paneFilePlist = paneFileContent.toPropertyList() as Map<String, List<Map<String, String>>>
       
    61             val license = getLicenseFromPaneFilePlist(paneFilePlist)
       
    62             libraryName to license
       
    63         }
       
    64     }
       
    65 
       
    66     private fun buildPaneUrl(directoryUrl: NSURL, paneName: String) = directoryUrl.URLByAppendingPathComponent(paneName)!!.URLByAppendingPathExtension("plist")!!
       
    67 
       
    68     private fun getLicenseFromPaneFilePlist(paneFilePList: Map<String, List<Map<String, String>>>): String {
       
    69         val specifiers = paneFilePList["PreferenceSpecifiers"]!!
       
    70         val licenses = specifiers.map {
       
    71             it["FooterText"]!!
       
    72         }
       
    73         return licenses.joinToString("\n\n")
       
    74     }
       
    75 
       
    76     private fun getContent(url: NSURL): NSData {
       
    77         return checkNotNull(NSData.dataWithContentsOfURL(url))
       
    78     }
       
    79 
       
    80     @OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
       
    81     private suspend fun NSData.toPropertyList(): Any? = suspendCoroutine { cont ->
       
    82         val parsed = memScoped {
       
    83             val error: ObjCObjectVar<NSError?> = alloc()
       
    84             val result = NSPropertyListSerialization.propertyListWithData(this@toPropertyList,
       
    85                 options = NSPropertyListMutableContainers,
       
    86                 format = null,
       
    87                 error.ptr
       
    88             )
       
    89             if (error.value != null) {
       
    90                 cont.resumeWithException(Exception(error.value!!.description))
       
    91             }
       
    92             result
       
    93         }
       
    94         cont.resume(parsed!!)
       
    95     }
       
    96 
       
    97     companion object {
       
    98         fun getDefaultLicensePlistUrl(): NSURL {
       
    99             val path = NSBundle.mainBundle.pathForResource(
       
   100                 "com.mono0926.LicensePlist",
       
   101                 ofType = "plist",
       
   102                 inDirectory = "licenseplist"
       
   103             )
       
   104             return NSURL.fileURLWithPath(path!!)
       
   105         }
       
   106     }
       
   107 }