package vooga.fighter.model.loaders;
import java.awt.Rectangle;
import java.io.File;
import java.util.ResourceBundle;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import util.Pixmap;
import vooga.fighter.model.ModelConstants;
import vooga.fighter.model.objects.GameObject;
import vooga.fighter.model.utils.State;
/**
* Abstract class with shared methods that all ObjectLoader-derived classes may need.
*
* @author David Le, alanni
*
*/
public abstract class ObjectLoader {
/**
* Raw format of XML document containing object information
*/
private Document myDocument;
/**
* Resource bundle containing the information on tags to search for in xml files
*/
private ResourceBundle myTags;
/**
* Resource bundle containing the information on default values if certain parameters
* are not found in xml files.
*/
private ResourceBundle myDefaults;
/**
* Resource bundle containing the information on which tags each class has in xml files
*/
private ResourceBundle myProperties;
/**
* Points to the xml file that the loader will be parsing.
* @param objectPath
* @param pathHierarchy The path to the folder containing the game's resources
*/
public ObjectLoader (String pathName, String pathHierarchy) {
setResourcePaths(pathHierarchy);
String objectPath = myTags.getString(pathName);
File objectFile = new File(objectPath);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
try {
dBuilder = dbFactory.newDocumentBuilder();
myDocument = dBuilder.parse(objectFile);
myDocument.getDocumentElement().normalize();
} catch (Exception e) {
myDocument = null;
System.err.println(ModelConstants.PARSING_ERROR+ getClass().getSimpleName());
}
}
/**
* Sets the resource paths for the object loader
* @param pathHierarchy The path to the folder containing the game's resources
*/
private void setResourcePaths(String pathHierarchy){
myTags = ResourceBundle.getBundle(pathHierarchy+ModelConstants.RESOURCE_OBJECT_PATH);
myDefaults= ResourceBundle.getBundle(pathHierarchy+ModelConstants.RESOURCE_DEFAULT_VALUE_PATH);
myProperties= ResourceBundle.getBundle(pathHierarchy+ModelConstants.RESOURCE_PROPERTIES_PATH);
}
/**
* Loads object based on the name given
* @param name The name to be matched in the respective subclass's xml file
* @param pathHierarchy The path to the folder containing the game's resources
*/
protected abstract void load(String name, String pathHierarchy);
/**
* Returns the xml document which the loader points to
* @return myDocument
*/
public Document getDocument() {
return myDocument;
}
/**
* Returns resource bundle which contains what the names of nodes in the xml files should be.
* return myResources
*/
protected ResourceBundle getResourceBundle() {
return myTags;
}
/**
* Returns the string attribute value of the specified tag for the specified mode.
* @param node The node to be examined
* @param tag The tag which we want to get the value of
* @return value The value matching the tag in the specified node
*/
public String getAttributeValue(Node node, String tag) {
try{
String value= node.getAttributes().getNamedItem(tag).getTextContent();
return value;
}
catch(NullPointerException e){
String value= myDefaults.getString(getClass().getSimpleName()+tag);
System.err.println(ModelConstants.PROPERTYSET_ERROR1+ tag+ ModelConstants.PROPERTYSET_ERROR2+value);
return value;
}
}
/**
* Loads and adds states for the GameObject.
* @param stateNodes The list of nodes which contain all the states' information
* @param myObject GameObject to load the states' information into
*/
protected void addStates(NodeList stateNodes, GameObject myObject) {
for (int i = 0; i < stateNodes.getLength(); i++) {
Element state = (Element) stateNodes.item(i);
String stateName = getAttributeValue(stateNodes.item(i), ModelConstants.STATENAME_PROPERTY);
NodeList frameNodes = state.getElementsByTagName(ModelConstants.FRAME_PROPERTY);
State newState = new State(myObject, frameNodes.getLength());
getFrameProperties(frameNodes, newState);
myObject.addState(stateName, newState);
}
}
/**
* Loads frames and states for the object which calls the method
* @param frameNodes The list of nodes which contain all the frames' information
* for this state
* @param newState The state which all of the frames' information will be loaded into.
*/
protected void getFrameProperties(NodeList frameNodes, State newState){
for (int j = 0; j < frameNodes.getLength(); j++) {
Element frame = (Element) frameNodes.item(j);
if (frame.getAttributes().getNamedItem(ModelConstants.IMAGE_PROPERTY) != null){
newState.populateImage(new Pixmap(getAttributeValue(frame, ModelConstants.IMAGE_PROPERTY)), j);
} else {
newState.populateImage(new Pixmap(ModelConstants.BLANK), j);
}
NodeList hitboxNodes = frame.getElementsByTagName(ModelConstants.HITBOX_PROPERTY);
for (int k=0; k<hitboxNodes.getLength(); k++){
newState.populateRectangle(new Rectangle(Integer.parseInt(getAttributeValue(hitboxNodes.item(k), ModelConstants.CORNERX_PROPERTY)),
Integer.parseInt(getAttributeValue(hitboxNodes.item(k), ModelConstants.CORNERY_PROPERTY)), Integer.parseInt(getAttributeValue(hitboxNodes.item(k), ModelConstants.WIDTH_PROPERTY)),
Integer.parseInt(getAttributeValue(hitboxNodes.item(k), ModelConstants.HEIGHT_PROPERTY))), j);
}
}
}
/**
* Return array of desired properties that will be loaded into the character
*/
protected String [] getProperties(){
return myProperties.getString(getClass().getSimpleName()).split(ModelConstants.DELIMITER);
}
/**
* Loops through list of properties and
* @param node The node which contains the property information
* @param object The GameObject which will have the property information added into
*/
protected void addProperties(Node node, GameObject object){
for (String property: getProperties()){
getAndAddProperty(node, property, object);
}
}
/**
* Adds property values for gameobject
*/
protected void getAndAddProperty(Node node, String property, GameObject object){
int propertyValue= Integer.parseInt(getAttributeValue(node, property));
object.addProperty(property, propertyValue);
}
}