Citadel v2 & Command Pattern

When I was in charge of implementing Version 2 of Citadel I was in charge of creating groups which allowed players in the group access reinforcements.

I also introduced the Command Pattern (which was later adopted by many plugin developers to easily manage the commands from the user’s console).

First we have to create the CommandHandler:

public class CommandHandler {

	private Map<String, Command> commands = new LinkedHashMap<String, Command>();
	private Map<String, Command> identifiers = new HashMap<String, Command>();

	public void addCommand(Command command){
		this.commands.put(command.getName().toLowerCase(), command);
		for(String ident : command.getIdentifiers()){
			this.identifiers.put(ident.toLowerCase(), command);
		}
	}

	public boolean dispatch(CommandSender sender, String label, String[] args){
		for(int argsIncluded = args.length; argsIncluded >= 0; argsIncluded--){
			StringBuilder identifier = new StringBuilder(label);
			for(int i = 0; i < argsIncluded; i++){
				identifier.append(" ").append(args[i]);
			}

			Command cmd = getCmdFromIdent(identifier.toString(), sender);
			if(cmd == null){
				continue;
			}
			String[] realArgs = (String[])Arrays.copyOfRange(args, argsIncluded, args.length);

			if(!cmd.isInProgress(sender)){
				if((realArgs.length < cmd.getMinArguments()) || (realArgs.length > cmd.getMaxArguments())){
					displayCommandHelp(cmd, sender);
					return true;
				}
				if((realArgs.length > 0) && (realArgs[0].equals("?"))){
					displayCommandHelp(cmd, sender);
					return true;
				}
			}

			cmd.execute(sender, realArgs);
			return true;
		}
		return true;
	}

	private void displayCommandHelp(Command cmd, CommandSender sender){
		sender.sendMessage(new StringBuilder().append("§cCommand:§e " ).append(cmd.getName()).toString());
		sender.sendMessage(new StringBuilder().append("§cDescription:§e " ).append(cmd.getDescription()).toString());
		sender.sendMessage(new StringBuilder().append("§cUsage:§e ").append(cmd.getUsage()).toString());
	}

	private Command getCmdFromIdent(String ident, CommandSender executor) {
		ident = ident.toLowerCase();
		if(this.identifiers.containsKey(ident)){
			return (Command)this.identifiers.get(ident);
		}

		for(Command cmd : this.commands.values()){
			if(cmd.isIdentifier(executor, ident)){
				return cmd;
			}
		}

		return null;
	}
}

Then a command:

public class PasswordCommand extends PlayerCommand {

	public PasswordCommand() {
		super("Set Group Password");
        setDescription("Sets the password for a group. Set password to \"null\" to make your group not joinable");
        setUsage("/ctpassword §8<group-name> <password>");
        setArgumentRange(2,2);
		setIdentifiers(new String[] {"ctpassword", "ctpw"});
	}

	public boolean execute(CommandSender sender, String[] args) {
		String groupName = args[0];
		GroupManager groupManager = Citadel.getGroupManager();
		Faction group = groupManager.getGroup(groupName);
		if(group == null){
			sendMessage(sender, ChatColor.RED, "Group doesn't exist");
			return true;
		}
		String playerName = sender.getName();
		if(!group.isFounder(playerName)){
			sendMessage(sender, ChatColor.RED, "Invalid permission to modify this group");
			return true;
		}
		String password = args[1];
		if(password.isEmpty() || password.equals("")){
			sendMessage(sender, ChatColor.RED, "Please enter a password");
			return true;
		}
		group.setPassword(password);
		groupManager.addGroup(group);
		sendMessage(sender, ChatColor.GREEN, "Changed password for %s to \"%s\"", groupName, password);
		return true;
	}

}

Notice the following:

super("Set Group Password");
        setDescription("Sets the password for a group. Set password to \"null\" to make your group not joinable");
        setUsage("/ctpassword §8<group-name> <password>");
        setArgumentRange(2,2);
		setIdentifiers(new String[] {"ctpassword", "ctpw"});

If there are parameters missing it will return a message from the information provided in the code above.
Notice in CommandHandler:

displayCommandHelp(cmd, sender);

Next we instantiate the commands in the singleton or main file called Citadel:

    public void registerCommands(){
    	commandHandler.addCommand(new AddModCommand());
    	commandHandler.addCommand(new AllowCommand());
    	commandHandler.addCommand(new BypassCommand());
    	commandHandler.addCommand(new CreateCommand());
    	commandHandler.addCommand(new DeleteCommand());
    	commandHandler.addCommand(new DisallowCommand());
    	commandHandler.addCommand(new FortifyCommand());
    	commandHandler.addCommand(new GroupCommand());
    	commandHandler.addCommand(new GroupsCommand());
    	commandHandler.addCommand(new InfoCommand());
    	commandHandler.addCommand(new JoinCommand());
    	commandHandler.addCommand(new LeaveCommand());
    	commandHandler.addCommand(new MaterialsCommand());
    	commandHandler.addCommand(new MembersCommand());
    	commandHandler.addCommand(new ModeratorsCommand());
    	commandHandler.addCommand(new NonReinforceableCommand());
    	commandHandler.addCommand(new OffCommand());
    	commandHandler.addCommand(new PasswordCommand());
    	commandHandler.addCommand(new PrivateCommand());
    	commandHandler.addCommand(new PublicCommand());
    	commandHandler.addCommand(new ReinforceCommand());
    	commandHandler.addCommand(new RemoveModCommand());
    	commandHandler.addCommand(new SecurableCommand());
    	commandHandler.addCommand(new StatsCommand());
    	commandHandler.addCommand(new TransferCommand());
    	commandHandler.addCommand(new VersionCommand());
    }
 

Greetapp – Specification Pattern Example

https://github.com/JonnyD/greetapp-api-java

Small example of the Specification pattern:

/**
     * GET  /greets-by-user/:groupId : get all the greets by group.
     *
     * @return the ResponseEntity with status 200 (OK) and the list of greets in body
     */
    @GetMapping("/greets-by-group/{groupId}")
    @Timed
    public Object getAllGreetsByGroup(@PathVariable Long groupId) {
        log.debug("REST request to get all Greets by user");
        List<Greet> greets = greetService.getByGang(groupId);

        Optional<Gang> optionalGang = this.gangService.getById(groupId);
        Gang gang = optionalGang.get();

        CanViewGang canViewGang = new CanViewGang(this.userService);
        if (!canViewGang.isSatisfiedBy(gang)) {
            return new ResponseEntity<>(HttpStatus.FORBIDDEN);
        }

        CanViewGreetsByGang canViewGreetsByGang = new CanViewGreetsByGang(this.userRepository);

        List<Greet> greetsToShow = new ArrayList<Greet>();
        for (Greet greet : greets) {
            if (canViewGreetsByGang.isSatisfiedBy(greet)) {
                greetsToShow.add(greet);
            }
        }

        return greetsToShow;
    }
public class CanViewGreetsByGang extends AbstractSpecification<Greet> {
    private User loggedInUser;

    public CanViewGreetsByGang(UserRepository userRepository) {
        Optional<String> login = SecurityUtils.getCurrentUserLogin();
        this.loggedInUser = userRepository.findOneByLogin(login.get()).get();
    }

    public boolean isSatisfiedBy(Greet greet) {
        IsGangPublic isGangPublic = new IsGangPublic();
        IsHostOfGreet isHostOfGreet = new IsHostOfGreet(this.loggedInUser);
        IsMemberOfGang isMemberOfGang = new IsMemberOfGang(this.loggedInUser);

        boolean isGangPublicOrIsMemberOfGangBoolean = isGangPublic
            .or(isMemberOfGang)
            .isSatisfiedBy(greet.getGang());

        boolean isHostOfGreetBoolean = isHostOfGreet
            .isSatisfiedBy(greet);

        return isGangPublicOrIsMemberOfGangBoolean || isHostOfGreetBoolean;
    }
}
 

A Finite State Machine in Minecraft

public String first(String text) {
return text.substring(0, 1);
}
 
@EventHandler(priority = EventPriority.HIGHEST)
public void blockPlace(BlockPlaceEvent bpe)
{
    // Block placed by player
    Block block = bpe.getBlock();
 
    // Get the blocks material relative to the block placed by the player
    Material main = block.getType();
    Material up1 = block.getRelative(BlockFace.UP, 1).getType();
    Material up2 = block.getRelative(BlockFace.UP, 2).getType();
    Material down1 = block.getRelative(BlockFace.DOWN, 1).getType();
    Material down2 = block.getRelative(BlockFace.DOWN, 2).getType();
 
    // Concatenate the first letters of each of the materials
    String materialFirstLetters = first(up1.toString()) + first(up2.toString()) +
                                  first(main.toString()) + first(down1.toString()) +
                                  first(down2.toString());
 
    // Regex pattern
    String regex = "DIJ..|I.DJ.|..IDJ";
 
    // Check if the first letters of the materials match the regex pattern
    if (materialFirstLetters.matches(regex))
    {
        System.out.println("Antenna Created");
    }
}
 

Online Book Store

For my final year project I had to build an n-tier web application from scratch using design patterns such as MVC, Command, Factory, Service, and DAO and technologies such as Java, JSP, JSTL, Tomcat, JUnit, XML, HTML/CSS, Javascript, MySQL.

Homepage. Displays featured books, latest books, newest members, and recent reviews.

bookstore2

All Books. Displays list of books which can be sorted by Title, Price, Rating, or Release Date.

bookstore3

Book (top). Shows the books title, image, rating and

bookstore4

Book (bottom). Shows similar themed books. Reviews by the users which can be filtered.

bookstore5

Profile. Shows profile of user and their recent reviews.