commit 0303da531ef24f0d05e9cd64e9e45610b2c0eb89 Author: Alexander Söderberg Date: Sun Aug 30 20:53:00 2020 +0200 Initial commit diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..0a682d52 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: Sauilitired +patreon: IntellectualSites # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://paypal.me/Sauilitired']# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..76e22beb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "maven" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "daily" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..8eeebdca --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,54 @@ +name: "CodeQL" + +on: + push: + branches: [master, ] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 18 * * 3' + +jobs: + analyse: + name: Analyse + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + with: + languages: java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..c8aeb3a2 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,24 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7e2eb2bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,213 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all,maven,eclipse,java,git +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all,maven,eclipse,java,git + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +# Uncomment this line if you wish to ignore the project description file. +# Typically, this file would be tracked if it contains build/dependency configurations: +#.project + +### Eclipse Patch ### +# Spring Boot Tooling +.sts4-cache/ + +### Git ### +# Created by git for backups. To disable backups in Git: +# $ git config --global mergetool.keepBackup false +*.orig + +# Created by git when using merge tools for conflicts +*.BACKUP.* +*.BASE.* +*.LOCAL.* +*.REMOTE.* +*_BACKUP_*.txt +*_BASE_*.txt +*_LOCAL_*.txt +*_REMOTE_*.txt + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,maven,eclipse,java,git diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..becafcf5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 IntellectualSites + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..5211bac2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,132 @@ + + + 4.0.0 + + com.intellectualsites + Commands + 1.0-SNAPSHOT + jar + 2020 + + + MIT License + https://raw.githubusercontent.com/Sauilitired/Commands/master/LICENSE + + + Commands + Commands for Java + + 1.8 + UTF-8 + + + + intellectualsites + IntellectualSites + https://mvn.intellectualsites.com/content/repositories/releases + + + intellectualsites-snapshots + IntellectualSites Snapshots + https://mvn.intellectualsites.com/content/repositories/snapshots + + + + clean package + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + + + default-deploy + deploy + + deploy + + + + + intellectualsites + https://mvn.intellectualsites.com/ + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + + + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + test + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + com.google.guava + guava + 29.0-jre + + + diff --git a/src/main/java/com/intellectualsites/commands/Command.java b/src/main/java/com/intellectualsites/commands/Command.java new file mode 100644 index 00000000..d5879b9c --- /dev/null +++ b/src/main/java/com/intellectualsites/commands/Command.java @@ -0,0 +1,85 @@ +// +// MIT License +// +// Copyright (c) 2020 IntellectualSites +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands; + +import com.intellectualsites.commands.components.CommandComponent; + +import javax.annotation.Nonnull; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * A command consists out of a chain of {@link com.intellectualsites.commands.components.CommandComponent command components}. + */ +public class Command { + + private final CommandComponent[] components; + + private Command(@Nonnull final CommandComponent[] commandComponents) { + this.components = Objects.requireNonNull(commandComponents, "Command components may not be null"); + if (this.components.length == 0){ + throw new IllegalArgumentException("At least one command component is required"); + } + // Enforce ordering of command components + boolean foundOptional = false; + for (final CommandComponent component : this.components) { + if (foundOptional && component.isRequired()) { + throw new IllegalArgumentException(String.format("Command component '%s' cannot be placed after an optional component", component.getName())); + } else if (!component.isRequired()) { + foundOptional = true; + } + } + } + + /** + * Return a copy of the command component array + * + * @return Copy of the command component array + */ + @Nonnull public CommandComponent[] getComponents() { + final CommandComponent[] commandComponents = new CommandComponent[this.components.length]; + System.arraycopy(this.components, 0, commandComponents, 0, this.components.length); + return commandComponents; + } + + /** + * Get the longest chain of similar components for + * two commands + * + * @return List containing the longest shared component chain + */ + public List> getSharedComponentChain(@Nonnull final Command other) { + final List> commandComponents = new LinkedList<>(); + for (int i = 0; i < this.components.length && i < other.components.length; i++) { + if (this.components[i].equals(other.components[i])) { + commandComponents.add(this.components[i]); + } else { + break; + } + } + return commandComponents; + } + +} diff --git a/src/main/java/com/intellectualsites/commands/CommandTree.java b/src/main/java/com/intellectualsites/commands/CommandTree.java new file mode 100644 index 00000000..9847171d --- /dev/null +++ b/src/main/java/com/intellectualsites/commands/CommandTree.java @@ -0,0 +1,155 @@ +package com.intellectualsites.commands; + +import com.google.common.base.Objects; +import com.intellectualsites.commands.components.CommandComponent; +import com.intellectualsites.commands.components.StaticComponent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Tree containing all commands and command paths + */ +public class CommandTree { + + private final Node> internalTree = new Node<>(null); + + private CommandTree() { + } + + /** + * Create a new command tree instance + * + * @return New command tree + */ + @Nonnull public static CommandTree newTree() { + return new CommandTree(); + } + + /** + * Insert a new command into the command tree + * + * @param command Command to insert + */ + public void insertCommand(@Nonnull final Command command) { + Node> node = this.internalTree; + for (final CommandComponent component : command.getComponents()) { + Node> tempNode = node.getChild(component); + if (tempNode == null) { + tempNode = node.addChild(component); + } + node = tempNode; + } + if (node.getValue() != null) { + node.getValue().setOwningCommand(command); + } + } + + /** + * Go through all commands and register them, and verify the + * command tree contracts + */ + public void verifyAndRegister() { + // All top level commands are supposed to be registered in the command manager + this.internalTree.children.stream().map(Node::getValue).forEach(commandComponent -> { + if (!(commandComponent instanceof StaticComponent)) { + throw new IllegalStateException("Top level command component cannot be a variable"); + } + // TODO: Register in the command handler + }); + this.checkAmbiguity(this.internalTree); + // Verify that all leaf nodes have command registered + this.getLeaves(this.internalTree).forEach(leaf -> { + if (leaf.getOwningCommand() == null) { + // TODO: Custom exception type + throw new IllegalStateException("Leaf node does not have associated owning command"); + } + }); + } + + private void checkAmbiguity(@Nonnull final Node> node) { + if (node.isLeaf()) { + return; + } + final int size = node.children.size(); + for (final Node> child : node.children) { + if (child.getValue() != null && !child.getValue().isRequired() && size > 1) { + // TODO: Use a custom exception type here + throw new IllegalStateException("Ambiguous command node found: " + node.getValue()); + } + } + node.children.forEach(this::checkAmbiguity); + } + + private List> getLeaves(@Nonnull final Node> node) { + final List> leaves = new LinkedList<>(); + if (node.isLeaf()) { + if (node.getValue() != null) { + leaves.add(node.getValue()); + } + } else { + node.children.forEach(child -> leaves.addAll(getLeaves(child))); + } + return leaves; + } + + + private static final class Node { + + private final List> children = new LinkedList<>(); + private final T value; + + private Node(@Nullable final T value) { + this.value = value; + } + + private List> getChildren() { + return Collections.unmodifiableList(this.children); + } + + @Nonnull private Node addChild(@Nonnull final T child) { + final Node node = new Node<>(child); + this.children.add(node); + return node; + } + + @Nullable private Node getChild(@Nonnull final T type) { + for (final Node child : this.children) { + if (type.equals(child.getValue())) { + return child; + } + } + return null; + } + + private boolean isLeaf() { + return this.children.isEmpty(); + } + + @Nullable public T getValue() { + return this.value; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final Node node = (Node) o; + return Objects.equal(getChildren(), node.getChildren()) && + Objects.equal(getValue(), node.getValue()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getChildren(), getValue()); + } + } + +} diff --git a/src/main/java/com/intellectualsites/commands/components/CommandComponent.java b/src/main/java/com/intellectualsites/commands/components/CommandComponent.java new file mode 100644 index 00000000..2d9b37dd --- /dev/null +++ b/src/main/java/com/intellectualsites/commands/components/CommandComponent.java @@ -0,0 +1,226 @@ +// +// MIT License +// +// Copyright (c) 2020 IntellectualSites +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands.components; + +import com.intellectualsites.commands.Command; +import com.intellectualsites.commands.parser.ComponentParser; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * A component that belongs to a command + * + * @param The type that the component parses into + */ +public class CommandComponent { + + private static final Pattern NAME_PATTERN = Pattern.compile("[A-Za-z0-9]+"); + + /** + * Indicates whether or not the component is required + * or not. All components prior to any other required + * component must also be required, such that the predicate + * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, + * where {c_0, ..., c_n-1} is the set of command components. + */ + private final boolean required; + /** + * The command component name. This might be exposed + * to command senders and so should be choosen carefully. + */ + private final String name; + /** + * The parser that is used to parse the command input + * into the corresponding command type + */ + private final ComponentParser parser; + + private Command owningCommand; + + CommandComponent(final boolean required, @Nonnull final String name, + @Nonnull final ComponentParser parser) { + this.required = required; + this.name = Objects.requireNonNull(name, "Name may not be null"); + if (!NAME_PATTERN.asPredicate().test(name)) { + throw new IllegalArgumentException("Name must be alphanumeric"); + } + this.parser = Objects.requireNonNull(parser, "Parser may not be null"); + } + + /** + * Create a new command component + * + * @param clazz Argument class + * @param Argument Type + * @return Component builder + */ + @Nonnull public static CommandComponent.Builder ofType(@Nonnull final Class clazz) { + return new Builder<>(); + } + + /** + * Check whether or not the command component is required + * + * @return {@code true} if the component is required, {@code false} if not + */ + public boolean isRequired() { + return this.required; + } + + /** + * Get the command component name; + * + * @return Component name + */ + @Nonnull public String getName() { + return this.name; + } + + /** + * Get the parser that is used to parse the command input + * into the corresponding command type + * + * @return Command parser + */ + @Nonnull public ComponentParser getParser() { + return this.parser; + } + + @Nonnull @Override public String toString() { + return String.format("CommandComponent{name=%s}", this.name); + } + + /** + * Get the owning command + * + * @return Owning command + */ + @Nullable public Command getOwningCommand() { + return this.owningCommand; + } + + /** + * Set the owning command + * + * @param owningCommand Owning command + */ + public void setOwningCommand(@Nonnull final Command owningCommand) { + if (this.owningCommand != null) { + throw new IllegalStateException("Cannot replace owning command"); + } + this.owningCommand = owningCommand; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final CommandComponent that = (CommandComponent) o; + return isRequired() == that.isRequired() && com.google.common.base.Objects.equal(getName(), that.getName()); + } + + @Override + public int hashCode() { + return com.google.common.base.Objects.hashCode(isRequired(), getName()); + } + + + public static class Builder { + + private String name; + private boolean required = true; + private ComponentParser parser; + + private Builder() { + } + + /** + * Set the component name. Must be alphanumeric + * + * @param name Alphanumeric component name + * @return Builder instance + */ + @Nonnull public Builder named(@Nonnull final String name) { + this.name = name; + return this; + } + + /** + * Indicates that the component is required. + * All components prior to any other required + * component must also be required, such that the predicate + * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, + * where {c_0, ..., c_n-1} is the set of command components. + * + * @return Builder instance + */ + @Nonnull public Builder asRequired() { + this.required = true; + return this; + } + + /** + * Indicates that the component is optional. + * All components prior to any other required + * component must also be required, such that the predicate + * (∀ c_i ∈ required)({c_0, ..., c_i-1} ⊂ required) holds true, + * where {c_0, ..., c_n-1} is the set of command components. + * + * @return Builder instance + */ + @Nonnull public Builder asOptional() { + this.required = false; + return this; + } + + /** + * Set the component parser + * + * @param parser Component parser + * @return Builder instance + */ + @Nonnull public Builder withParser(@Nonnull final ComponentParser parser) { + this.parser = Objects.requireNonNull(parser, "Parser may not be null"); + return this; + } + + /** + * Construct a command component from the builder settings + * + * @return Constructed component + */ + @Nonnull public CommandComponent build() { + return new CommandComponent<>(this.required, this.name, this.parser); + } + + } + +} diff --git a/src/main/java/com/intellectualsites/commands/components/StaticComponent.java b/src/main/java/com/intellectualsites/commands/components/StaticComponent.java new file mode 100644 index 00000000..e3c46f78 --- /dev/null +++ b/src/main/java/com/intellectualsites/commands/components/StaticComponent.java @@ -0,0 +1,44 @@ +// +// MIT License +// +// Copyright (c) 2020 IntellectualSites +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands.components; + +import com.intellectualsites.commands.parser.ComponentParser; + +import javax.annotation.Nonnull; + +public final class StaticComponent extends CommandComponent { + + private StaticComponent(final boolean required, @Nonnull final String name, @Nonnull final String ... aliases) { + super(required, name, new StaticComponentParser(name, aliases)); + } + + + private static final class StaticComponentParser implements ComponentParser { + + private StaticComponentParser(@Nonnull final String name, @Nonnull final String ... aliases) { + } + + } + +} diff --git a/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java b/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java new file mode 100644 index 00000000..57b9b16e --- /dev/null +++ b/src/main/java/com/intellectualsites/commands/parser/ComponentParser.java @@ -0,0 +1,27 @@ +// +// MIT License +// +// Copyright (c) 2020 IntellectualSites +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands.parser; + +public interface ComponentParser { +}