001/** 002 * Copyright (c) 2025-2026, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.gnu.org/licenses/lgpl-3.0.txt 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package dev.tinyflow.core.util; 017 018 019import com.alibaba.fastjson.JSONPath; 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023import java.util.*; 024import java.util.function.Function; 025 026public class MapUtil { 027 private static final Logger log = LoggerFactory.getLogger(MapUtil.class); 028 029 private static final boolean IS_JDK8 = (8 == getJvmVersion0()); 030 031 private MapUtil() { 032 } 033 034 private static String tryTrim(String string) { 035 return string != null ? string.trim() : ""; 036 } 037 038 private static int getJvmVersion0() { 039 int jvmVersion = -1; 040 try { 041 String javaSpecVer = tryTrim(System.getProperty("java.specification.version")); 042 if (StringUtil.hasText(javaSpecVer)) { 043 if (javaSpecVer.startsWith("1.")) { 044 javaSpecVer = javaSpecVer.substring(2); 045 } 046 if (javaSpecVer.indexOf('.') == -1) { 047 jvmVersion = Integer.parseInt(javaSpecVer); 048 } 049 } 050 } catch (Throwable ignore) { 051 // ignore 052 } 053 // default is jdk8 054 if (jvmVersion == -1) { 055 jvmVersion = 8; 056 } 057 return jvmVersion; 058 } 059 060 /** 061 * A temporary workaround for Java 8 specific performance issue JDK-8161372 .<br> 062 * This class should be removed once we drop Java 8 support. 063 * 064 * @see <a href= 065 * "https://bugs.openjdk.java.net/browse/JDK-8161372">https://bugs.openjdk.java.net/browse/JDK-8161372</a> 066 */ 067 public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) { 068 if (IS_JDK8) { 069 V value = map.get(key); 070 if (value != null) { 071 return value; 072 } 073 } 074 return map.computeIfAbsent(key, mappingFunction); 075 } 076 077 public static Object getByPath(Map<String, Object> from, String keyOrPath) { 078 if (StringUtil.noText(keyOrPath) || from == null || from.isEmpty()) { 079 return null; 080 } 081 082 Object result = from.get(keyOrPath); 083 if (result != null) { 084 return result; 085 } 086 087 List<String> parts = Arrays.asList(keyOrPath.split("\\.")); 088 if (parts.isEmpty()) { 089 return null; 090 } 091 092 int matchedLevels = 0; 093 for (int i = parts.size(); i > 0; i--) { 094 String tryKey = String.join(".", parts.subList(0, i)); 095 Object tempResult = from.get(tryKey); 096 if (tempResult != null) { 097 result = tempResult; 098 matchedLevels = i; 099 break; 100 } 101 } 102 103 if (result == null) { 104 return null; 105 } 106 107 if (result instanceof Collection) { 108 List<Object> results = new ArrayList<>(); 109 for (Object item : ((Collection<?>) result)) { 110 results.add(getResult(parts, matchedLevels, item)); 111 } 112 return results; 113 } 114 115 return getResult(parts, matchedLevels, result); 116 } 117 118 private static Object getResult(List<String> parts, int matchedLevels, Object result) { 119 List<String> remainingParts = parts.subList(matchedLevels, parts.size()); 120 String jsonPath = "$." + String.join(".", remainingParts); 121 try { 122 return JSONPath.eval(result, jsonPath); 123 } catch (Exception e) { 124 log.error(e.toString(), e); 125 } 126 127 return null; 128 } 129 130}