001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.spongepowered.configurate.extra.dfu.v2; 018 019import static java.util.Objects.requireNonNull; 020 021import com.google.common.collect.ImmutableMap; 022import com.mojang.datafixers.DSL; 023import com.mojang.datafixers.Dynamic; 024import com.mojang.datafixers.types.DynamicOps; 025import com.mojang.datafixers.types.Type; 026import org.checkerframework.checker.nullness.qual.Nullable; 027import org.spongepowered.configurate.CommentedConfigurationNode; 028import org.spongepowered.configurate.ConfigurationNode; 029import org.spongepowered.configurate.ConfigurationOptions; 030import org.spongepowered.configurate.serialize.TypeSerializerCollection; 031 032import java.util.Map; 033import java.util.Optional; 034import java.util.function.Function; 035import java.util.function.Supplier; 036import java.util.stream.Stream; 037 038/** 039 * Implementation of DataFixerUpper's DynamicOps. 040 * 041 * <p>When possible, the first node's {@link ConfigurationNode#copy()} method 042 * will be used to create a new node to contain results. Otherwise, the provided 043 * factory will be used. The default factory creates a 044 * {@link CommentedConfigurationNode} with the {@link TypeSerializerCollection#defaults() default TypeSerializer collection}, 045 * but a custom factory may be provided. 046 * 047 * @since 4.0.0 048 */ 049public final class ConfigurateOps implements DynamicOps<ConfigurationNode> { 050 051 private static final ConfigurateOps INSTANCE = new ConfigurateOps(CommentedConfigurationNode::root); 052 053 private final Supplier<? extends ConfigurationNode> factory; 054 055 /** 056 * Get the shared instance of this class, which creates new nodes using 057 * the default factory. 058 * 059 * @return the shared instance 060 * @since 4.0.0 061 */ 062 public static DynamicOps<ConfigurationNode> instance() { 063 return INSTANCE; 064 } 065 066 /** 067 * Create a new instance of the ops, with a custom node factory. 068 * 069 * @param factory the factory function 070 * @return a new ops instance 071 * @since 4.0.0 072 */ 073 public static DynamicOps<ConfigurationNode> withNodeFactory(final Supplier<? extends ConfigurationNode> factory) { 074 return new ConfigurateOps(factory); 075 } 076 077 /** 078 * Wrap a ConfigurationNode in a {@link Dynamic} instance. The returned Dynamic will use the same type 079 * serializer collection as the original node for its operations. 080 * 081 * @param node the node to wrap 082 * @return a wrapped node 083 * @since 4.0.0 084 */ 085 public static Dynamic<ConfigurationNode> wrap(final ConfigurationNode node) { 086 if (node.options().serializers().equals(TypeSerializerCollection.defaults())) { 087 return new Dynamic<>(instance(), node); 088 } else { 089 final ConfigurationOptions opts = node.options(); 090 return new Dynamic<>(withNodeFactory(() -> CommentedConfigurationNode.root(opts)), node); 091 } 092 } 093 094 ConfigurateOps(final Supplier<? extends ConfigurationNode> factory) { 095 this.factory = factory; 096 } 097 098 private static String unwrapKey(final ConfigurationNode node) { 099 return requireNonNull(node.getString(), "Kep nodes must have a value!"); 100 } 101 102 @Override 103 public ConfigurationNode empty() { 104 return this.factory.get(); 105 } 106 107 @Override 108 public Type<?> getType(final ConfigurationNode input) { 109 requireNonNull(input, "input"); 110 111 if (input.isMap()) { 112 return DSL.compoundList(DSL.remainderType(), DSL.remainderType()); 113 } else if (input.isList()) { 114 return DSL.list(DSL.remainderType()); 115 } else { 116 final @Nullable Object value = input.rawScalar(); 117 if (value == null) { 118 return DSL.nilType(); 119 } else if (value instanceof String) { 120 return DSL.string(); 121 } else if (value instanceof Boolean) { 122 return DSL.bool(); 123 } else if (value instanceof Short) { 124 return DSL.shortType(); 125 } else if (value instanceof Integer) { 126 return DSL.intType(); 127 } else if (value instanceof Long) { 128 return DSL.longType(); 129 } else if (value instanceof Float) { 130 return DSL.floatType(); 131 } else if (value instanceof Double) { 132 return DSL.doubleType(); 133 } else if (value instanceof Byte) { 134 return DSL.byteType(); 135 } else { 136 throw new IllegalArgumentException("Scalar value '" + input + "' has an unknown type: " + value.getClass().getName()); 137 } 138 } 139 } 140 141 @Override 142 public Optional<Number> getNumberValue(final ConfigurationNode input) { 143 if (!(input.isMap() || input.isList())) { 144 final @Nullable Object raw = input.rawScalar(); 145 if (raw instanceof Number) { 146 return Optional.of((Number) raw); 147 } else if (raw instanceof Boolean) { 148 return Optional.of(input.getBoolean() ? 1 : 0); 149 } 150 } 151 152 return Optional.empty(); 153 } 154 155 @Override 156 public ConfigurationNode createNumeric(final Number i) { 157 return empty().raw(i); 158 } 159 160 @Override 161 public ConfigurationNode createBoolean(final boolean value) { 162 return empty().raw(value); 163 } 164 165 @Override 166 public Optional<String> getStringValue(final ConfigurationNode input) { 167 return Optional.ofNullable(input.getString()); 168 } 169 170 @Override 171 public ConfigurationNode createString(final String value) { 172 return empty().raw(value); 173 } 174 175 @Override 176 public ConfigurationNode mergeInto(final ConfigurationNode input, final ConfigurationNode value) { 177 if (input.isList()) { 178 final ConfigurationNode ret = input.copy(); 179 ret.appendListNode().from(value); 180 return ret; 181 } 182 return input; 183 } 184 185 @Override 186 public ConfigurationNode mergeInto(final ConfigurationNode input, final ConfigurationNode key, final ConfigurationNode value) { 187 return input.copy().node(unwrapKey(key)).from(value); 188 } 189 190 /** 191 * Merge into a newly created node. 192 * 193 * @param first the primary node 194 * @param second the second node, with values that will override those in 195 * the first node 196 * @return a newly created node 197 */ 198 @Override 199 public ConfigurationNode merge(final ConfigurationNode first, final ConfigurationNode second) { 200 return first.copy().mergeFrom(second); 201 202 } 203 204 @Override 205 public Optional<Map<ConfigurationNode, ConfigurationNode>> getMapValues(final ConfigurationNode input) { 206 if (input.isMap()) { 207 final ImmutableMap.Builder<ConfigurationNode, ConfigurationNode> builder = ImmutableMap.builder(); 208 for (final Map.Entry<Object, ? extends ConfigurationNode> entry : input.childrenMap().entrySet()) { 209 builder.put(empty().raw(entry.getKey()), entry.getValue().copy()); 210 } 211 return Optional.of(builder.build()); 212 } 213 214 return Optional.empty(); 215 } 216 217 @Override 218 public ConfigurationNode createMap(final Map<ConfigurationNode, ConfigurationNode> map) { 219 final ConfigurationNode ret = empty(); 220 221 for (final Map.Entry<ConfigurationNode, ConfigurationNode> entry : map.entrySet()) { 222 ret.node(unwrapKey(entry.getKey())).from(entry.getValue()); 223 } 224 225 return ret; 226 } 227 228 @Override 229 public Optional<Stream<ConfigurationNode>> getStream(final ConfigurationNode input) { 230 if (input.isList()) { 231 final Stream<ConfigurationNode> stream = input.childrenList().stream().map(it -> it); 232 return Optional.of(stream); 233 } 234 235 return Optional.empty(); 236 } 237 238 @Override 239 public ConfigurationNode createList(final Stream<ConfigurationNode> input) { 240 final ConfigurationNode ret = empty(); 241 input.forEach(it -> ret.appendListNode().from(it)); 242 return ret; 243 } 244 245 @Override 246 public ConfigurationNode remove(final ConfigurationNode input, final String key) { 247 if (input.isMap()) { 248 final ConfigurationNode ret = input.copy(); 249 ret.node(key).raw(null); 250 return ret; 251 } 252 253 return input; 254 } 255 256 @Override 257 public Optional<ConfigurationNode> get(final ConfigurationNode input, final String key) { 258 final ConfigurationNode ret = input.node(key); 259 return ret.virtual() ? Optional.empty() : Optional.of(ret); 260 } 261 262 @Override 263 public Optional<ConfigurationNode> getGeneric(final ConfigurationNode input, final ConfigurationNode key) { 264 final ConfigurationNode ret = input.node(unwrapKey(key)); 265 return ret.virtual() ? Optional.empty() : Optional.of(ret); 266 } 267 268 @Override 269 public ConfigurationNode set(final ConfigurationNode input, final String key, final ConfigurationNode value) { 270 final ConfigurationNode ret = input.copy(); 271 ret.node(key).from(value); 272 return ret; 273 } 274 275 @Override 276 public ConfigurationNode update(final ConfigurationNode input, final String key, final Function<ConfigurationNode, ConfigurationNode> function) { 277 if (input.node(key).virtual()) { 278 return input; 279 } 280 281 final ConfigurationNode ret = input.copy(); 282 283 final ConfigurationNode child = ret.node(key); 284 child.from(function.apply(child)); 285 return ret; 286 } 287 288 @Override 289 public ConfigurationNode updateGeneric(final ConfigurationNode input, final ConfigurationNode wrappedKey, 290 final Function<ConfigurationNode, ConfigurationNode> function) { 291 final Object key = unwrapKey(wrappedKey); 292 if (input.node(key).virtual()) { 293 return input; 294 } 295 296 final ConfigurationNode ret = input.copy(); 297 298 final ConfigurationNode child = ret.node(key); 299 child.from(function.apply(child)); 300 return ret; 301 } 302 303 @Override 304 public String toString() { 305 return "Configurate"; 306 } 307 308}