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.examples; 018 019import io.leangen.geantyref.TypeToken; 020import org.checkerframework.checker.nullness.qual.Nullable; 021import org.spongepowered.configurate.CommentedConfigurationNode; 022import org.spongepowered.configurate.ConfigurateException; 023import org.spongepowered.configurate.NodePath; 024import org.spongepowered.configurate.hocon.HoconConfigurationLoader; 025import org.spongepowered.configurate.objectmapping.ConfigSerializable; 026import org.spongepowered.configurate.reference.ConfigurationReference; 027import org.spongepowered.configurate.reference.ValueReference; 028import org.spongepowered.configurate.reference.WatchServiceListener; 029 030import java.io.IOException; 031import java.nio.file.Path; 032import java.nio.file.Paths; 033import java.util.List; 034import java.util.Objects; 035import java.util.UUID; 036 037public class ValueReferences implements AutoCloseable { 038 039 private final WatchServiceListener listener; 040 private final ConfigurationReference<CommentedConfigurationNode> base; 041 private final ValueReference<String, CommentedConfigurationNode> name; 042 private final ValueReference<Integer, CommentedConfigurationNode> cookieCount; 043 private final ValueReference<List<TestObject>, CommentedConfigurationNode> complex; 044 045 @ConfigSerializable 046 static class TestObject { 047 String name; 048 UUID id = UUID.randomUUID(); 049 050 @Override 051 public boolean equals(final Object o) { 052 if (this == o) { 053 return true; 054 } 055 056 if (!(o instanceof TestObject)) { 057 return false; 058 } 059 060 final TestObject that = (TestObject) o; 061 return this.name.equals(that.name) 062 && this.id.equals(that.id); 063 } 064 065 @Override 066 public int hashCode() { 067 return Objects.hash(this.name, this.id); 068 } 069 070 @Override 071 public String toString() { 072 return "TestObject{" 073 + "name='" + this.name + '\'' 074 + ", id=" + this.id 075 + '}'; 076 } 077 } 078 079 public ValueReferences(final Path configFile) throws IOException, ConfigurateException { 080 // Initialize the watch service and node 081 this.listener = WatchServiceListener.create(); 082 this.base = this.listener.listenToConfiguration(file -> HoconConfigurationLoader.builder() 083 .defaultOptions(opts -> opts.shouldCopyDefaults(true)) 084 .path(file) 085 .build(), configFile); 086 087 // Perform actions on successful and unsuccessful reloads 088 this.base.updates().subscribe($ -> System.out.println("Configuration auto-reloaded")); 089 this.base.errors().subscribe(err -> { 090 final Throwable thr = err.getValue(); 091 System.out.println("Unable to " + err.getKey() + " the configuration: " + thr.getMessage()); 092 if (thr.getCause() != null) { 093 System.out.println(thr.getCause().getMessage()); 094 } 095 }); 096 097 // Get node references 098 this.name = this.base.referenceTo(String.class, "name"); 099 this.name.subscribe(newName -> System.out.println("Reloaded, name is: " + newName)); 100 this.cookieCount = this.base.referenceTo(Integer.class, NodePath.path("cookie-count"), 5); 101 this.complex = this.base.referenceTo(new TypeToken<List<TestObject>>() {}, "complex"); 102 103 // And then save now that values have been initialized 104 this.base.save(); 105 } 106 107 public void repl() { 108 boolean running = true; 109 if (System.console() == null) { 110 System.err.println("Not at an interactive prompt"); 111 this.printData(); 112 return; 113 } 114 115 while (running) { 116 final @Nullable String next = System.console().readLine(">"); 117 if (next == null) { 118 break; 119 } 120 final String[] cmd = next.split(" ", -1); 121 if (cmd.length == 0) { 122 continue; 123 } 124 125 switch (cmd[0]) { 126 case "stop": 127 running = false; 128 break; 129 case "name": 130 if (cmd.length < 2) { 131 System.err.println("Not enough arguments, usage: name <new-name>"); 132 break; 133 } 134 this.name.setAndSave(cmd[1]); 135 System.out.println("Name: " + this.name.get()); 136 break; 137 case "dump": 138 printData(); 139 break; 140 case "help": 141 System.out.println("Value reference tester\n" 142 + "Commands:\n\n" 143 + "stop: Exit the loop\n" 144 + "name <name>: Update the name in the config file\n" 145 + "dump: Dump all accessed data in the config file\n" 146 + "help: Show this message" 147 ); 148 break; 149 default: 150 System.err.println("Unknown command '" + next + "'"); 151 System.err.println("help for help"); 152 } 153 } 154 } 155 156 public void printData() { 157 System.out.println("Name: " + this.name.get()); 158 System.out.println("Cookie count: " + this.cookieCount.get()); 159 System.out.println("Complex: "); 160 if (this.complex.get().isEmpty()) { 161 System.out.println("(empty)"); 162 } else { 163 for (TestObject obj : this.complex.get()) { 164 System.out.println("- " + obj); 165 } 166 } 167 } 168 169 @Override 170 public void close() throws IOException { 171 this.base.close(); 172 this.listener.close(); 173 } 174 175 public static void main(final String[] args) { 176 if (args.length != 1) { 177 System.err.println("Usage: ./reference-example <file>"); 178 return; 179 } 180 final Path path = Paths.get(args[0]); 181 try (ValueReferences engine = new ValueReferences(path)) { 182 engine.repl(); 183 } catch (final IOException e) { // may be a ConfigurateException, or something else 184 System.out.println("Error loading configuration: " + e.getMessage()); 185 e.printStackTrace(); 186 } 187 } 188 189}