package mekhq.campaign.universe;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.joda.time.DateTime;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import mekhq.MekHQ;
/**
* Instead of making this a static like Planets, we are just going to reload a years
* worth of news items at the start of every year, to cut down on memory usage. If this
* slows things down too much on year turn over we can reconsider
* @author Jay Lawson
*
*/
public class News {
private final static Object LOADING_LOCK = new Object[0];
// Marshaller / unmarshaller instances
private static Marshaller marshaller;
private static Unmarshaller unmarshaller;
static {
try {
JAXBContext context = JAXBContext.newInstance(NewsItem.class);
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
unmarshaller = context.createUnmarshaller();
// For debugging only!
unmarshaller.setEventHandler(new javax.xml.bind.helpers.DefaultValidationEventHandler());
} catch(JAXBException e) {
MekHQ.logError(e);
}
}
//we need two hashes - one to access by date and the other by an id
private Map<DateTime, List<NewsItem>> archive;
private Map<Integer, NewsItem> news;
public News(int year, long seed) {
loadNewsFor(year, seed);
}
public NewsItem getNewsItem(int id) {
synchronized(LOADING_LOCK) {
return news.get(id);
}
}
public List<NewsItem> fetchNewsFor(DateTime d) {
synchronized(LOADING_LOCK) {
if(archive.containsKey(d)) {
return archive.get(d);
}
return new ArrayList<>();
}
}
public void loadNewsFor(int year, long seed) {
synchronized(LOADING_LOCK) {
archive = new HashMap<>();
news = new HashMap<>();
int id = 0;
MekHQ.logMessage("Starting load of news data for " + year + " from XML...");
// Initialize variables.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document xmlDoc = null;
try(FileInputStream fis = new FileInputStream("data/universe/news.xml")) {
// Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// Parse using builder to get DOM representation of the XML file
xmlDoc = db.parse(fis);
} catch (Exception ex) {
MekHQ.logError(ex);
}
Element newsEle = xmlDoc.getDocumentElement();
NodeList nl = newsEle.getChildNodes();
// Get rid of empty text nodes and adjacent text nodes...
// Stupid weird parsing of XML. At least this cleans it up.
newsEle.normalize();
// Okay, lets iterate through the children, eh?
for (int x = 0; x < nl.getLength(); x++) {
Node wn = nl.item(x);
if (wn.getParentNode() != newsEle)
continue;
int xc = wn.getNodeType();
if (xc == Node.ELEMENT_NODE) {
// This is what we really care about.
// All the meat of our document is in this node type, at this
// level.
// Okay, so what element is it?
String xn = wn.getNodeName();
if (xn.equalsIgnoreCase("newsItem")) {
NewsItem newsItem = null;
try {
newsItem = (NewsItem) unmarshaller.unmarshal(wn);
} catch(JAXBException e) {
MekHQ.logError(e);
continue;
}
if(null == newsItem.getDate()) {
MekHQ.logMessage("The date is null for news Item " + newsItem.getHeadline());
}
if(!newsItem.isInYear(year)) {
continue;
}
List<NewsItem> items;
newsItem.finalizeDate(seed);
if(null == archive.get(newsItem.getDate())) {
items = new ArrayList<NewsItem>();
items.add(newsItem);
archive.put(newsItem.getDate(), items);
} else {
items = archive.get(newsItem.getDate());
items.add(newsItem);
archive.put(newsItem.getDate(), items);
}
newsItem.setId(id);
news.put(id, newsItem);
++ id;
}
}
}
MekHQ.logMessage("loaded " + archive.size() + " days of news items for " + year);
}
}
}