001/* 002 * Trident - A Multithreaded Server Alternative 003 * Copyright 2014 The TridentSDK Team 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 */ 017 018package net.tridentsdk.config; 019 020import com.google.common.collect.Lists; 021import com.google.common.collect.Sets; 022import com.google.gson.JsonArray; 023import com.google.gson.JsonElement; 024import com.google.gson.JsonObject; 025import net.tridentsdk.util.TridentLogger; 026 027import javax.annotation.concurrent.GuardedBy; 028import javax.annotation.concurrent.ThreadSafe; 029import java.math.BigDecimal; 030import java.math.BigInteger; 031import java.util.Collection; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035import java.util.stream.Collectors; 036 037/** 038 * Represents a section of the Config file 039 * 040 * @author The TridentSDK Team 041 * @since 0.3-alpha-DP 042 */ 043@ThreadSafe 044public class ConfigSection { 045 final Object handleLock = new Object(); 046 private final Object parentLock = new Object(); 047 @GuardedBy("parentLock") 048 ConfigSection parent; 049 @GuardedBy("handleLock") 050 JsonObject jsonHandle; 051 052 /** 053 * Instantiated by subclasses only 054 */ 055 protected ConfigSection() { 056 } 057 058 /** 059 * Creates a config section for the parent section 060 * 061 * @param parent the section to be sub-sectioned under 062 * @param obj the section handler, used to store values 063 */ 064 protected ConfigSection(ConfigSection parent, JsonObject obj) { 065 this.parent = parent; 066 this.jsonHandle = obj; 067 } 068 069 /** 070 * Gets a config section with the elements defined in the specified collection 071 * 072 * @param list the collection of objects to serialize to a config section 073 * @param <V> the type in the collection 074 * @return the config section with all the values in the list 075 */ 076 public static <V> ConfigSection addToList(Collection<V> list) { 077 if (!(list instanceof ConfigSectionList)) { 078 TridentLogger.get().error( 079 new UnsupportedOperationException("Can only add new ConfigSection-s to ConfigSectionList")); 080 return null; 081 } 082 ConfigSection section = new ConfigSection(((ConfigSectionList) list).parent(), new JsonObject()); 083 list.add((V) section); 084 085 return section; 086 } 087 088 /** 089 * Gets an integer from the config with the given tag, or defaults to the fallback if the tag is not found 090 * 091 * @param tag the tag to find the value from 092 * @param def the default value if no tag is found 093 * @return the integer at the tag 094 */ 095 public int getInt(String tag, int def) { 096 synchronized (handleLock) { 097 return this.contains(tag) ? this.jsonHandle.get(tag).getAsInt() : def; 098 } 099 } 100 101 /** 102 * Gets an integer from the config with the given tag 103 * 104 * <p>Gives {@code 0} if the value could not be found</p> 105 * 106 * @param tag the tag to find the value from 107 * @return the integer at the tag 108 */ 109 public int getInt(String tag) { 110 synchronized (handleLock) { 111 return this.getInt(tag, 0); 112 } 113 } 114 115 /** 116 * Sets the value at the tag to a specified integer 117 * 118 * @param tag the tag to set the value 119 * @param in the integer value to set the tag 120 */ 121 public void setInt(String tag, int in) { 122 synchronized (handleLock) { 123 this.jsonHandle.addProperty(tag, in); 124 } 125 } 126 127 /** 128 * Gets a double from the config with the given tag, or defaults to the fallback if the tag is not found 129 * 130 * @param tag the tag to find the value from 131 * @param def the default value if no tag is found 132 * @return the double at the tag 133 */ 134 public double getDouble(String tag, double def) { 135 synchronized (handleLock) { 136 return this.contains(tag) ? this.jsonHandle.get(tag).getAsDouble() : def; 137 } 138 } 139 140 /** 141 * Gets a double from the config with the given tag 142 * 143 * <p>Gives {@code 0.0D} if the value could not be found</p> 144 * 145 * @param tag the tag to find the value from 146 * @return the double at the tag 147 */ 148 public double getDouble(String tag) { 149 synchronized (handleLock) { 150 return this.getDouble(tag, 0.0D); 151 } 152 } 153 154 /** 155 * Sets a double at the tag to a specified double 156 * 157 * @param tag the tag to set the value 158 * @param d the double to set to the tag 159 */ 160 public void setDouble(String tag, double d) { 161 synchronized (handleLock) { 162 this.jsonHandle.addProperty(tag, d); 163 } 164 } 165 166 /** 167 * Gets a float from the config with the given tag, or defaults to the fallback if the tag is not found 168 * 169 * @param tag the tag to find the value from 170 * @param def the default value if no tag is found 171 * @return the float at the tag 172 */ 173 public float getFloat(String tag, float def) { 174 synchronized (handleLock) { 175 return this.contains(tag) ? this.jsonHandle.get(tag).getAsFloat() : def; 176 } 177 } 178 179 /** 180 * Gets an float from the config with the given tag 181 * 182 * <p>Gives {@code 0.0F if the value could not be found}</p> 183 * 184 * @param tag the tag to find the value from 185 * @return the float at the tag 186 */ 187 public float getFloat(String tag) { 188 synchronized (handleLock) { 189 return this.getFloat(tag, 0.0F); 190 } 191 } 192 193 /** 194 * Sets the float at the tag to a specified float 195 * 196 * @param tag the tag to set the value 197 * @param f the float to set the tag to 198 */ 199 public void setFloat(String tag, float f) { 200 synchronized (handleLock) { 201 this.jsonHandle.addProperty(tag, f); 202 } 203 } 204 205 /** 206 * Gets a character from the config with the given tag, or defaults to the fallback if the tag is not found 207 * 208 * @param tag the tag to find the value from 209 * @param def the default value if no tag is found 210 * @return the character at the tag 211 */ 212 public char getChar(String tag, char def) { 213 synchronized (handleLock) { 214 return this.contains(tag) ? this.jsonHandle.get(tag).getAsCharacter() : def; 215 } 216 } 217 218 /** 219 * Gets a character from the config with the given tag 220 * 221 * <p>Gives {@code \u0000} if the value could not be found</p> 222 * 223 * @param tag the tag to find the value from 224 * @return the character at the tag 225 */ 226 public char getChar(String tag) { 227 synchronized (handleLock) { 228 return this.getChar(tag, '\u0000'); 229 } 230 } 231 232 /** 233 * Sets a character at the tag to the specified character 234 * 235 * @param tag the tag to set the value 236 * @param c the character to set the tag to 237 */ 238 public void setChar(String tag, char c) { 239 synchronized (handleLock) { 240 this.jsonHandle.addProperty(tag, c); 241 } 242 } 243 244 /** 245 * Gets a boolean from the config with the given tag, defaulting to the fallback if the tag is not found 246 * 247 * @param tag the tag to find the value from 248 * @param def the default value if the tag is not found 249 * @return the boolean at the tag 250 */ 251 public boolean getBoolean(String tag, boolean def) { 252 synchronized (handleLock) { 253 return this.contains(tag) ? this.jsonHandle.get(tag).getAsBoolean() : def; 254 } 255 } 256 257 /** 258 * Gets a boolean from the config with the given tag 259 * 260 * <p>Gives {@code false} if the value could not be found</p> 261 * 262 * @param tag the tag to find the value from 263 * @return the boolean at the tag 264 */ 265 public boolean getBoolean(String tag) { 266 synchronized (handleLock) { 267 return this.getBoolean(tag, false); 268 } 269 } 270 271 /** 272 * Sets the boolean the specified tag 273 * 274 * @param tag the tag to set the value 275 * @param b the boolean to set to at the tag 276 */ 277 public void setBoolean(String tag, boolean b) { 278 synchronized (handleLock) { 279 this.jsonHandle.addProperty(tag, b); 280 } 281 } 282 283 /** 284 * Gets a byte from the config with the given tag, defaulting to the fallback if the tag is not found 285 * 286 * @param tag the tag to find the value from 287 * @param def the default if the tag is not found 288 * @return the byte at the tag 289 */ 290 public byte getByte(String tag, byte def) { 291 synchronized (handleLock) { 292 return this.contains(tag) ? this.jsonHandle.get(tag).getAsByte() : def; 293 } 294 } 295 296 /** 297 * Gets a character from the config with the given tag 298 * 299 * <p>Gives {@code (byte) 0} if the value could not be found</p> 300 * 301 * @param tag the tag to find the value from 302 * @return the character at the tag 303 */ 304 public byte getByte(String tag) { 305 synchronized (handleLock) { 306 return this.getByte(tag, (byte) 0); 307 } 308 } 309 310 /** 311 * Sets the byte the specified tag 312 * 313 * @param tag the tag to set the value 314 * @param b the byte to set to at the tag 315 */ 316 public void setByte(String tag, byte b) { 317 synchronized (handleLock) { 318 this.jsonHandle.addProperty(tag, b); 319 } 320 } 321 322 /** 323 * Gets the string at the specified tag, defaulting to the specified default if not found 324 * 325 * @param tag the tag to set the value 326 * @param def the default value to 327 */ 328 public String getString(String tag, String def) { 329 synchronized (handleLock) { 330 return this.contains(tag) ? this.jsonHandle.get(tag).getAsString() : def; 331 } 332 } 333 334 public String getString(String tag) { 335 synchronized (handleLock) { 336 return this.getString(tag, null); 337 } 338 } 339 340 /** 341 * Sets the string the specified tag 342 * 343 * @param tag the tag to set the value 344 * @param s the string to set at the tag 345 */ 346 public void setString(String tag, String s) { 347 synchronized (handleLock) { 348 this.jsonHandle.addProperty(tag, s); 349 } 350 } 351 352 /** 353 * Gets the list at the tag 354 * 355 * @param tag the tag to find the value from 356 * @param type the types contained in the list 357 * @param <V> the list type 358 * @return the list from the section 359 */ 360 public <V> List<V> getList(String tag, Class<V> type) { 361 JsonArray array; 362 synchronized (handleLock) { 363 array = this.jsonHandle.get(tag).getAsJsonArray(); 364 } 365 366 //Handle ConfigSection seperately as it is special 367 if (type.equals(ConfigSection.class)) { 368 List<V> result = new ConfigSectionList<>(this, array); 369 for (JsonElement element : array) { 370 if (element == null) 371 continue; 372 result.add((V) new ConfigSection(this, element.getAsJsonObject())); 373 } 374 return result; 375 } else { 376 List<V> result = new ConfigList<>(array); 377 for (JsonElement element : array) { 378 if (element == null) 379 continue; 380 result.add(GsonFactory.gson().fromJson(element, type)); 381 } 382 return result; 383 } 384 } 385 386 /** 387 * Adds an empty list into the tag 388 * 389 * @param tag the tag to set the value 390 * @param type the types in the list 391 * @param <V> the list type 392 * @return the list added to the section 393 */ 394 public <V> List<V> addList(String tag, Class<V> type) { 395 synchronized (handleLock) { 396 this.jsonHandle.add(tag, new JsonArray()); 397 } 398 399 return this.getList(tag, type); 400 } 401 402 /** 403 * Gets a BigInteger from the section, defaulting the the fallback if not found 404 * 405 * @param tag the tag to find the value from 406 * @param def the default value 407 * @return the BigInteger value at the tag 408 */ 409 public BigInteger getBigInteger(String tag, BigInteger def) { 410 synchronized (handleLock) { 411 return this.contains(tag) ? this.jsonHandle.get(tag).getAsBigInteger() : def; 412 } 413 } 414 415 /** 416 * Gets the BigInteger at the tag 417 * 418 * <p>Defaults to {@code null} if not found</p> 419 * 420 * @param tag the tag to find the value from 421 * @return the BigInteger at the tag 422 */ 423 public BigInteger getBigInteger(String tag) { 424 synchronized (handleLock) { 425 return this.getBigInteger(tag, null); 426 } 427 } 428 429 /** 430 * Sets the {@link java.math.BigInteger} the specified tag 431 * 432 * @param s the tag to set the integer to 433 * @param bi the BigInteger ot set the tag to 434 */ 435 public void setBigInteger(String s, BigInteger bi) { 436 synchronized (handleLock) { 437 this.setString(s, bi.toString()); 438 } 439 } 440 441 /** 442 * Gets a BigDecimal at the specified tag, defaulting to the fallback if not found 443 * 444 * @param tag the tag to find the value from 445 * @param def the default value 446 * @return the value at the tag 447 */ 448 public BigDecimal getBigDecimal(String tag, BigDecimal def) { 449 synchronized (handleLock) { 450 return this.contains(tag) ? this.jsonHandle.get(tag).getAsBigDecimal() : def; 451 } 452 } 453 454 /** 455 * Gets a BigDecimal at the specified tag 456 * 457 * <p>Defaults to {@code null} if the tag is not found</p> 458 * 459 * @param tag the tag to find the value from 460 * @return the value of the tag 461 */ 462 public BigDecimal getBigDecimal(String tag) { 463 synchronized (handleLock) { 464 return this.getBigDecimal(tag, null); 465 } 466 } 467 468 /** 469 * Sets the {@link java.math.BigDecimal} the specified tag 470 * 471 * @param tag the tag to set the value 472 * @param bd the BigDecimal to set the tag to 473 */ 474 public void setBigDecimal(String tag, BigDecimal bd) { 475 synchronized (handleLock) { 476 this.setString(tag, bd.toPlainString()); 477 } 478 } 479 480 /** 481 * Gets the Object at the specified tag 482 * 483 * @param tag the tag to find the value from 484 * @param clazz the type of the object 485 * @param <V> the object type 486 * @return the value 487 */ 488 public <V> V getObject(String tag, Class<V> clazz) { 489 synchronized (handleLock) { 490 return this.contains(tag) ? GsonFactory.gson().fromJson(this.jsonHandle.get(tag), clazz) : null; 491 } 492 } 493 494 /** 495 * Sets the object at the specified tag 496 * 497 * @param tag the tag to set the value 498 * @param object the value to set the tag to 499 */ 500 public void setObject(String tag, Object object) { 501 synchronized (handleLock) { 502 this.jsonHandle.add(tag, GsonFactory.gson().toJsonTree(object)); 503 } 504 } 505 506 /** 507 * Removes the tag from the configuration 508 * 509 * @param tag the tag to remove 510 */ 511 public void remove(String tag) { 512 synchronized (handleLock) { 513 this.jsonHandle.remove(tag); 514 } 515 } 516 517 /** 518 * Checks to see if the section contains the tag 519 * 520 * @param tag the tag to see if contained 521 * @return {@code true} if the tag is in the section, {@code false} if not 522 */ 523 public boolean contains(String tag) { 524 synchronized (handleLock) { 525 return this.jsonHandle.has(tag); 526 } 527 } 528 529 /** 530 * As JSON form 531 * 532 * @return the JSON version of the section 533 */ 534 public JsonObject asJsonObject() { 535 synchronized (handleLock) { 536 return this.jsonHandle; 537 } 538 } 539 540 /** 541 * The JSON root from the parent 542 * 543 * @return the parent root section 544 */ 545 public Config rootSection() { 546 synchronized (parentLock) { 547 return this.parent.rootSection(); 548 } 549 } 550 551 /** 552 * The section parent 553 * 554 * @return the parent of the section 555 */ 556 public ConfigSection parentSection() { 557 synchronized (parentLock) { 558 return this.parent; 559 } 560 } 561 562 /** 563 * Gets a sub section which has the current section as a parent 564 * 565 * <p>A new section is created if it does not exist</p> 566 * 567 * @param tag the tag to get the section from 568 * @return the section with the given tag under this section 569 */ 570 public ConfigSection getConfigSection(String tag) { 571 if (this.contains(tag)) { 572 return new ConfigSection(this, this.jsonHandle.get(tag).getAsJsonObject()); 573 } else { 574 this.jsonHandle.add(tag, new JsonObject()); 575 return new ConfigSection(this, this.jsonHandle.get(tag).getAsJsonObject()); 576 } 577 } 578 579 /** 580 * Returns all of the topmost keys. Will not have inner section keys. 581 * 582 * @return the config keys 583 */ 584 public Set<String> keys() { 585 Set<Map.Entry<String, JsonElement>> entries = entries(); 586 587 Set<String> keys = Sets.newHashSet(); 588 keys.addAll(entries.stream().map(Map.Entry::getKey).collect(Collectors.toList())); 589 590 return keys; 591 } 592 593 /** 594 * Returns the topmost values 595 * 596 * @return the values 597 */ 598 public Collection<JsonElement> values() { 599 Set<Map.Entry<String, JsonElement>> entries = entries(); 600 601 List<JsonElement> values = Lists.newArrayList(); 602 values.addAll(entries.stream().map(Map.Entry::getValue).collect(Collectors.toList())); 603 604 return values; 605 } 606 607 /** 608 * Obtains the set of the topmost key-value entries 609 * 610 * @return the key-value entry set 611 */ 612 public Set<Map.Entry<String, JsonElement>> entries() { 613 synchronized (handleLock) { 614 return jsonHandle.entrySet(); 615 } 616 } 617 618 /** 619 * Saves the parent data 620 */ 621 public void save() { 622 synchronized (parentLock) { 623 this.parent.save(); 624 } 625 } 626}