/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sshtools.ssh.components;
import java.util.Hashtable;
import java.util.Vector;
import com.sshtools.ssh.SshException;
import com.sshtools.util.Arrays;
/**
* <p>
* A utility class used to store the available transport components and provide
* delimited listing as required in the key exchange initialization process.
* </p>
*
* @author Lee David Painter
*/
public class ComponentFactory implements Cloneable {
/**
* The supported components stored in a Hashtable with a String key as the
* component name such as "3des-cbc" and a Class value storing the
* implementation class.
*/
protected Hashtable<String, Class<?>> supported = new Hashtable<String, Class<?>>();
protected Vector<Object> order = new Vector<Object>();
Class<?> type;
private boolean locked;
public synchronized String changePositionofAlgorithm(String name,
int position) throws SshException {
if (position < 0) {
throw new SshException("index out of bounds",
SshException.BAD_API_USAGE);
}
if (position >= order.size()) {
position = order.size();
}
int currentLocation = order.indexOf(name);
if (currentLocation < position) {
order.insertElementAt(name, position);
order.removeElementAt(currentLocation);
} else {
order.removeElementAt(currentLocation);
order.insertElementAt(name, position);
}
return (String) order.elementAt(0);
}
public synchronized String createNewOrdering(int[] ordering)
throws SshException {
if (ordering.length > order.size()) {
throw new SshException("too many indicies",
SshException.BAD_API_USAGE);
}
// move indices specified in ordering to end of vector
for (int i = 0; i < ordering.length; i++) {
if (!(ordering[i] >= 0 && ordering[i] < order.size())) {
throw new SshException("index out of bounds",
SshException.BAD_API_USAGE);
}
order.insertElementAt(order.elementAt(ordering[i]), order.size());
}
// sort ordering indices so that remove lowest indices first
Arrays.sort(ordering);
// remove from order starting from end
for (int i = (ordering.length - 1); i >= 0; i--) {
order.removeElementAt(ordering[i]);
}
// move ones moved to end to beginning starting from end
for (int i = 0; i < ordering.length; i++) {
Object element = order.elementAt(order.size() - 1);
order.removeElementAt(order.size() - 1);
order.insertElementAt(element, 0);
}
return (String) order.elementAt(0);
}
/**
* Create a component factory with the base class supplied.
*
* @param type
* @throws java.lang.ClassNotFoundException
* Thrown if the class cannot be resolved.
*/
public ComponentFactory(Class<?> type) {
this.type = type;
}
/**
* Determine whether the factory supports a given component type.
*
* @param name
* @return <code>true</code> if the component is supported otherwise
* <code>false</code>
*/
public boolean contains(String name) {
return supported.containsKey(name);
}
/**
* List the types of components supported by this factory. Returns the list
* as a comma delimited string with the preferred value as the first entry
* in the list. If the preferred value is "" then the list is returned
* unordered.
*
* @param preferred
* The preferred component type.
* @return A comma delimited String of component types; for example
* "3des-cbc,blowfish-cbc"
*/
public synchronized String list(String preferred) {
return createDelimitedList(preferred);
}
/**
* Add a new component type to the factory. This method throws an exception
* if the class cannot be resolved. The name of the component IS NOT
* verified to allow component implementations to be overridden.
*
* @param name
* @param cls
* @throws ClassNotFoundException
*/
public synchronized void add(String name, Class<?> cls) {
if (locked) {
throw new IllegalStateException(
"Component factory is locked. Components cannot be added");
}
supported.put(name, cls);
// add name to end of order vector
if (!order.contains(name))
order.addElement(name);
}
/**
* Get a new instance of a supported component.
*
* @param name
* The name of the component; for example "3des-cbc"
* @return the newly instantiated object
* @throws ClassNotFoundException
*/
public Object getInstance(String name) throws SshException {
if (supported.containsKey(name)) {
try {
return createInstance(name, (Class<?>) supported.get(name));
} catch (Throwable t) {
throw new SshException(t.getMessage(),
SshException.INTERNAL_ERROR);
}
}
throw new SshException(name + " is not supported",
SshException.UNSUPPORTED_ALGORITHM);
}
/**
* Override this method to create an instance of the component.
*
* @param cls
* @return the newly instantiated object
* @throws java.lang.Throwable
*/
protected Object createInstance(String name, Class<?> cls) throws Throwable {
return cls.newInstance();
}
/**
* Create a delimited list of supported components.
*
* @param preferred
* @return a comma delimited list
*/
private synchronized String createDelimitedList(String preferred) {
StringBuffer listBuf = new StringBuffer();
int prefIndex = order.indexOf(preferred);
// remove preferred and add it back at the end to ensure it is not
// duplicated in the list returned
if (prefIndex != -1) {
listBuf.append(preferred);
}
for (int i = 0; i < order.size(); i++) {
if (prefIndex == i) {
continue;
}
listBuf.append("," + (String) order.elementAt(i));
}
if (prefIndex == -1 && listBuf.length() > 0) {
return listBuf.toString().substring(1);
}
return listBuf.toString();
}
/**
* Remove a supported component
*
* @param name
*/
public synchronized void remove(String name) {
supported.remove(name);
// remove name from order vector
order.removeElement(name);
}
/**
* Clear all of the entries in this component factory.
*/
public synchronized void clear() {
if (locked) {
throw new IllegalStateException(
"Component factory is locked. Removing all components renders it unusable");
}
supported.clear();
// clear order vector
order.removeAllElements();
}
@SuppressWarnings("unchecked")
public Object clone() {
ComponentFactory clone = new ComponentFactory(type);
clone.order = (Vector<Object>) order.clone();
clone.supported = (Hashtable<String, Class<?>>) supported.clone();
clone.locked = locked;
return clone;
}
public String[] toArray() {
return (String[]) order.toArray(new String[order.size()]);
}
public void lockComponents() {
this.locked = true;
}
}