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

com.tencent.devops.common.api.util.EnumUtil.kt Maven / Gradle / Ivy

/*
 * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company.  All rights reserved.
 *
 * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license.
 *
 * A copy of the MIT License is included in this file.
 *
 *
 * Terms of the MIT License:
 * ---------------------------------------------------
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.tencent.devops.common.api.util

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import sun.misc.Unsafe
import sun.reflect.ReflectionFactory
import java.lang.reflect.AccessibleObject
import java.lang.reflect.Field
import kotlin.reflect.full.isSubclassOf

/**
 * 枚举工具类,用于动态修改枚举值和内容
 *
 */
@Suppress("ALL")
object EnumUtil {

    /**
     * 动态修改枚举,但动态范围仅限于后来使用枚举类的values或valueOf方法实例化出来的枚举值
     *
     * 不支持动态替换:直接在代码中使用 MyEnum.APPLE.type 这种写法在编译期已经生成为常量,就无法实现动态替换了.
     * 支持动态替换:  MyEnum.valueOf("APPLE") MyEnum.values() 这种可以实现动态读取到
     *
     * @param  枚举的泛型
     * @param enumType 要动态修改的枚举类
     * @param enumName 枚举值名称
     * @param additionalValues 枚举定义的其他字段的值,如果有,如果无传空数组
     */
    inline fun  addEnum(enumType: Class, enumName: String, additionalValues: Array) {

        // 检查是否是枚举类型,如果不是则抛出异常
        if (!Enum::class.java.isAssignableFrom(enumType)) {
            throw RuntimeException("class $enumType is not an instance of Enum")
        }
        // 1. Lookup "$VALUES" holder in enum class and get previous enum instances
        var valuesField: Field? = null
        val fields: Array = enumType.declaredFields
        for (field in fields) {
            if (field.name.contains("\$VALUES")) {
                valuesField = field
                break
            }
        }

        val additionalTypes = mutableListOf>()
        additionalValues.forEach { value ->
            when {
                // kotlin 中 List可变mutable与不可变immutable是两个不同的接口,需要区分对待
                value::class.isSubclassOf(MutableList::class) -> additionalTypes.add(MutableList::class.java)
                // 赋值时注意其他Java对List的各种扩展子类,都转为List
                value::class.isSubclassOf(List::class) -> additionalTypes.add(List::class.java)
                // 其他场景暂时未覆盖完
                else -> additionalTypes.add(value::class.java)
            }
        }

        AccessibleObject.setAccessible(arrayOf(valuesField), true)

        try {
            // 将先之前的枚举值保存下来
            val previousValues = valuesField!![enumType] as Array

            val values: MutableList = mutableListOf()
            var ordinal = previousValues.size

            previousValues.forEachIndexed { idx, value ->
                // 如果是存在的枚举值,则服它替换旧值
                if (value.toString() == enumName) {
                    ordinal = idx
                }
                values.add(value)
            }

//            // 构建新枚举值
//            val newValue: T = makeEnum(
//                enumClass = enumType,
//                value = enumName,
//                ordinal = ordinal,
//                additionalTypes = additionalTypes.toTypedArray(),
//                additionalValues = additionalValues
//            )

            // 构造新的枚举实例
            val newValue = makeEnum(enumType, enumName, ordinal, additionalValues)

            if (ordinal < previousValues.size) {
                values[ordinal] = newValue
            } else {
                values.add(newValue)
            }

            setStaticFieldValue(enumType, valuesField, values.toTypedArray())

            cleanEnumCache(enumType)
        } catch (e: Exception) {
            println(e)
            e.printStackTrace()
            throw RuntimeException(e.message, e)
        }
    }

    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
    inline fun  setStaticFieldValue(
        enumType: Class,
        valuesField: Field,
        newValues: Any?
    ) {
        val unsafe = getUnsafe()
        val arrayBaseOffset = unsafe.staticFieldOffset(valuesField)
        unsafe.putObjectVolatile(enumType, arrayBaseOffset, newValues)
    }

    inline fun  setObjectFieldValue(
        enumType: Class,
        valuesField: Field,
        newValues: Any?
    ) {
        val unsafe = getUnsafe()
        val arrayBaseOffset = unsafe.objectFieldOffset(valuesField)
        unsafe.putObjectVolatile(enumType, arrayBaseOffset, newValues)
    }

    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
    inline fun  blankField(enumClass: Class, fieldName: String) {
        for (field in Class::class.java.declaredFields) {
            if (field.name.contains(fieldName)) {
                setObjectFieldValue(enumClass, field, null)
                break
            }
        }
    }

    @Throws(NoSuchFieldException::class, IllegalAccessException::class)
    inline fun  cleanEnumCache(enumClass: Class) {
        blankField(enumClass, "enumConstantDirectory") // OracleJDK & OpenJDK
        blankField(enumClass, "enumConstants") // IBM JDK
    }

    fun compareParameterType(constructorParameterType: Array>, parameterTypes: Array?>): Boolean {
        if (constructorParameterType.size != parameterTypes.size) {
            return false
        }
        for (i in constructorParameterType.indices) {
            if (constructorParameterType[i] !== parameterTypes[i]) {
                if (constructorParameterType[i].isPrimitive && parameterTypes[i]!!.isPrimitive) {
                    if (constructorParameterType[i].kotlin.javaPrimitiveType
                        !== parameterTypes[i]!!.kotlin.javaPrimitiveType
                    ) {
                        return false
                    }
                }
            }
        }
        return true
    }

//    @Throws(Exception::class)
//    inline fun  makeEnum(
//        enumClass: Class,
//        value: String,
//        ordinal: Int,
//        additionalTypes: Array>,
//        additionalValues: Array
//    ): T {
//        val params = arrayOfNulls(additionalValues.size + 2)
//        params[0] = value
//        params[1] = Integer.valueOf(ordinal)
//        System.arraycopy(additionalValues, 0, params, 2, additionalValues.size)
//        return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes)!!.newInstance(params))
//    }

    // 创建新的枚举实例
    inline fun  makeEnum(
        enumType: Class,
        name: String,
        ordinal: Int,
        additionalValues: Array
    ): T {
        val unsafe = getUnsafe()
        val obj = unsafe.allocateInstance(enumType)

        // 使用Unsafe设置name、ordinal字段
        setEnumFieldValue(obj, "name", name)
        setEnumFieldValue(obj, "ordinal", ordinal)

        // 初始化自定义字段
        val enumFields =
            enumType.declaredFields.filterNot { it.isSynthetic || it.isEnumConstant || it.name == "Companion" }
        if (additionalValues.size < enumFields.size) {
            logger.warn("additionalValues size(${additionalValues.size}) less than enumField size(${enumFields.size})")
        } else {
            enumFields.forEachIndexed { index, field ->
                field.isAccessible = true
                field.set(obj, additionalValues[index])
            }
        }

        return obj as T
    }

    // 使用Unsafe设置枚举字段值
    fun setEnumFieldValue(enumInstance: Any, fieldName: String, value: Any) {
        try {
            val field = enumInstance.javaClass.superclass.getDeclaredField(fieldName)
            val unsafe = getUnsafe()
            val offset = unsafe.objectFieldOffset(field)
            when (value) {
                is Int -> unsafe.putInt(enumInstance, offset, value)
                is Long -> unsafe.putLong(enumInstance, offset, value)
                is Short -> unsafe.putShort(enumInstance, offset, value)
                is Byte -> unsafe.putByte(enumInstance, offset, value)
                is Float -> unsafe.putFloat(enumInstance, offset, value)
                is Double -> unsafe.putDouble(enumInstance, offset, value)
                is Boolean -> unsafe.putBoolean(enumInstance, offset, value)
                is Char -> unsafe.putChar(enumInstance, offset, value)
                else -> unsafe.putObject(enumInstance, offset, value)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    // 从 JVM 获取 Unsafe 实例
    fun getUnsafe(): Unsafe {
        return try {
            val field = Unsafe::class.java.getDeclaredField("theUnsafe")
            field.isAccessible = true
            field.get(null) as Unsafe
        } catch (e: Exception) {
            throw RuntimeException("Unsafe not found", e)
        }
    }

    val reflectionFactory: ReflectionFactory = ReflectionFactory.getReflectionFactory()
    val logger: Logger = LoggerFactory.getLogger(EnumUtil::class.java)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy