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 ninja.leaping.configurate.examples; 018 019import ninja.leaping.configurate.ConfigurationNode; 020import org.checkerframework.checker.nullness.qual.Nullable; 021import ninja.leaping.configurate.hocon.HoconConfigurationLoader; 022import ninja.leaping.configurate.transformation.ConfigurationTransformation; 023 024import java.io.IOException; 025import java.nio.file.Paths; 026 027 028/** 029 * An example of how to use transformations to migrate a configuration to a newer schema version. 030 * <p> 031 * It's like DFU but not hot garbage! (and probably less PhD-worthy) 032 */ 033public class Transformations { 034 private static final int VERSION_LATEST = 1; // easy way to track the latest version, update as more revisions are added 035 private static final String VERSION_KEY = "version"; 036 037 private static Object[] path(Object... path) { 038 return path; 039 } 040 041 /** 042 * Create a new builder for versioned configurations. This builder uses a field in the node (by default {@code 043 * schema-version}) to determine the current schema version (using -1 for no version present). 044 * 045 * @return versioned transformation 046 */ 047 public static ConfigurationTransformation create() { 048 return ConfigurationTransformation.versionedBuilder() 049 .setVersionKey(VERSION_KEY) // this is the default, but to show it can be customized 050 .addVersion(VERSION_LATEST, zeroToOne()) // syntax: target version, latest version 051 .addVersion(0, initialTransform()) 052 .build(); 053 } 054 055 /** 056 * A transformation. This one has multiple actions, and demonstrates how wildcards work 057 * 058 * @return created transformation 059 */ 060 public static ConfigurationTransformation initialTransform() { 061 return ConfigurationTransformation.builder() 062 // Move the node at `serverVersion` to the location <code>{"server", "version"}</code> 063 .addAction(path("serverVersion"), (path, value) -> { 064 return new Object[]{"server", "version"}; 065 }) 066 // For every direct child of the `section` node, set the value of its child `new-value` to something 067 .addAction(path("section", ConfigurationTransformation.WILDCARD_OBJECT), (path, value) -> { 068 value.getNode("new-value").setValue("i'm a default"); 069 070 return null; // don't move the value 071 }) 072 .build(); 073 } 074 075 public static ConfigurationTransformation zeroToOne() { 076 return ConfigurationTransformation.builder() 077 // oh, turns out we want to use a different format for this, so we'll change it again 078 .addAction(path("server", "version"), (path, value) -> { 079 @Nullable String val = value.getString(); 080 if (val != null) { 081 value.setValue(val.replaceAll("-", "_")); 082 } 083 return null; 084 }) 085 .build(); 086 } 087 088 /** 089 * Apply the transformations to a node 090 * <p> 091 * This method also prints information about the version update that occurred 092 * 093 * @param node The node to transform 094 * @param <N> node type 095 * @return provided node, after transformation 096 */ 097 public static <N extends ConfigurationNode> N updateNode(N node) { 098 if (!node.isVirtual()) { // we only want to migrate existing data 099 ConfigurationTransformation trans = create(); 100 int startVersion = node.getNode(VERSION_KEY).getInt(-1); 101 trans.apply(node); 102 int endVersion = node.getNode(VERSION_KEY).getInt(); 103 if (startVersion != endVersion) { // we might not have made any changes 104 System.out.println("Updated config schema from " + startVersion + " to " + endVersion); 105 } 106 } 107 return node; 108 } 109 110 public static void main(String[] args) throws IOException { 111 if (args.length != 1) { 112 System.err.println("Not enough arguments, usage: transformations <file>"); 113 System.err.println("Apply the test transformations to a single file"); 114 } 115 HoconConfigurationLoader loader = HoconConfigurationLoader.builder() 116 .setPath(Paths.get(args[0])) 117 .build(); 118 119 loader.save(updateNode(loader.load())); // tada 120 } 121 122}