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
018import com.alibaba.fastjson.JSON;
019import com.alibaba.fastjson.serializer.SerializerFeature;
020
021import java.lang.reflect.Array;
022import java.util.Collection;
023import java.util.HashMap;
024import java.util.Map;
025import java.util.function.Function;
026
027public class Maps extends HashMap<String, Object> {
028
029    public static Maps of() {
030        return new Maps();
031    }
032
033    public static Maps of(String key, Object value) {
034        Maps maps = Maps.of();
035        maps.put(key, value);
036        return maps;
037    }
038
039    public static Maps ofNotNull(String key, Object value) {
040        return new Maps().setIfNotNull(key, value);
041    }
042
043    public static Maps ofNotEmpty(String key, Object value) {
044        return new Maps().setIfNotEmpty(key, value);
045    }
046
047    public static Maps ofNotEmpty(String key, Maps value) {
048        return new Maps().setIfNotEmpty(key, value);
049    }
050
051
052    public Maps set(String key, Object value) {
053        super.put(key, value);
054        return this;
055    }
056
057    public Maps setChild(String key, Object value) {
058        if (key.contains(".")) {
059            String[] keys = key.split("\\.");
060            Map<String, Object> currentMap = this;
061            for (int i = 0; i < keys.length; i++) {
062                String currentKey = keys[i].trim();
063                if (currentKey.isEmpty()) {
064                    continue;
065                }
066                if (i == keys.length - 1) {
067                    currentMap.put(currentKey, value);
068                } else {
069                    //noinspection unchecked
070                    currentMap = (Map<String, Object>) currentMap.computeIfAbsent(currentKey, k -> Maps.of());
071                }
072            }
073        } else {
074            super.put(key, value);
075        }
076
077        return this;
078    }
079
080    public Maps setOrDefault(String key, Object value, Object orDefault) {
081        if (isNullOrEmpty(value)) {
082            return this.set(key, orDefault);
083        } else {
084            return this.set(key, value);
085        }
086    }
087
088    public Maps setIf(boolean condition, String key, Object value) {
089        if (condition) put(key, value);
090        return this;
091    }
092
093    public Maps setIf(Function<Maps, Boolean> func, String key, Object value) {
094        if (func.apply(this)) put(key, value);
095        return this;
096    }
097
098    public Maps setIfNotNull(String key, Object value) {
099        if (value != null) put(key, value);
100        return this;
101    }
102
103    public Maps setIfNotEmpty(String key, Object value) {
104        if (!isNullOrEmpty(value)) {
105            put(key, value);
106        }
107        return this;
108    }
109
110    public Maps setIfNotEmpty(Map<String, Object> source) {
111        if (!isNullOrEmpty(source)) {
112            this.putAll(source);
113        }
114        return this;
115    }
116
117
118    public Maps setIfContainsKey(String checkKey, String key, Object value) {
119        if (this.containsKey(checkKey)) {
120            this.put(key, value);
121        }
122        return this;
123    }
124
125    public Maps setIfNotContainsKey(String checkKey, String key, Object value) {
126        if (!this.containsKey(checkKey)) {
127            this.put(key, value);
128        }
129        return this;
130    }
131
132    public String toJSON() {
133        return JSON.toJSONString(this, SerializerFeature.DisableCircularReferenceDetect);
134    }
135
136
137    private static boolean isNullOrEmpty(Object value) {
138        if (value == null) {
139            return true;
140        }
141
142        if (value instanceof Collection && ((Collection<?>) value).isEmpty()) {
143            return true;
144        }
145
146        if (value instanceof Map && ((Map<?, ?>) value).isEmpty()) {
147            return true;
148        }
149
150        if (value.getClass().isArray() && Array.getLength(value) == 0) {
151            return true;
152        }
153
154        return value instanceof String && ((String) value).trim().isEmpty();
155    }
156}