package vooga.rts.gamedesign.factories;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream.GetField;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import vooga.rts.gamedesign.sprite.gamesprites.Projectile;
import vooga.rts.gamedesign.sprite.gamesprites.Resource;
import vooga.rts.gamedesign.sprite.gamesprites.interactive.InteractiveEntity;
import vooga.rts.gamedesign.sprite.gamesprites.interactive.buildings.Building;
import vooga.rts.gamedesign.sprite.gamesprites.interactive.units.Unit;
import vooga.rts.gamedesign.strategy.Strategy;
import vooga.rts.gamedesign.strategy.attackstrategy.AttackStrategy;
import vooga.rts.gamedesign.strategy.gatherstrategy.GatherStrategy;
import vooga.rts.gamedesign.strategy.occupystrategy.OccupyStrategy;
import vooga.rts.gamedesign.strategy.production.CanProduce;
import vooga.rts.gamedesign.strategy.production.CannotProduce;
import vooga.rts.gamedesign.strategy.production.ProductionStrategy;
import vooga.rts.gamedesign.strategy.upgradestrategy.CanUpgrade;
import vooga.rts.gamedesign.strategy.upgradestrategy.UpgradeStrategy;
import vooga.rts.gamedesign.upgrades.UpgradeTree;
import vooga.rts.gamedesign.weapon.Weapon;
import vooga.rts.util.Location3D;
import vooga.rts.util.ReflectionHelper;
/**
* This class is in charge of the loading of input XML files for different
* class types. It will figure out the class type this given file is in charge
* of, and pass the information to the corresponding decoder. All the decoders
* are loaded through an input file.
*
* @author Francesco Agosti
* @author Wenshun Liu
*/
public class Factory {
public static final String GAMEELEMENT_TAG = "GameElements";
public static final String DECODER_MATCHING_FILE = "DecodeMatchUp";
public static final String MATCHING_PAIR_TAG = "pair";
public static final String MATCHING_TYPE_TAG = "type";
public static final String MATCHING_PATH_TAG = "path";
private Map<String, Integer> myStarterPack;
private Map<String, String> myDecoderPaths;
private Map<String, Decoder> myDecoders;
private Map<String, InteractiveEntity> mySprites;
private Map<String, Resource> myResources;
private Map<String, Strategy> myStrategies;
private Map<String, Weapon> myWeapons;
private Map<String, Projectile> myProjectiles;
private Map<String, String[]> myProductionDependencies;
private Map<String, String[]> myStrategyDependencies;
private Map<String, UpgradeTree> myUpgradeTrees;
private Map<String, String[]> myWeaponDependencies;
private Map<String, String> myProjectileDependencies;
public Factory () {
myDecoderPaths = new HashMap<String, String>();
myDecoders = new HashMap<String, Decoder>();
try {
loadMappingInfo(DECODER_MATCHING_FILE, myDecoderPaths);
createDecoders();
}
catch (Exception e) {
e.printStackTrace();
}
myStarterPack = new HashMap<String, Integer>();
mySprites = new HashMap<String, InteractiveEntity>();
myResources = new HashMap<String, Resource>();
myStrategies = new HashMap<String, Strategy>();
myWeapons = new HashMap<String, Weapon>();
myProjectiles = new HashMap<String, Projectile>();
myProductionDependencies = new HashMap<String, String[]>();
myStrategyDependencies = new HashMap<String, String[]>();
myWeaponDependencies = new HashMap<String, String[]>();
myProjectileDependencies = new HashMap<String, String>();
myUpgradeTrees = new HashMap<String, UpgradeTree>();
}
/**
* all put methods add a key, game element pair to the appropriate Map instance variable.
* (The method is overloaded a few times since there are several maps).
*
* @param name
* @param value
*/
public void put (String name, InteractiveEntity value) {
mySprites.put(name, value);
}
public void put (String name, Resource resource) {
myResources.put(name, resource);
}
public void put (String name, Strategy value) {
myStrategies.put(name, value);
}
public void put (String name, UpgradeTree upgradeTree) {
System.out.println("puts here");
myUpgradeTrees.put(name, upgradeTree);
}
public void put (String name, Weapon weapon) {
myWeapons.put(name, weapon);
}
public void put (String name, Projectile proj) {
myProjectiles.put(name, proj);
}
public void put (String name, int amount) {
myStarterPack.put(name, amount);
}
public Map<String, UpgradeTree> getUpgradeTrees () {
return myUpgradeTrees;
}
/**
* Returns an Attack Strategy from a map of strategies.
* (this format is used so that you do not have to cast later on).
*
* @param key
* @return AttackStrategy
*/
public AttackStrategy getAttackStrategy (String key) {
return (AttackStrategy) myStrategies.get(key);
}
/**
* Returns a Gather Strategy from a map of strategies.
*
* @param key
* @return GatherStrategy
*/
public GatherStrategy getGatherStrategy (String key) {
return (GatherStrategy) myStrategies.get(key);
}
/**
* Returns an Occupy Strategy from a map of strategies.
*
* @param key
* @return OccupyStrategy
*/
public OccupyStrategy getOccupyStrategy (String key) {
return (OccupyStrategy) myStrategies.get(key);
}
/**
* Returns an Upgrade Strategy from a map of strategies.
*
* @param key
* @return UpgradeStrategy
*/
public UpgradeStrategy getUpgradeStrategy (String key) {
return (UpgradeStrategy) myStrategies.get(key);
}
/**
* Returns the whole map of Entities
* (you do not need to return any other maps because entities encapsulate other objects).
*/
public Map<String, InteractiveEntity> getEntitiesMap () {
return mySprites;
}
/**
* Returns the whole map of resources.
*/
public Map<String, Resource> getResourceMap () {
return myResources;
}
/**
* Returns an Interactive Entity from a map of InteractiveEntities
*
* @param key
* @return InteractiveEntity
*/
public InteractiveEntity getInteractiveEntity (String key) {
return mySprites.get(key);
}
/**
* Returns an Resource from a map of Resources
*
* @param key
* @return InteractiveEntity
*/
public Resource getResource (String key) {
return myResources.get(key);
}
/**
* Returns the initial values of the game defined by the XML file.
*/
public Map<String, Integer> getStarterPack () {
return myStarterPack;
}
/**
* Puts a production dependency (tells the factory what "name" can produce) in
* a dependency map.
*
* @param name
* @param itProduces
*/
public void putProductionDependency (String name, String[] itProduces) {
myProductionDependencies.put(name, itProduces);
}
/**
* Puts a strategy dependency (tells the factory what strategies "name" uses) in
* a dependency map.
*
* @param name
* @param strategies
*/
public void putStrategyDependency (String name, String[] strategies) {
myStrategyDependencies.put(name, strategies);
}
/**
* Puts a weapon dependency (tells the factory what weapon "name" uses) in
* a dependency map.
*
* @param name
* @param strategies
*/
public void putWeaponDependency (String name, String[] weapons) {
myWeaponDependencies.put(name, weapons);
}
/**
* Puts a projectile dependency (tells the factory what projectile "name" uses) in
* a dependency map.
*
* @param name
* @param strategies
*/
public void putProjectileDependency (String name, String projectile) {
myProjectileDependencies.put(name, projectile);
}
/**
* Creates decoders by loading the input file that specifies the path of
* each Decoder and the type of class it is in charge of. Puts the decoders
* and their corresponding types into a map.
*
* This method will be called when the Factory class is created.
*
* @param fileName the name of the XML file that specifies decoder paths.
* @throws ClassNotFoundException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchMethodException
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
public void loadMappingInfo (String fileName, Map map) throws ClassNotFoundException,
IllegalArgumentException,
SecurityException,
InstantiationException,
IllegalAccessException,
InvocationTargetException,
NoSuchMethodException,
ParserConfigurationException,
SAXException, IOException {
URI f = null;
try {
f = getClass().getResource(fileName).toURI();
}
catch (URISyntaxException e) {
return;
}
File file = new File(f);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
NodeList nodeLst = doc.getElementsByTagName(MATCHING_PAIR_TAG);
for (int i = 0; i < nodeLst.getLength(); i++) {
Element pairElmnt = (Element) nodeLst.item(i);
Element typeElmnt = (Element) pairElmnt.getElementsByTagName(MATCHING_TYPE_TAG).item(0);
NodeList typeList = typeElmnt.getChildNodes();
String type = ((Node) typeList.item(0)).getNodeValue();
Element pathElmnt = (Element) pairElmnt.getElementsByTagName(MATCHING_PATH_TAG).item(0);
NodeList pathList = pathElmnt.getChildNodes();
String path = ((Node) pathList.item(0)).getNodeValue();
map.put(type, path);
}
}
private void createDecoders () throws IllegalArgumentException, SecurityException,
InstantiationException, IllegalAccessException,
InvocationTargetException, NoSuchMethodException,
ClassNotFoundException {
for (String key : myDecoderPaths.keySet()) {
Decoder decoder =
(Decoder) ReflectionHelper.makeInstance(myDecoderPaths.get(key), this);
myDecoders.put(key, decoder);
}
}
/**
* Loads the XML file passed in and determines the type of class it provides
* information for. Then passes the input file to the corresponding decoder
* in charge of that type of class.
*
* @param fileName the name of the XML file that provides class information
* and to be loaded
*/
public void loadXMLFile (String fileName) {
try {
URI f = null;
try {
f = getClass().getResource(fileName).toURI();
}
catch (URISyntaxException e) {
return;
}
File file = new File(f);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(file);
doc.getDocumentElement().normalize();
NodeList head = doc.getChildNodes();
Node childNode = head.item(0);
NodeList children = childNode.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node tempNode = children.item(i);
if (tempNode.getNodeType() == Node.ELEMENT_NODE) {
String type = tempNode.getNodeName();
myDecoders.get(type).create(doc, type);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
initializeStrategies();
initializeProjectiles();
initializeWeapons();
initializeProducables();
}
/**
* Once instances of game elements are loaded into their maps this method adds the
* producables (specified from the XML file) to their respective entities.
*/
private void initializeProducables () {
for (String key : myProductionDependencies.keySet()) {
InteractiveEntity producer = mySprites.get(key);
String[] produces = myProductionDependencies.get(key);
producer.setProductionStrategy(new CanProduce(producer));
for (String baby : produces) {
InteractiveEntity producable = mySprites.get(baby);
producer.addProducable(producable.copy());
}
}
}
/**
* Once strategies defined by the XML file are loaded into their maps this method uses
* the strategy dependency map to add strategies to their holder
*
*/
private void initializeStrategies () {
for (String key : myStrategyDependencies.keySet()) {
String[] strategies = myStrategyDependencies.get(key);
AttackStrategy attack = (AttackStrategy) myStrategies.get(strategies[0]);
mySprites.get(key).setAttackStrategy(attack);
OccupyStrategy occupy = (OccupyStrategy) myStrategies.get(strategies[1]);
mySprites.get(key).setOccupyStrategy(occupy);
GatherStrategy gather = (GatherStrategy) myStrategies.get(strategies[2]);
mySprites.get(key).setGatherStrategy(gather);
UpgradeStrategy upgrade = (UpgradeStrategy) myStrategies.get(strategies[3]);
mySprites.get(key).setUpgradeStrategy(upgrade);
if (upgrade instanceof CanUpgrade) {
UpgradeTree relatedUpgradeTree = myUpgradeTrees.get(strategies[4]);
System.out.println("RELATED UPGRADE: " + strategies[4]);
mySprites.get(key).setUpgradeTree(relatedUpgradeTree);
}
}
}
/**
* Once weapons defined by the XML file are loaded into their map this method uses
* the weapon dependency map to assign weapons to the correct entity.
*
* @param args
*/
private void initializeWeapons () {
for (String key : myWeaponDependencies.keySet()) {
String[] weapons = myWeaponDependencies.get(key);
InteractiveEntity holder = mySprites.get(key);
for (String weapon : weapons) {
Weapon toAdd = myWeapons.get(weapon);
holder.addWeapon(toAdd);
}
}
}
/**
* Once projectiles that have been defined in the XML file have been loaded into their
* maps this method gives the projectiles to their weapon.
*
* @param args
*/
private void initializeProjectiles () {
for (String key : myProjectileDependencies.keySet()) {
String projectile = myProjectileDependencies.get(key);
Weapon holder = myWeapons.get(key);
Projectile toAdd = myProjectiles.get(projectile);
holder.setProjectile(toAdd);
}
}
public Weapon getWeapon(int index) {
Iterator<Weapon> it = myWeapons.values().iterator();
int i = 0;
while (it.hasNext()) {
Weapon w = it.next();
if (i == index) {
return w;
}
i++;
}
return null;
}
}