package models.database.importers;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import models.Answer;
import models.Question;
import models.User;
import models.database.IDatabase;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
public class XMLParser extends DefaultHandler {
private final Map<Integer, User> idUserBase;
private final Map<Integer, Question> idQuestionBase;
private final IDatabase db;
private final List<ProtoAnswer> protoanswers;
private final List<ProtoQuestion> protoquestions;
private final ElementParser parser;
private final Action createUser;
private final Action createQuestion;
private final Action createAnswer;
private final Action makeConnections;
public XMLParser(IDatabase db) {
this.idUserBase = new HashMap<Integer, User>();
this.idQuestionBase = new HashMap<Integer, Question>();
this.db = db;
this.protoanswers = new LinkedList();
this.protoquestions = new LinkedList();
this.makeConnections = new Action() {
public void call(Element e) {
makeConnections();
}
};
this.createAnswer = new Action() {
public void call(Element e) {
createAnswer(e);
}
};
this.createUser = new Action() {
public void call(Element e) {
createUser(e);
}
};
this.createQuestion = new Action() {
public void call(Element e) {
createQuestion(e);
}
};
this.parser = getSyntax();
}
/**
* Defines the standard syntax for the xml import.
*
* @return
*/
private ElementParser getSyntax() {
Syntax syntax = new Syntax("syntax");
Syntax qa = syntax.by("QA");
qa
.by("users")
.by("user")
.read("displayname")
.read("age")
.read("ismoderator")
.read("email")
.read("password")
.read("aboutme")
.read("location")
.read("website")
.call(this.createUser);
qa
.by("questions")
.by("question")
.read("ownerid")
.read("creationdate")
.read("lastactivity")
.read("body")
.read("title")
.read("lastedit")
.read("acceptedanswer")
.by("tags")
.read("tag")
.end()
.call(this.createQuestion);
qa
.by("answers")
.by("answer")
.read("ownerid")
.read("questionid")
.read("creationdate")
.read("lastactivity")
.read("body")
.read("title")
.read("lastedit")
.read("accepted")
.call(this.createAnswer);
qa.call(this.makeConnections);
return new ElementParser(syntax);
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes atts) {
Map<String, String> attributes = new HashMap();
for (int i = 0; i < atts.getLength(); i++) {
String attrname = atts.getLocalName(i);
attributes.put(attrname, atts.getValue(i));
}
this.parser.start(qName, attributes);
}
@Override
public void characters(char[] str, int start, int length) {
this.parser.text(String.copyValueOf(str, start, length));
}
@Override
public void endElement(String uri, String localName, String qName) {
this.parser.end();
}
/**
* Create an User from the given element.
*
* @param e
*/
private void createUser(Element e) {
String name = e.getText("displayname");
String password = e.getText("password");
String email = e.getText("email");
User user = this.db.users().register(name, password, email);
Integer age = this.toIntValue(e.getText("age"));
if (age != -1) {
Calendar pseudobirthday = GregorianCalendar.getInstance();
pseudobirthday.add(Calendar.YEAR, -age);
user.setDateOfBirth(pseudobirthday.getTime());
}
user.setBiography(e.getText("aboutme"));
user.setWebsite(e.getText("website"));
user.setModerator(e.getText("ismoderator").equals("true"), this.db
.users().getModeratorMailbox());
int id = this.toIntValue(e.getArg("id"));
this.idUserBase.put(id, user);
}
/**
* Create a question or throw a SemanticError, if it does
* not define a valid question.
*
* @param e
*/
private void createQuestion(Element e) {
ProtoQuestion question = new ProtoQuestion();
try {
question.title = e.getText("title");
question.body = e.getText("body");
question.id = this.toIntValue(e.getArg("id"));
question.creation = new Date((new Long(e.getText("creationdate")))*1000);
question.ownerid = this.toIntValue(e.getText("ownerid"));
for (Element tagE : e.get("tags").get(0).get("tag")) {
question.tags += " " + tagE.getText();
}
this.protoquestions.add(question);
} catch (Throwable t) {
throw new SemanticError("Question #" + e.getArg("id")
+ " is invalid");
}
}
/**
* Create a answer or throw a SemanticError, if it does not define a valid
* question.
*
* @param e
*/
protected void createAnswer(Element e) {
ProtoAnswer answer = new ProtoAnswer();
try {
answer.ownerid = this.toIntValue(e.getText("ownerid"));
answer.questionid = this.toIntValue(e.getText("questionid"));
answer.title = e.getText("title");
answer.body = e.getText("body");
answer.accepted = e.getText("accepted").equals("true");
answer.id = this.toIntValue(e.getArg("id"));
answer.creation = new Date((new Long(e.getText("creationdate")))*1000);
this.protoanswers.add(answer);
} catch (Throwable t) {
throw new SemanticError("Answer #" + e.getArg("id")
+ " is invalid");
}
}
protected int toIntValue(String maybeInt) {
if (maybeInt == null || maybeInt.equals(""))
return -1;
return new Integer(maybeInt).intValue();
}
protected void makeConnections() {
for (ProtoQuestion protoquestion : this.protoquestions) {
User owner = this.idUserBase.get(protoquestion.ownerid);
if (owner == null && protoquestion.ownerid != -1)
throw new SemanticError("No valid user: "
+ protoquestion.ownerid);
String content = protoquestion.body;
content = "<h3>" + protoquestion.title + "</h3>\n" + content;
Question question = this.db.questions().add(owner, content);
question.setTimestamp(protoquestion.creation);
this.idQuestionBase.put(protoquestion.id, question);
question.setTagString(protoquestion.tags);
}
for (ProtoAnswer ans : this.protoanswers) {
Question question = this.idQuestionBase.get(ans.questionid);
if (question == null) {
question = this.db.questions().add(null,
"Anonymous question for imported answer #" + ans.id);
}
User owner = this.idUserBase.get(ans.ownerid);
String content = ans.body;
content = "<h3>" + ans.title + "</h3>\n" + content;
Answer answer = question.answer(owner, content);
answer.setTimestamp(ans.creation);
if (ans.accepted) {
question.setBestAnswer(answer);
}
}
}
private class ProtoQuestion {
private String tags = "";
private int ownerid;
private Date creation;
private String title;
private String body;
private int id;
}
private class ProtoAnswer {
private int ownerid, questionid;
private Date creation;
private String title;
private String body;
private boolean accepted;
private int id;
}
}