diff --git a/docs/commands.md b/docs/commands.md
index 70a752d3..ba38674d 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -1,5 +1,5 @@
-
+
## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
@@ -7,74 +7,72 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
- **/authme**: The main AuthMeReloaded command. The root for all admin commands.
- **/authme register** <player> <password>: Register the specified player with the specified password.
-
Requires `authme.admin.register`
+
Requires `authme.admin.register`
- **/authme unregister** <player>: Unregister the specified player.
-
Requires `authme.admin.unregister`
+
Requires `authme.admin.unregister`
- **/authme forcelogin** [player]: Enforce the specified player to login.
-
Requires `authme.player.canbeforced`
+
Requires `authme.admin.forcelogin`
- **/authme password** <player> <pwd>: Change the password of a player.
-
Requires `authme.admin.changepassword`
+
Requires `authme.admin.changepassword`
- **/authme lastlogin** [player]: View the date of the specified players last login.
-
Requires `authme.admin.lastlogin`
+
Requires `authme.admin.lastlogin`
- **/authme accounts** [player]: Display all accounts of a player by his player name or IP.
-
Requires `authme.admin.accounts`
+
Requires `authme.admin.accounts`
- **/authme email** [player]: Display the email address of the specified player if set.
-
Requires `authme.admin.getemail`
+
Requires `authme.admin.getemail`
- **/authme setemail** <player> <email>: Change the email address of the specified player.
-
Requires `authme.admin.changemail`
+
Requires `authme.admin.changemail`
- **/authme getip** <player>: Get the IP address of the specified online player.
-
Requires `authme.admin.getip`
-- **/authme spawn** <player>: Teleport to the spawn.
-
Requires `authme.admin.spawn`
+
Requires `authme.admin.getip`
+- **/authme spawn**: Teleport to the spawn.
+
Requires `authme.admin.spawn`
- **/authme setspawn**: Change the player's spawn to your current position.
-
Requires `authme.admin.setspawn`
+
Requires `authme.admin.setspawn`
- **/authme firstspawn**: Teleport to the first spawn.
-
Requires `authme.admin.firstspawn`
+
Requires `authme.admin.firstspawn`
- **/authme setfirstspawn**: Change the first player's spawn to your current position.
-
Requires `authme.admin.setfirstspawn`
+
Requires `authme.admin.setfirstspawn`
- **/authme purge** <days>: Purge old AuthMeReloaded data longer than the specified amount of days ago.
-
Requires `authme.admin.purge`
+
Requires `authme.admin.purge`
- **/authme resetpos** <player/*>: Purge the last know position of the specified player or all of them.
-
Requires `authme.admin.purgelastpos`
+
Requires `authme.admin.purgelastpos`
- **/authme purgebannedplayers**: Purge all AuthMeReloaded data for banned players.
-
Requires `authme.admin.purgebannedplayers`
+
Requires `authme.admin.purgebannedplayers`
- **/authme switchantibot** [mode]: Switch or toggle the AntiBot mode to the specified state.
-
Requires `authme.admin.switchantibot`
+
Requires `authme.admin.switchantibot`
- **/authme reload**: Reload the AuthMeReloaded plugin.
-
Requires `authme.admin.reload`
+
Requires `authme.admin.reload`
- **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license.
+- **/authme converter** <job>: Converter command for AuthMeReloaded.
+
Requires `authme.admin.converter`
- **/authme help** [query]: View detailed help for /authme commands.
- **/login** <password>: Command to log in using AuthMeReloaded.
-
Requires `authme.player.login`
+
Requires `authme.player.login`
- **/login help** [query]: View detailed help for /login commands.
- **/logout**: Command to logout using AuthMeReloaded.
-
Requires `authme.player.logout`
+
Requires `authme.player.logout`
- **/logout help** [query]: View detailed help for /logout commands.
- **/register** [password] [verifyPassword]: Command to register using AuthMeReloaded.
-
Requires `authme.player.register`
+
Requires `authme.player.register`
- **/register help** [query]: View detailed help for /register commands.
- **/unreg** <password>: Command to unregister using AuthMeReloaded.
-
Requires `authme.player.unregister`
+
Requires `authme.player.unregister`
- **/unreg help** [query]: View detailed help for /unreg commands.
- **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
-
Requires `authme.player.changepassword`
+
Requires `authme.player.changepassword`
- **/changepassword help** [query]: View detailed help for /changepassword commands.
- **/email**: The AuthMeReloaded Email command base.
- **/email add** <email> <verifyEmail>: Add a new email address to your account.
-
Requires `authme.player.email.add`
+
Requires `authme.player.email.add`
- **/email change** <oldEmail> <newEmail>: Change an email address of your account.
-
Requires `authme.player.email.change`
+
Requires `authme.player.email.change`
- **/email recover** <email>: Recover your account using an Email address by sending a mail containing a new password.
-
Requires `authme.player.email.recover`
+
Requires `authme.player.email.recover`
- **/email help** [query]: View detailed help for /email commands.
- **/captcha** <captcha>: Captcha command for AuthMeReloaded.
-
Requires `authme.player.captcha`
+
Requires `authme.player.captcha`
- **/captcha help** [query]: View detailed help for /captcha commands.
-- **/converter** <job>: Converter command for AuthMeReloaded.
-
Requires `authme.admin.converter`
-- **/converter help** [query]: View detailed help for /converter commands.
-
---
-This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Sun Feb 14 19:00:30 CET 2016
+This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:20 CEST 2016
diff --git a/docs/hash_algorithms.md b/docs/hash_algorithms.md
index a8ba9c6f..6bc2a24f 100644
--- a/docs/hash_algorithms.md
+++ b/docs/hash_algorithms.md
@@ -1,5 +1,5 @@
-
+
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.
@@ -17,7 +17,7 @@ JOOMLA | Recommended | 65 | | | Text | 32 |
MD5 | Do not use | 32 | | | None | |
MD5VB | Acceptable | 56 | | | Text | 16 |
MYBB | Acceptable | 32 | | | Text | 8 | Y
-PBKDF2 | Does not work | 330 | | | Text | 12 |
+PBKDF2 | Does not work | 328 | | | Text | 12 |
PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 |
PHPBB | Acceptable | 34 | | | Text | 16 |
PHPFUSION | Do not use | 64 | Y | | | | Y
@@ -82,4 +82,4 @@ or bad.
---
-This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Sun Feb 14 19:00:32 CET 2016
+This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:22 CEST 2016
diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md
index 457f1102..0591b97a 100644
--- a/docs/permission_nodes.md
+++ b/docs/permission_nodes.md
@@ -1,5 +1,5 @@
-
+
## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds.
@@ -42,7 +42,6 @@ The following are the permission nodes that are currently supported by the lates
- **authme.player.unregister** – Command permission to unregister.
- **authme.vip** – Permission node to identify VIP users.
-
---
-This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Sun Feb 14 19:00:34 CET 2016
+This page was automatically generated on the [AuthMe-Team/AuthMeReloaded repository](https://github.com/AuthMe-Team/AuthMeReloaded/tree/master/docs/) on Thu Apr 07 17:17:24 CEST 2016
diff --git a/src/tools/commands/CommandPageCreater.java b/src/tools/commands/CommandPageCreater.java
index 56d41722..8d92dbfb 100644
--- a/src/tools/commands/CommandPageCreater.java
+++ b/src/tools/commands/CommandPageCreater.java
@@ -1,6 +1,5 @@
package commands;
-import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.command.CommandArgumentDescription;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.command.CommandInitializer;
@@ -8,12 +7,12 @@ import fr.xephi.authme.command.CommandPermissions;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.permission.PermissionNode;
import utils.FileUtils;
-import utils.TagReplacer;
+import utils.TagValue.NestedTagValue;
+import utils.TagValueHolder;
import utils.ToolTask;
import utils.ToolsConstants;
import java.util.Collection;
-import java.util.Map;
import java.util.Scanner;
import java.util.Set;
@@ -29,31 +28,27 @@ public class CommandPageCreater implements ToolTask {
@Override
public void execute(Scanner scanner) {
final Set baseCommands = CommandInitializer.buildCommands();
- final String template = FileUtils.readFromFile(ToolsConstants.TOOLS_SOURCE_ROOT
- + "commands/command_entry.tpl.md");
-
- StringBuilder commandsResult = new StringBuilder();
- addCommandsInfo(commandsResult, baseCommands, template);
+ NestedTagValue commandTags = new NestedTagValue();
+ addCommandsInfo(commandTags, baseCommands);
FileUtils.generateFileFromTemplate(
ToolsConstants.TOOLS_SOURCE_ROOT + "commands/commands.tpl.md",
OUTPUT_FILE,
- ImmutableMap.of("commands", commandsResult.toString()));
+ TagValueHolder.create().put("commands", commandTags));
System.out.println("Wrote to '" + OUTPUT_FILE + "' with " + baseCommands.size() + " base commands.");
}
- private static void addCommandsInfo(StringBuilder sb, Collection commands,
- final String template) {
+ private static void addCommandsInfo(NestedTagValue commandTags, Collection commands) {
for (CommandDescription command : commands) {
- Map tags = ImmutableMap.of(
- "command", CommandUtils.constructCommandPath(command),
- "description", command.getDetailedDescription(),
- "arguments", formatArguments(command.getArguments()),
- "permissions", formatPermissions(command.getCommandPermissions()));
- sb.append(TagReplacer.applyReplacements(template, tags));
+ TagValueHolder tags = TagValueHolder.create()
+ .put("command", CommandUtils.constructCommandPath(command))
+ .put("description", command.getDetailedDescription())
+ .put("arguments", formatArguments(command.getArguments()))
+ .put("permissions", formatPermissions(command.getCommandPermissions()));
+ commandTags.add(tags);
if (!command.getChildren().isEmpty()) {
- addCommandsInfo(sb, command.getChildren(), template);
+ addCommandsInfo(commandTags, command.getChildren());
}
}
}
diff --git a/src/tools/commands/command_entry.tpl.md b/src/tools/commands/command_entry.tpl.md
deleted file mode 100644
index c2978484..00000000
--- a/src/tools/commands/command_entry.tpl.md
+++ /dev/null
@@ -1,2 +0,0 @@
-- **{command}**{arguments}: {description}[permissions]
-
Requires `{permissions}`[/permissions]
diff --git a/src/tools/commands/commands.tpl.md b/src/tools/commands/commands.tpl.md
index 0815e6ce..c0f4da12 100644
--- a/src/tools/commands/commands.tpl.md
+++ b/src/tools/commands/commands.tpl.md
@@ -5,6 +5,9 @@
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
brackets; optional arguments are enclosed in square brackets (`[ ]`).
-{commands}
+[#commands]
+ - **{command}**{arguments}: {description}[permissions]
+
Requires `{permissions}`[/permissions]
+[/#commands]
{gen_footer}
diff --git a/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java b/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java
index 129242b3..8c8cecbe 100644
--- a/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java
+++ b/src/tools/hashmethods/HashAlgorithmsDescriptionTask.java
@@ -1,11 +1,11 @@
package hashmethods;
-import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
import utils.FileUtils;
-import utils.TagReplacer;
+import utils.TagValue.NestedTagValue;
+import utils.TagValueHolder;
import utils.ToolTask;
import utils.ToolsConstants;
@@ -33,30 +33,28 @@ public class HashAlgorithmsDescriptionTask implements ToolTask {
// Gather info and construct a row for each method
EncryptionMethodInfoGatherer infoGatherer = new EncryptionMethodInfoGatherer();
Map descriptions = infoGatherer.getDescriptions();
- final String methodRows = constructMethodRows(descriptions);
+ final NestedTagValue methodRows = constructMethodRows(descriptions);
// Write to the docs file
- Map tags = ImmutableMap.of("method_rows", methodRows);
+ TagValueHolder tags = TagValueHolder.create().put("algorithms", methodRows);
FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags);
}
- private static String constructMethodRows(Map descriptions) {
- final String rowTemplate = FileUtils.readFromFile(CUR_FOLDER + "hash_algorithms_row.tpl.md");
- StringBuilder result = new StringBuilder();
+ private static NestedTagValue constructMethodRows(Map descriptions) {
+ NestedTagValue methodTags = new NestedTagValue();
for (Map.Entry entry : descriptions.entrySet()) {
MethodDescription description = entry.getValue();
- Map tags = ImmutableMap.builder()
+ TagValueHolder tags = TagValueHolder.create()
.put("name", asString(entry.getKey()))
.put("recommendation", asString(description.getUsage()))
.put("hash_length", asString(description.getHashLength()))
.put("ascii_restricted", asString(description.isAsciiRestricted()))
.put("salt_type", asString(description.getSaltType()))
.put("salt_length", asString(description.getSaltLength()))
- .put("separate_salt", asString(description.hasSeparateSalt()))
- .build();
- result.append(TagReplacer.applyReplacements(rowTemplate, tags));
+ .put("separate_salt", asString(description.hasSeparateSalt()));
+ methodTags.add(tags);
}
- return result.toString();
+ return methodTags;
}
@Override
diff --git a/src/tools/hashmethods/hash_algorithms.tpl.md b/src/tools/hashmethods/hash_algorithms.tpl.md
index 883ae323..73e85ba0 100644
--- a/src/tools/hashmethods/hash_algorithms.tpl.md
+++ b/src/tools/hashmethods/hash_algorithms.tpl.md
@@ -7,7 +7,10 @@ AuthMe supports the following hash algorithms for storing your passwords safely.
Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
-{method_rows}CUSTOM | | | | | | | |
+[#algorithms]
+{name} | {recommendation} | {hash_length} | {ascii_restricted} | | {salt_type} | {salt_length} | {separate_salt}
+[/#algorithms]
+CUSTOM | | | | | | | |
diff --git a/src/tools/hashmethods/hash_algorithms_row.tpl.md b/src/tools/hashmethods/hash_algorithms_row.tpl.md
deleted file mode 100644
index 411d1127..00000000
--- a/src/tools/hashmethods/hash_algorithms_row.tpl.md
+++ /dev/null
@@ -1 +0,0 @@
-{name} | {recommendation} | {hash_length} | {ascii_restricted} | | {salt_type} | {salt_length} | {separate_salt}
diff --git a/src/tools/permissions/PermissionsListWriter.java b/src/tools/permissions/PermissionsListWriter.java
index 7c599994..dc16cea2 100644
--- a/src/tools/permissions/PermissionsListWriter.java
+++ b/src/tools/permissions/PermissionsListWriter.java
@@ -1,8 +1,8 @@
package permissions;
-import com.google.common.collect.ImmutableMap;
import utils.FileUtils;
-import utils.TagReplacer;
+import utils.TagValue.NestedTagValue;
+import utils.TagValueHolder;
import utils.ToolTask;
import utils.ToolsConstants;
@@ -45,29 +45,25 @@ public class PermissionsListWriter implements ToolTask {
}
private static void generateAndWriteFile() {
- final String permissionsTagValue = generatePermissionsList();
+ final NestedTagValue permissionsTagValue = generatePermissionsList();
- Map tags = ImmutableMap.of("permissions", permissionsTagValue);
+ TagValueHolder tags = TagValueHolder.create().put("nodes", permissionsTagValue);
FileUtils.generateFileFromTemplate(
ToolsConstants.TOOLS_SOURCE_ROOT + "permissions/permission_nodes.tpl.md", PERMISSIONS_OUTPUT_FILE, tags);
System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'");
System.out.println("Before committing, please verify the output!");
}
- private static String generatePermissionsList() {
+ private static NestedTagValue generatePermissionsList() {
PermissionNodesGatherer gatherer = new PermissionNodesGatherer();
Map permissions = gatherer.gatherNodesWithJavaDoc();
-
- final String template = FileUtils.readFromToolsFile("permissions/permission_node_entry.tpl.md");
- StringBuilder sb = new StringBuilder();
-
+ NestedTagValue permissionTags = new NestedTagValue();
for (Map.Entry entry : permissions.entrySet()) {
- Map tags = ImmutableMap.of(
- "node", entry.getKey(),
- "description", entry.getValue());
- sb.append(TagReplacer.applyReplacements(template, tags));
+ permissionTags.add(TagValueHolder.create()
+ .put("node", entry.getKey())
+ .put("description", entry.getValue()));
}
- return sb.toString();
+ return permissionTags;
}
private static void outputSimpleList() {
diff --git a/src/tools/permissions/permission_node_entry.tpl.md b/src/tools/permissions/permission_node_entry.tpl.md
deleted file mode 100644
index ab7f0687..00000000
--- a/src/tools/permissions/permission_node_entry.tpl.md
+++ /dev/null
@@ -1 +0,0 @@
-- **{node}** – {description}
diff --git a/src/tools/permissions/permission_nodes.tpl.md b/src/tools/permissions/permission_nodes.tpl.md
index 1f420809..8d2a2b4f 100644
--- a/src/tools/permissions/permission_nodes.tpl.md
+++ b/src/tools/permissions/permission_nodes.tpl.md
@@ -4,6 +4,8 @@
## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds.
-{permissions}
+[#nodes]
+ - **{node}** – {description}
+[/#nodes]
{gen_footer}
diff --git a/src/tools/utils/FileUtils.java b/src/tools/utils/FileUtils.java
index 02b9eddd..853965e9 100644
--- a/src/tools/utils/FileUtils.java
+++ b/src/tools/utils/FileUtils.java
@@ -6,7 +6,6 @@ import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
-import java.util.Map;
/**
* Utility class for reading from and writing to files.
@@ -18,7 +17,7 @@ public final class FileUtils {
private FileUtils() {
}
- public static void generateFileFromTemplate(String templateFile, String destinationFile, Map tags) {
+ public static void generateFileFromTemplate(String templateFile, String destinationFile, TagValueHolder tags) {
String template = readFromFile(templateFile);
String result = TagReplacer.applyReplacements(template, tags);
writeToFile(destinationFile, result);
@@ -56,8 +55,4 @@ public final class FileUtils {
}
}
- public static String readFromToolsFile(String file) {
- return readFromFile(ToolsConstants.TOOLS_SOURCE_ROOT + file);
- }
-
}
diff --git a/src/tools/utils/TagReplacer.java b/src/tools/utils/TagReplacer.java
index 8e75ecd0..1197e0e7 100644
--- a/src/tools/utils/TagReplacer.java
+++ b/src/tools/utils/TagReplacer.java
@@ -1,6 +1,7 @@
package utils;
-import fr.xephi.authme.util.StringUtils;
+import utils.TagValue.NestedTagValue;
+import utils.TagValue.TextTagValue;
import java.util.Date;
import java.util.Map;
@@ -26,18 +27,25 @@ public class TagReplacer {
* Replace a template with default tags and custom ones supplied by a map.
*
* @param template The template to process
- * @param tags Map with additional tags, e.g. a map entry with key "foo" and value "bar" will replace
+ * @param tagValues Map with additional tags, e.g. a map entry with key "foo" and value "bar" will replace
* any occurrences of "{foo}" to "bar".
* @return The filled template
*/
- public static String applyReplacements(String template, Map tags) {
+ public static String applyReplacements(String template, TagValueHolder tagValues) {
String result = template;
- for (Map.Entry tagRule : tags.entrySet()) {
+ for (Map.Entry> tagRule : tagValues.getValues().entrySet()) {
final String name = tagRule.getKey();
- final String value = tagRule.getValue();
- result = replaceOptionalTag(result, name, value)
- .replace("{" + name + "}", value);
+ if (tagRule.getValue() instanceof TextTagValue) {
+ final TextTagValue value = (TextTagValue) tagRule.getValue();
+ result = replaceOptionalTag(result, name, value)
+ .replace("{" + name + "}", value.getValue());
+ } else if (tagRule.getValue() instanceof NestedTagValue) {
+ final NestedTagValue value = (NestedTagValue) tagRule.getValue();
+ result = replaceIterateTag(replaceOptionalTag(result, name, value), name, value);
+ } else {
+ throw new IllegalStateException("Unknown tag value type");
+ }
}
return applyReplacements(result);
}
@@ -58,14 +66,14 @@ public class TagReplacer {
+ " on " + curDate);
}
- private static String replaceOptionalTag(String text, String tagName, String tagValue) {
+ private static String replaceOptionalTag(String text, String tagName, TagValue> tagValue) {
Pattern regex = Pattern.compile("\\[" + tagName + "](.*?)\\[/" + tagName + "]", Pattern.DOTALL);
Matcher matcher = regex.matcher(text);
if (!matcher.find()) {
// Couldn't find results, so just return text as it is
return text;
- } else if (StringUtils.isEmpty(tagValue)) {
+ } else if (tagValue.isEmpty()) {
// Tag is empty, replace [tagName]some_text[/tagName] to nothing
return matcher.replaceAll("");
} else {
@@ -74,5 +82,30 @@ public class TagReplacer {
}
}
+ /**
+ * Replace iterating tags with the value. Tags of the type [#tag]...[/#tag] specify to iterate over the
+ * entries in {@link NestedTagValue} and to apply any replacements in there.
+ *
+ * @param text The file text
+ * @param tagName The tag name to handle
+ * @param tagValue The associated value
+ * @return The text with the applied replacement
+ */
+ private static String replaceIterateTag(String text, String tagName, NestedTagValue tagValue) {
+ Pattern regex = Pattern.compile("\\[#" + tagName + "](.*?)\\[/#" + tagName + "]\\s?", Pattern.DOTALL);
+ Matcher matcher = regex.matcher(text);
+
+ if (!matcher.find()) {
+ return text;
+ } else if (tagValue.isEmpty()) {
+ return matcher.replaceAll("");
+ }
+ final String innerTemplate = matcher.group(1).trim() + "\n";
+ String result = "";
+ for (TagValueHolder entry : tagValue.getValue()) {
+ result += applyReplacements(innerTemplate, entry);
+ }
+ return matcher.replaceAll(result);
+ }
}
diff --git a/src/tools/utils/TagValue.java b/src/tools/utils/TagValue.java
new file mode 100644
index 00000000..03b34097
--- /dev/null
+++ b/src/tools/utils/TagValue.java
@@ -0,0 +1,51 @@
+package utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public abstract class TagValue {
+
+ private final T value;
+
+ public TagValue(T value) {
+ this.value = value;
+ }
+
+ public T getValue() {
+ return value;
+ }
+
+ public abstract boolean isEmpty();
+
+ public static final class TextTagValue extends TagValue {
+ public TextTagValue(String value) {
+ super(value);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getValue().isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+
+ public static final class NestedTagValue extends TagValue> {
+ public NestedTagValue() {
+ super(new ArrayList());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return getValue().isEmpty();
+ }
+
+ public void add(TagValueHolder entry) {
+ getValue().add(entry);
+ }
+ }
+}
diff --git a/src/tools/utils/TagValueHolder.java b/src/tools/utils/TagValueHolder.java
new file mode 100644
index 00000000..e2bd09b0
--- /dev/null
+++ b/src/tools/utils/TagValueHolder.java
@@ -0,0 +1,34 @@
+package utils;
+
+import utils.TagValue.TextTagValue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class TagValueHolder {
+
+ private Map> values;
+
+ private TagValueHolder() {
+ this.values = new HashMap<>();
+ }
+
+ public static TagValueHolder create() {
+ return new TagValueHolder();
+ }
+
+ public TagValueHolder put(String key, TagValue> value) {
+ values.put(key, value);
+ return this;
+ }
+
+ public TagValueHolder put(String key, String value) {
+ values.put(key, new TextTagValue(value));
+ return this;
+ }
+
+ public Map> getValues() {
+ return values;
+ }
+}