package util.input;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.swing.JComponent;
/**
* Input API built to allow games to accept input from an expandable set of input devices.
*
* @author Gavin Ovsak, aaronkrolik
*/
@SuppressWarnings("rawtypes")
public class Input {
private List<WeakReference> myWeakReferences = new ArrayList<WeakReference>();
private Map<String, Method> gameBehaviors = new HashMap<String, Method>();
private Map<String, Class> gameParameters = new HashMap<String, Class>();
private ArrayList<InputDevice> inputDevices = new ArrayList<InputDevice>();
private ResourceMappingObject myInputMap;
private ResourceBundle myDefaultSettings;
private ResourceBundle myOverrideSettings;
public Input(String inputMapResourcePath, JComponent component) {
myInputMap = new ResourceMappingObject("mapping", inputMapResourcePath);
myDefaultSettings = ResourceBundle.getBundle("util/input/DefaultSettings");
inputDevices.add(new KeyboardInput(component, this));
inputDevices.add(new MouseInput(component, this));
// inputDevices.add(new VoiceInput(component,this));
}
public Input(String inputMapResourcePath, String overrideSettingsResourcePath, JComponent component) {
this(inputMapResourcePath, component);
myOverrideSettings = ResourceBundle.getBundle(overrideSettingsResourcePath);
sendUpdatedWebButtons();
}
/**
* put new key/values in our input/game behavior mapping object
* @param inputBehavior
* @param gameBehavior
*/
public void overrideMapping(String gameBehavior, String inputBehavior) {
myInputMap.overrideMapping(gameBehavior, inputBehavior);
sendUpdatedWebButtons();
}
/**
* Dynamically replaces the mapping resource properties file path reference.
* @param resourcePath
*/
public void replaceMappingResourcePath(String resourcePath) {
myInputMap.setResourcePath(resourcePath);
sendUpdatedWebButtons();
}
/**
* restores the mapping properties to their initial defaults.
*/
public void restoreMappingDefaults(){
myInputMap.restoreDefault();
sendUpdatedWebButtons();
}
/**
* Override our default input settings
* @param String resourcePath is the relative location to the settings resource file
*/
public void overrideSettings(String resourcePath){
myOverrideSettings = ResourceBundle.getBundle(resourcePath);
}
/**
* Gets an interpreted list of the game behaviors from the ResourcemappingObject
* and then uses reflection to add information to the list about what types of information
* the behaviors are expecting so that the web site can render the buttons appropriately.
*/
private void sendUpdatedWebButtons() {
ArrayList<WebButton> webButtons = myInputMap.getWebButtons();
for(WebButton webButton : webButtons) {
String referenceGameBehavior = (webButton.getUpBehavior().length() != 0)?webButton.getUpBehavior():webButton.getDownBehavior();
if(gameBehaviors.containsKey(referenceGameBehavior)) {
Class[] paramClasses = gameBehaviors.get(referenceGameBehavior).getParameterTypes();
if(paramClasses.length > 0 && paramClasses[0] == PositionObject.class) {
webButton.setButtonType("PositionObject");
} else if(paramClasses.length > 0 && paramClasses[0] == RollObject.class) {
webButton.setButtonType("RollObject");
} else {
webButton.setButtonType("AlertObject");
}
}
}
//send webButtons to the web
}
/**
* Include input instance in our collection of instances that can have
* annotated methods invoked
*
* @param Object input
*/
@SuppressWarnings("unchecked")
public void addListenerTo(Object in) {
myWeakReferences.add(new WeakReference(in));
Class inputClass = in.getClass();
while (inputClass != Object.class){
if (inputClass.getAnnotation(InputClassTarget.class) != null) {
for (Method method : inputClass.getMethods()) {
Annotation annotation = method.getAnnotation(InputMethodTarget.class);
if (annotation instanceof InputMethodTarget) {
String methodName = ((InputMethodTarget) annotation).name();
gameBehaviors.put(methodName,method);
Class[] paramClasses = method.getParameterTypes();
if(paramClasses.length == 0) {
gameParameters.put(methodName, null);
} else {
gameParameters.put(methodName, paramClasses[0]);
}
}
}
}
inputClass = inputClass.getSuperclass();
}
}
/**
* Removes instance from our cache of instances to invoke methods on
* @param Instance to be removed
*/
public void removeListener(Object in) {
for (int i = 0; i < myWeakReferences.size(); i++) {
if (in == myWeakReferences.get(i).get()) {
myWeakReferences.remove(i);
break;
}
}
}
/**
* Get a setting from our settings resource file objects
* @param String settingName
* @return String value out
*/
protected String getSetting(String settingName){
if(myOverrideSettings != null && myOverrideSettings.containsKey(settingName)) {
return myOverrideSettings.getString(settingName);
} else {
return myDefaultSettings.getString(settingName);
}
}
/**
* Notification receiver from input devices
* TODO better exception handling
* @param String action (key for dynamicMapping)
* @param AlertObject object (input state and specifics)
*/
protected void actionNotification(String inputBehavior, AlertObject object) {
//System.out.println(inputBehavior);
try {
if (myInputMap.containsInputBehavior(inputBehavior)) {
execute(myInputMap.getGameBehavior(inputBehavior), object);
}
} catch (NullPointerException e) {
System.out.println("Null Pointer Exception");
} catch (MissingResourceException e) {
System.out.println("Missing Resource Exception! Resources did not contain: "
+ inputBehavior);
}
}
/**
* Executes methods using reflection
* TODO: handle exceptions
* @param key
* @param in
*/
protected void execute(String gameBehavior, AlertObject in) {
//System.out.println(gameBehavior);
for (WeakReference x : myWeakReferences) {
try {
Class[] paramClasses = gameBehaviors.get(gameBehavior).getParameterTypes();
if (paramClasses.length == 0) {
gameBehaviors.get(gameBehavior).invoke(x.get()); //An argument is not required for game behaviors
} else if(paramClasses[0].isInstance(in)) {
gameBehaviors.get(gameBehavior).invoke(x.get(), paramClasses[0].cast(in));
}
} catch (IllegalArgumentException e) {
System.out.println("Illegal Argument Exception on game method call: " + gameBehavior);
} catch (IllegalAccessException e) {
System.out.println("Illegal Access Exception on game method call: " + gameBehavior);
} catch (InvocationTargetException e) {
System.out.println("Inovaction Target Exception on game method call: " + gameBehavior);
} catch (NullPointerException e) {
System.out.println("Null Pointer Exception on game method call: " + gameBehavior);
}
}
}
}