package database;
import gui.WindowMain;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
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 utils.StringPair;
/**
*
* @author Alec Thomson
* Class representing a model of the database.
* Characterized by a list of DatabaseItems and DatabaseProperties
* Interacts with the database views (list and table) to correctly represent the data. 1
*/
public class DatabaseModel {
//private List<DatabaseItem> _items;
private Map<String, DatabaseItem> _items;
// A list of specific properties each item might want to define.
private Map<String, DatabaseProperty> _properties;
public DatabaseModel()
{
//_items = new ArrayList<DatabaseItem>();
_items = new HashMap<String, DatabaseItem>();
_properties = new HashMap<String, DatabaseProperty>();
}
/**
* Function to add a new item to the database. Updates all of the views and the internal model of the database
* Returns true if the item was successfully added. Returns false otherwise (such as if the item name already exists in the database)
*
* @param item the item to add to the database
* @return Whether the item was successfully inserted
*/
public boolean addItemToDatabase(DatabaseItem item)
{
if(_items.containsKey(item.getName())) {
return false;
}
// Give the item a copy of every property, if it doesn't already have it
for(String propertyName : _properties.keySet()) {
if (!item.hasProperty(propertyName))
item.addProperty(propertyName, null);
}
_items.put(item.getName(), item);
WindowMain.addItemToListView(item);
WindowMain.addItemToTableView(item);
return true;
}
public void removeItemFromDatabase(DatabaseItem item)
{
if (!_items.containsKey(item.getName()))
return;
_items.remove(item.getName());
WindowMain.removeItemFromListView(item);
WindowMain.removeItemFromTableView(item);
}
/**
* Function to add a new property type to the database. Acts as a master list of representative property types.
* @param property a Database property that must be initialized
* @return true if the property was successfully added. Returns false otherwise (such as if the property name already exists in the database)
*/
public boolean addPropertyToDatabase(DatabaseProperty property)
{
if(_properties.containsKey(property.getName()))
return false;
_properties.put(property.getName(), property);
// Add an initial null value for this property to all of our database items.
for(DatabaseItem item : _items.values()) {
item.addProperty(property.getName(), null);
}
WindowMain.addPropertyToTableView(property);
WindowMain.updateGUI();
return true;
}
public void removePropertyFromDatabase(DatabaseProperty property)
{
if (!_properties.containsKey(property.getName()))
return;
_properties.remove(property.getName());
for(DatabaseItem item : _items.values()) {
item.removeProperty(property.getName());
}
WindowMain.removePropertyFromTableView(property);
WindowMain.updateGUI();
}
/**
* Creates an instance of a specific property based on its name. If the name of the property is not contained within
* our property map, returns null
* @param propertyName
* @return an instance of a specific property type, null if the property name is not in the property map.
*/
public DatabaseProperty createInstance(String propertyName)
{
if(!_properties.containsKey(propertyName))
return null;
return _properties.get(propertyName).initializeInstance();
}
public boolean isPropertyNameInDatabase(String propertyName) {
return _properties.containsKey(propertyName);
}
public DatabaseItem[] getItemList()
{
DatabaseItem[] items = new DatabaseItem[_items.values().size()];
return _items.values().toArray(items);
}
public DatabaseProperty[] getPropertyList()
{
DatabaseProperty[] properties = new DatabaseProperty[_properties.values().size()];
return _properties.values().toArray(properties);
}
public DatabaseProperty getPropertyValue(String propertyName) {
return _properties.get(propertyName);
}
/**
* Returns an XML representation of this database.
* @return an XML representation of this databse.
*/
public String xmlDigest()
{
String xml = "<Database>\n<Properties>\n";
// First define our properties
for (DatabaseProperty property : _properties.values()) {
xml += property.toXML() + "\n";
}
xml += "</Properties>\n<Items>\n";
for (DatabaseItem item : _items.values()) {
xml += item.toXML() + "\n";
}
xml += "</Items>\n</Database>";
return xml;
}
/**
* Function that loads a database model from an xml file
* @param xmlFile the file to load a database from
* @return the database model loaded from the xml file
*/
public static DatabaseModel createModelFromXML(File xmlFile)
{
DatabaseModel model = new DatabaseModel();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = dbf.newDocumentBuilder();
Document dom = builder.parse(xmlFile);
Element docElement = dom.getDocumentElement();
// Go through the list of properties and add them to our database.
NodeList propertyGroups = docElement.getElementsByTagName("Properties");
if (propertyGroups.getLength() == 0)
return null;
for (int i = 0; i < propertyGroups.getLength(); i++) {
Element propertyList = (Element)propertyGroups.item(i);
NodeList properties = propertyList.getChildNodes();
for (int j = 0; j < properties.getLength(); j++) {
if (properties.item(j).getNodeType() != Node.ELEMENT_NODE)
continue;
Element propertyElem = (Element)properties.item(j);
DatabaseProperty propertyTemplate = parsePropertyTemplate(propertyElem);
model.addPropertyToDatabase(propertyTemplate);
}
}
// Go through the list of items and add them to our database
NodeList itemGroups = docElement.getElementsByTagName("Items");
if (itemGroups.getLength() == 0)
return null;
Map<String, DatabaseItem> itemMap = new HashMap<String, DatabaseItem>();
for (int i = 0; i < itemGroups.getLength(); i++) {
Element itemList = (Element)itemGroups.item(i);
NodeList items = itemList.getChildNodes();
for (int j = 0; j < items.getLength(); j++) {
if (items.item(j).getNodeType() != Node.ELEMENT_NODE)
continue;
Element itemElem = (Element)items.item(j);
DatabaseItem item = parseItem(itemElem);
model.addItemToDatabase(item);
itemMap.put(item.getName(), item);
}
}
// After we've added all of our items to the database
// resolve all the names to actual references
for (DatabaseItem item : model.getItemList()) {
item.resolveNamesToItems(itemMap);
}
}
catch (Exception e) {
e.printStackTrace();
return null;
}
return model;
}
private static DatabaseItem parseItem(Element itemElem)
{
String itemName = itemElem.getAttribute("name");
DatabaseItem item = new DatabaseItem(itemName);
NodeList itemProperties = itemElem.getChildNodes();
for (int i = 0; i < itemProperties.getLength(); i++) {
if (itemProperties.item(i).getNodeType() != Node.ELEMENT_NODE)
continue;
Element propertyElem = (Element)itemProperties.item(i);
DatabaseProperty property = parseFullProperty(propertyElem);
item.addProperty(property.getName(), property);
}
return item;
}
private static DatabaseProperty parseFullProperty(Element propertyElem)
{
String propertyName = propertyElem.getAttribute("name");
DatabaseProperty property;
String propertyType = propertyElem.getTagName();
// Switch statement for our property type
if (propertyType.equals("BooleanProperty")) {
property = new BooleanDatabaseProperty(propertyName);
boolean value = propertyElem.getAttribute("value").equals("true");
((BooleanDatabaseProperty)property).setValue(value);
}
else if (propertyType.equals("IntegerProperty")) {
property = new IntegerDatabaseProperty(propertyName);
int value = Integer.parseInt(propertyElem.getAttribute("value"));
((IntegerDatabaseProperty)property).setValue(value);
}
else if (propertyType.equals("TextProperty")) {
property = new TextDatabaseProperty(propertyName);
String text = propertyElem.getAttribute("text");
((TextDatabaseProperty)property).setText(text);
}
else if (propertyType.equals("ItemListProperty")) {
property = new ItemListDatabaseProperty(propertyName);
NodeList itemRefs = propertyElem.getElementsByTagName("ItemReference");
for (int i = 0; i < itemRefs.getLength(); i++) {
Element itemRef = (Element)itemRefs.item(i);
String itemName = itemRef.getAttribute("name");
((ItemListDatabaseProperty)property).addItemName(itemName);
}
}
else if (propertyType.equals("StringListProperty")) {
property = new StringListDatabaseProperty(propertyName);
NodeList stringRefs = propertyElem.getElementsByTagName("String");
String[] stringList = new String[stringRefs.getLength()];
for (int i = 0; i < stringRefs.getLength(); i++) {
Element stringRef = (Element)stringRefs.item(i);
stringList[i] = stringRef.getAttribute("string");
}
((StringListDatabaseProperty)property).setStringList(stringList);
}
else if (propertyType.equals("StringPairListProperty")) {
property = new StringPairListDatabaseProperty(propertyName);
NodeList pairRefs = propertyElem.getElementsByTagName("StringPair");
StringPair[] pairList = new StringPair[pairRefs.getLength()];
for (int i = 0; i < pairRefs.getLength(); i++) {
Element pairRef = (Element)pairRefs.item(i);
String string1 = pairRef.getAttribute("string1");
String string2 = pairRef.getAttribute("string2");
pairList[i] = new StringPair(string1, string2);
}
((StringPairListDatabaseProperty)property).setStringPairs(pairList);
}
else if (propertyType.equals("CustomProperty")) {
NodeList subPropertyElems = propertyElem.getChildNodes();
List<DatabaseProperty> subProperties = new ArrayList<DatabaseProperty>();
for (int i = 0; i < subPropertyElems.getLength(); i++) {
if (subPropertyElems.item(i).getNodeType() != Node.ELEMENT_NODE)
continue;
Element subPropertyElem = (Element)subPropertyElems.item(i);
subProperties.add(parseFullProperty(subPropertyElem));
}
DatabaseProperty[] subPropertyArray = new DatabaseProperty[subProperties.size()];
property = new CustomProperty(propertyName, subProperties.toArray(subPropertyArray));
}
else
property = null;
return property;
}
private static DatabaseProperty parsePropertyTemplate(Element propertyElem)
{
String propertyName = propertyElem.getAttribute("name");
DatabaseProperty property;
String propertyType = propertyElem.getTagName();
// Switch statement for our property type
if (propertyType.equals("BooleanProperty"))
property = new BooleanDatabaseProperty(propertyName);
else if (propertyType.equals("IntegerProperty"))
property = new IntegerDatabaseProperty(propertyName);
else if (propertyType.equals("TextProperty"))
property = new TextDatabaseProperty(propertyName);
else if (propertyType.equals("ItemListProperty"))
property = new ItemListDatabaseProperty(propertyName);
else if (propertyType.equals("StringListProperty"))
property = new StringListDatabaseProperty(propertyName);
else if (propertyType.equals("StringPairListProperty"))
property = new StringPairListDatabaseProperty(propertyName);
else if (propertyType.equals("CustomProperty")) {
NodeList subPropertyElems = propertyElem.getChildNodes();
List<DatabaseProperty> subProperties = new ArrayList<DatabaseProperty>();
for (int i = 0; i < subPropertyElems.getLength(); i++) {
if (subPropertyElems.item(i).getNodeType() != Node.ELEMENT_NODE)
continue;
Element subPropertyElem = (Element)subPropertyElems.item(i);
subProperties.add(parsePropertyTemplate(subPropertyElem));
}
DatabaseProperty[] subPropertyArray = new DatabaseProperty[subProperties.size()];
property = new CustomProperty(propertyName, subProperties.toArray(subPropertyArray));
}
else
property = null;
return property;
}
}