/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.sip.communicator.impl.protocol.irc;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.Entry;
import net.java.sip.communicator.impl.protocol.irc.exception.*;
import net.java.sip.communicator.util.*;
/**
* Command factory.
*
* TODO Consider implementing 1 "management" command for querying the registered
* commands at runtime and maybe some other things. Not very urgent, though.
*
* @author Danny van Heumen
*/
public class CommandFactory
{
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(CommandFactory.class);
/**
* Registry for commands. Contains all available commands.
*/
private static final Map<String, Class<? extends Command>> COMMANDS
= new HashMap<String, Class<? extends Command>>();
/**
* Get an unmodifiable map of registered commands.
*
* @return returns an unmodifiable map of registered commands
*/
public static synchronized Map<String, Class<? extends Command>>
getCommands()
{
return new HashMap<String, Class<? extends Command>>(COMMANDS);
}
/**
* Register a new command at the factory.
*
* @param command the command word
* @param type the type to instantiate for command execution
*/
public static synchronized void registerCommand(final String command,
final Class<? extends Command> type)
{
if (command == null)
{
throw new IllegalArgumentException("command cannot be null");
}
if (type == null)
{
throw new IllegalArgumentException("type cannot be null");
}
COMMANDS.put(command, type);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Registered command '" + command + "' ("
+ type.toString() + ")");
}
}
/**
* Unregister a command from the factory.
*
* @param type the type to unregister
* @param command (optional) specify the command for which the type is
* registered. This can be used to unregister only one of
* multiple commands for the same implementation type.
*/
public static synchronized void unregisterCommand(
final Class<? extends Command> type,
final String command)
{
Iterator<Entry<String, Class<? extends Command>>> it =
COMMANDS.entrySet().iterator();
while (it.hasNext())
{
Entry<String, Class<? extends Command>> entry = it.next();
if (entry.getValue() == type
&& (command == null || command.equals(entry.getKey())))
{
it.remove();
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Unregistered command '" + entry.getKey()
+ "' (" + type.toString() + ")");
}
}
}
}
/**
* Instance of protocol provider service.
*/
private final ProtocolProviderServiceIrcImpl provider;
/**
* Instance of IRC connection.
*/
private final IrcConnection connection;
/**
* Constructor for instantiating a command factory.
*
* @param provider the protocol provider service
* @param connection the IRC connection
*/
CommandFactory(final ProtocolProviderServiceIrcImpl provider,
final IrcConnection connection)
{
if (provider == null)
{
throw new IllegalArgumentException("provider cannot be null");
}
this.provider = provider;
if (connection == null)
{
throw new IllegalArgumentException("connection cannot be null");
}
this.connection = connection;
}
/**
* Create new command based on the provided key if available in the command
* registry.
*
* @param command the command to look up and instantiate
* @return returns a command instance
* @throws UnsupportedCommandException in case command cannot be found
* @throws BadCommandException In case of a incompatible command or bad
* implementation.
*/
public Command createCommand(final String command)
throws UnsupportedCommandException,
BadCommandException
{
if (command == null || command.isEmpty())
{
throw new IllegalArgumentException(
"command cannot be null or empty");
}
final Class<? extends Command> type = COMMANDS.get(command);
if (type == null)
{
throw new UnsupportedCommandException(command);
}
try
{
Constructor<? extends Command> cmdCtor =
type.getConstructor(ProtocolProviderServiceIrcImpl.class,
IrcConnection.class);
return cmdCtor.newInstance(this.provider, this.connection);
}
catch (NoSuchMethodException e)
{
throw new BadCommandException(command, type,
"There is no compatible constructor for instantiating this "
+ "command.", e);
}
catch (InstantiationException e)
{
throw new BadCommandException(command, type,
"Unable to instantiate this command class.", e);
}
catch (IllegalAccessException e)
{
throw new BadCommandException(command, type,
"Unable to access the constructor of this command class.", e);
}
catch (InvocationTargetException e)
{
throw new BadCommandException(command, type,
"An exception occurred while executing the constructor.", e);
}
catch (IllegalArgumentException e)
{
throw new BadCommandException(command, type,
"Invalid arguments were passed to the "
+ "constructor. This may be a bug in the CommandFactory "
+ "implementation.", e);
}
}
}