/**
* PermissionsEx
* Copyright (C) zml and PermissionsEx contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ninja.leaping.permissionsex.backend.file;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import ninja.leaping.configurate.transformation.ConfigurationTransformation;
import ninja.leaping.configurate.transformation.TransformAction;
import ninja.leaping.permissionsex.backend.ConversionUtils;
import ninja.leaping.permissionsex.logging.TranslatableLogger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static ninja.leaping.configurate.transformation.ConfigurationTransformation.WILDCARD_OBJECT;
import static ninja.leaping.permissionsex.util.Translations.t;
public class SchemaMigrations {
public static final int LATEST_VERSION = 4;
private SchemaMigrations() {
}
private static ConfigurationTransformation.Builder tBuilder() {
return ConfigurationTransformation.builder();
}
static ConfigurationTransformation versionedMigration(final TranslatableLogger logger) {
return ConfigurationTransformation.versionedBuilder()
.setVersionKey("schema-version")
.addVersion(LATEST_VERSION, threeToFour())
.addVersion(3, twoTo3())
.addVersion(2, oneTo2(logger))
.addVersion(1, initialTo1())
.build();
}
static ConfigurationTransformation threeToFour() {
return ConfigurationTransformation.chain(
tBuilder()
.addAction(new Object[]{"worlds", WILDCARD_OBJECT, "inheritance"}, (inputPath, valueAtPath) -> {
try {
valueAtPath.setValue(Lists.transform(valueAtPath.getList(TypeToken.of(String.class)), input -> "world:" + input));
} catch (ObjectMappingException e) {
throw new RuntimeException(e);
}
return new Object[]{"context-inheritance", "world:" + inputPath.get(1)};
}).build(),
tBuilder()
.addAction(new Object[]{"worlds"}, (inputPath, valueAtPath) -> {
valueAtPath.setValue(null);
return null;
}).build());
}
static ConfigurationTransformation twoTo3() {
final Map<String, List<Map.Entry<String, Integer>>> convertedRanks = new HashMap<>();
return ConfigurationTransformation.chain(
tBuilder()
.addAction(new Object[]{WILDCARD_OBJECT}, (nodePath, configurationNode) -> {
if (configurationNode.hasMapChildren()) {
String lastPath = nodePath.get(0).toString();
if (lastPath.endsWith("s")) {
lastPath = lastPath.substring(0, lastPath.length() - 1);
}
return new Object[]{"subjects", lastPath};
} else {
return null;
}
}).build(),
tBuilder()
.addAction(new Object[]{"subjects", "group", WILDCARD_OBJECT}, (nodePath, configurationNode) -> {
for (ConfigurationNode child : configurationNode.getChildrenList()) {
if (child.getNode(FileSubjectData.KEY_CONTEXTS).isVirtual() || child.getNode(FileSubjectData.KEY_CONTEXTS).getChildrenMap().isEmpty()) {
ConfigurationNode optionsNode = child.getNode("options");
if (optionsNode.isVirtual()) {
return null;
}
ConfigurationNode rank = optionsNode.getNode("rank");
if (!rank.isVirtual()) {
final String rankLadder = optionsNode.getNode("rank-ladder").getString("default");
List<Map.Entry<String, Integer>> tempVals = convertedRanks.get(rankLadder.toLowerCase());
if (tempVals == null) {
tempVals = new ArrayList<>();
convertedRanks.put(rankLadder.toLowerCase(), tempVals);
}
tempVals.add(Maps.immutableEntry(configurationNode.getKey().toString(), rank.getInt()));
rank.setValue(null);
optionsNode.getNode("rank-ladder").setValue(null);
if (optionsNode.getChildrenMap().isEmpty()) {
optionsNode.setValue(null);
}
}
}
}
return null;
}).build(),
tBuilder().addAction(new Object[0], (nodePath, configurationNode) -> {
for (Map.Entry<String, List<Map.Entry<String, Integer>>> ent : convertedRanks.entrySet()) {
Collections.sort(ent.getValue(), (a, b) -> b.getValue().compareTo(a.getValue()));
ConfigurationNode ladderNode = configurationNode.getNode(FileDataStore.KEY_RANK_LADDERS, ent.getKey());
for (Map.Entry<String, Integer> grp : ent.getValue()) {
ladderNode.getAppendedNode().setValue("group:" + grp.getKey());
}
}
return null;
}).build());
}
static ConfigurationTransformation oneTo2(final TranslatableLogger logger) {
return ConfigurationTransformation.chain(
tBuilder()
.addAction(new Object[]{WILDCARD_OBJECT, WILDCARD_OBJECT}, (nodePath, configurationNode) -> {
Object value = configurationNode.getValue();
configurationNode.setValue(null);
configurationNode.getAppendedNode().setValue(value);
return null;
})
.build(),
tBuilder()
.addAction(new Object[]{WILDCARD_OBJECT, WILDCARD_OBJECT, 0, "worlds"}, (nodePath, configurationNode) -> {
ConfigurationNode entityNode = configurationNode.getParent().getParent();
for (Map.Entry<Object, ? extends ConfigurationNode> ent : configurationNode.getChildrenMap().entrySet()) {
entityNode.getAppendedNode().setValue(ent.getValue())
.getNode(FileSubjectData.KEY_CONTEXTS, "world").setValue(ent.getKey());
}
configurationNode.setValue(null);
return null;
}).build(),
tBuilder()
.addAction(new Object[]{WILDCARD_OBJECT, WILDCARD_OBJECT, WILDCARD_OBJECT, "permissions"}, (nodePath, configurationNode) -> {
List<String> existing = configurationNode.getList(Object::toString);
if (!existing.isEmpty()) {
configurationNode.setValue(ImmutableMap.of());
}
for (String permission : existing) {
int value = permission.startsWith("-") ? -1 : 1;
if (value < 0) {
permission = permission.substring(1);
}
if (permission.equals("*")) {
configurationNode.getParent().getNode("permissions-default").setValue(value);
continue;
}
permission = ConversionUtils.convertLegacyPermission(permission);
if (permission.contains("*")) {
logger.warn(t("The permission at %s contains a now-illegal character '*'", Arrays.toString(configurationNode.getPath())));
}
configurationNode.getNode(permission).setValue(value);
}
if (configurationNode.getChildrenMap().isEmpty()) {
configurationNode.setValue(null);
}
return null;
})
.addAction(new Object[]{"users", WILDCARD_OBJECT, WILDCARD_OBJECT, "group"}, (nodePath, configurationNode) -> {
Object[] retPath = nodePath.getArray();
retPath[retPath.length - 1] = "parents";
for (ConfigurationNode child : configurationNode.getChildrenList()) {
child.setValue("group:" + child.getValue());
}
return retPath;
})
.addAction(new Object[]{"groups", WILDCARD_OBJECT, WILDCARD_OBJECT, "inheritance"}, (nodePath, configurationNode) -> {
Object[] retPath = nodePath.getArray();
retPath[retPath.length - 1] = "parents";
for (ConfigurationNode child : configurationNode.getChildrenList()) {
child.setValue("group:" + child.getValue());
}
return retPath;
})
.addAction(new Object[]{"groups", WILDCARD_OBJECT, WILDCARD_OBJECT}, (inputPath, valueAtPath) -> {
ConfigurationNode defaultNode = valueAtPath.getNode("options", "default");
if (!defaultNode.isVirtual()) {
if (defaultNode.getBoolean()) {
ConfigurationNode addToNode = null;
final ConfigurationNode defaultsParent = valueAtPath.getParent().getParent().getParent().getNode("systems", "default");
for (ConfigurationNode node : defaultsParent.getChildrenList()) {
if (Objects.equal(node.getNode(FileSubjectData.KEY_CONTEXTS).getValue(), valueAtPath.getNode(FileSubjectData.KEY_CONTEXTS).getValue())) {
addToNode = node;
break;
}
}
if (addToNode == null) {
addToNode = defaultsParent.getAppendedNode();
}
addToNode.getNode("parents").getAppendedNode().setValue("group:" + valueAtPath.getParent().getKey());
}
defaultNode.setValue(null);
final ConfigurationNode optionsNode = valueAtPath.getNode("options");
if (optionsNode.getChildrenMap().isEmpty()) {
optionsNode.setValue(null);
}
}
return null;
}).build());
}
private static final TransformAction MOVE_PREFIX_SUFFIX_ACTION = (nodePath, configurationNode) -> {
final ConfigurationNode prefixNode = configurationNode.getNode("prefix");
if (!prefixNode.isVirtual()) {
configurationNode.getNode("options", "prefix").setValue(prefixNode);
prefixNode.setValue(null);
}
final ConfigurationNode suffixNode = configurationNode.getNode("suffix");
if (!suffixNode.isVirtual()) {
configurationNode.getNode("options", "suffix").setValue(suffixNode);
suffixNode.setValue(null);
}
final ConfigurationNode defaultNode = configurationNode.getNode("default");
if (!defaultNode.isVirtual()) {
configurationNode.getNode("options", "default").setValue(defaultNode);
defaultNode.setValue(null);
}
return null;
};
static ConfigurationTransformation initialTo1() {
return ConfigurationTransformation.builder()
.addAction(new Object[]{WILDCARD_OBJECT, WILDCARD_OBJECT}, MOVE_PREFIX_SUFFIX_ACTION)
.addAction(new Object[]{WILDCARD_OBJECT, WILDCARD_OBJECT, "worlds", WILDCARD_OBJECT}, MOVE_PREFIX_SUFFIX_ACTION)
.build();
}
}