Java tutorial
/* * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>. * * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@users.sf.net>, * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * Copyright (C) 2004 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * * Contributor(s): */ package com.silverwrist.venice.core.impl; import java.io.*; import java.sql.*; import java.text.*; import java.util.*; import java.util.zip.*; import javax.xml.parsers.*; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.*; import org.xml.sax.*; import org.xml.sax.helpers.*; import com.silverwrist.util.*; import com.silverwrist.venice.core.ConferenceContext; import com.silverwrist.venice.core.internals.EngineBackend; import com.silverwrist.venice.db.*; import com.silverwrist.venice.except.*; class ConferencingImporter { /*-------------------------------------------------------------------------------- * Inner class containing topic data *-------------------------------------------------------------------------------- */ private class TopicData { /*==================================================================== * Attributes *==================================================================== */ private int m_index; // topic index private boolean m_frozen = false; // are we frozen? private boolean m_archived = false; // are we archived? private StringBuffer m_name; // topic name buffer /*==================================================================== * Constructor *==================================================================== */ TopicData(Attributes attrs) throws SAXException { boolean index_seen = false; for (int i = 0; i < attrs.getLength(); i++) { // grind the attributes into information String name = resolveName(attrs.getLocalName(i), attrs.getQName(i)); if (name.equals("index")) { // saw the index attribute try { // handle parsing the topic index if (index_seen) throw new SAXException("<topic> index specified twice"); m_index = Integer.parseInt(attrs.getValue(i)); index_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <topic> index"); } // end catch } // end if else if (name.equals("frozen")) { // parse the "frozen" attribute if (StringUtil.isBooleanTrue(attrs.getValue(i))) m_frozen = true; else if (StringUtil.isBooleanFalse(attrs.getValue(i))) m_frozen = false; else throw new SAXException("invalid boolean format for <topic> frozen"); } // end else if else if (name.equals("archived")) { // parse the "archived" attribute if (StringUtil.isBooleanTrue(attrs.getValue(i))) m_archived = true; else if (StringUtil.isBooleanFalse(attrs.getValue(i))) m_archived = false; else throw new SAXException("invalid boolean format for <topic> archived"); } // end else if // else ignore any unknown attributes } // end for if (!index_seen) throw new SAXException("<topic> index not specified"); m_name = new StringBuffer(); } // end constructor /*==================================================================== * External operations *==================================================================== */ final void appendNameData(char[] ch, int start, int length) { m_name.append(ch, start, length); } // end appendNameData final void resolveTopic() throws SAXException { Statement stmt_x = null; PreparedStatement stmt = null; boolean mid_create = false; try { // lock the tables we need stmt_x = m_conn.createStatement(); stmt_x.executeUpdate("LOCK TABLES confs WRITE, topics WRITE;"); // determine how to try to match the topic switch (m_match_method) { case ConferenceContext.IMPORT_MATCH_NUM: stmt = m_conn.prepareStatement("SELECT topicid FROM topics WHERE confid = ? AND num = ?;"); stmt.setInt(2, m_index); break; case ConferenceContext.IMPORT_MATCH_NAME: stmt = m_conn.prepareStatement("SELECT topicid FROM topics WHERE confid = ? AND name = ?;"); stmt.setString(2, m_name.toString()); break; default: throw new SAXException("internal error: invalid match method"); } // end switch stmt.setInt(1, m_conf_id); ResultSet rs = stmt.executeQuery(); if (rs.next()) { // found a matching topic - save the ID and return m_cur_topic = rs.getInt(1); SQLUtil.shutdown(rs); return; } // end if SQLUtil.shutdown(rs); if (!m_create_new) { // the topic was not matched and cannot be created - ignore it recordEvent("Topic <" + m_index + ".> (" + m_name.toString() + ") not matched or created - skipped."); m_cur_topic = -1; return; } // end if mid_create = true; // get the proper topic index SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement("SELECT top_topic + 1 FROM confs WHERE confid = ?;"); stmt.setInt(1, m_conf_id); rs = stmt.executeQuery(); if (rs.next()) m_index = rs.getInt(1); else m_index = 1; SQLUtil.shutdown(rs); // prepare the new topic entry SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement( "INSERT INTO topics (confid, num, creator_uid, top_message, frozen, archived, " + "createdate, lastupdate, name) VALUES (?, ?, ?, -1, ?, ?, ?, ?, ?);"); stmt.setInt(1, m_conf_id); stmt.setInt(2, m_index); stmt.setInt(3, m_uid); stmt.setInt(4, m_frozen ? 1 : 0); stmt.setInt(5, m_archived ? 1 : 0); java.util.Date now = new java.util.Date(); SQLUtil.setFullDateTime(stmt, 6, now); SQLUtil.setFullDateTime(stmt, 7, now); stmt.setString(8, m_name.toString()); stmt.executeUpdate(); // get the new topic ID rs = stmt_x.executeQuery("SELECT LAST_INSERT_ID();"); if (rs.next()) m_cur_topic = rs.getInt(1); else throw new SAXException("internal error: can\'t get new topic ID"); // update the last update and top topic fields SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement("UPDATE confs SET lastupdate = ?, top_topic = ? WHERE confid = ?;"); SQLUtil.setFullDateTime(stmt, 1, now); stmt.setInt(2, m_index); stmt.setInt(3, m_conf_id); stmt.executeUpdate(); } // end try catch (SQLException e) { // record the event and continue if (mid_create) recordEvent("error creating topic \"" + m_name.toString() + "\"", e); else recordEvent("error resolving topic <" + m_index + ".> (" + m_name.toString() + ")", e); m_cur_topic = -1; } // end catch finally { // shut down things SQLUtil.unlockTables(m_conn); SQLUtil.shutdown(stmt); SQLUtil.shutdown(stmt_x); } // end finally } // end resolveTopic } // end class TopicData /*-------------------------------------------------------------------------------- * Inner class containing post data *-------------------------------------------------------------------------------- */ private class PostData { /*==================================================================== * Attributes *==================================================================== */ private long m_id_in; private long m_parent_in; private int m_index; private int m_line_count; private String m_author; private java.util.Date m_date; private boolean m_hidden = false; private String m_scribble_by = null; private java.util.Date m_scribble_date = null; private StringBuffer m_pseud; private StringBuffer m_text; private int m_att_length = -1; private String m_att_type = null; private String m_att_filename = null; private File m_att_data = null; /*==================================================================== * Constructor *==================================================================== */ PostData(Attributes attrs) throws SAXException { boolean id_seen = false, parent_seen = false, index_seen = false, linecount_seen = false, author_seen = false; boolean date_seen = false; for (int i = 0; i < attrs.getLength(); i++) { // grind the attributes into information String name = resolveName(attrs.getLocalName(i), attrs.getQName(i)); if (name.equals("id")) { // get the post ID try { // handle parsing the topic index if (id_seen) throw new SAXException("<post> ID specified twice"); m_id_in = Long.parseLong(attrs.getValue(i)); id_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <post> ID"); } // end catch } // end if else if (name.equals("parent")) { // get the parent ID try { // handle parsing the topic index if (parent_seen) throw new SAXException("<post> parent ID specified twice"); m_parent_in = Long.parseLong(attrs.getValue(i)); parent_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <post> parent ID"); } // end catch } // end else if else if (name.equals("index")) { // get the index try { // handle parsing the topic index if (index_seen) throw new SAXException("<post> index specified twice"); m_index = Integer.parseInt(attrs.getValue(i)); index_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <post> index"); } // end catch } // end else if else if (name.equals("lines")) { // get the number of lines try { // handle parsing the topic index if (linecount_seen) throw new SAXException("<post> line count specified twice"); m_line_count = Integer.parseInt(attrs.getValue(i)); linecount_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <post> line count"); } // end catch } // end else if else if (name.equals("author")) { // get the post author as a string if (author_seen) throw new SAXException("<post> author name specified twice"); m_author = attrs.getValue(i); author_seen = true; } // end else if else if (name.equals("date")) { // get the post date try { // parse the ISO 8601 date if (date_seen) throw new SAXException("<post> date specified twice"); m_date = s_datefmt.parse(attrs.getValue(i)); date_seen = true; } // end try catch (ParseException e) { // date parsing failed throw new SAXException("invalid date format for <post> date", e); } // end catch } // end else if else if (name.equals("hidden")) { // parse the "hidden" attribute if (StringUtil.isBooleanTrue(attrs.getValue(i))) m_hidden = true; else if (StringUtil.isBooleanFalse(attrs.getValue(i))) m_hidden = false; else throw new SAXException("invalid boolean format for <post> hidden"); } // end else if // else ignore any unknown attributes } // end for if (!id_seen) throw new SAXException("<post> ID not specified"); if (!parent_seen) throw new SAXException("<post> parent ID not specified"); if (!index_seen) throw new SAXException("<post> index not specified"); if (!linecount_seen) throw new SAXException("<post> line count not specified"); if (!author_seen) throw new SAXException("<post> author name not specified"); if (!date_seen) throw new SAXException("<post> date not specified"); m_pseud = new StringBuffer(); m_text = new StringBuffer(); } // end constructor /*==================================================================== * External operations *==================================================================== */ final void dispose() { if (m_att_data != null) { // delete the temp file m_att_data.delete(); m_att_data = null; } // end if } // end dispose final void markScribbled(Attributes attrs) throws SAXException { boolean by_seen = false, date_seen = false; for (int i = 0; i < attrs.getLength(); i++) { // grind the attributes into information String name = resolveName(attrs.getLocalName(i), attrs.getQName(i)); if (name.equals("by")) { // get the user name that scribbled it if (by_seen) throw new SAXException("<scribbled> user name specified twice"); m_scribble_by = attrs.getValue(i); by_seen = true; } // end if else if (name.equals("date")) { // get the scribbled date try { // parse the ISO 8601 date if (date_seen) throw new SAXException("<scribbled> date specified twice"); m_scribble_date = s_datefmt.parse(attrs.getValue(i)); date_seen = true; } // end try catch (ParseException e) { // date parsing failed throw new SAXException("invalid date format for <scribbled> date", e); } // end catch } // end else if // else ignore any unknown attributes } // end for if (!by_seen) throw new SAXException("<scribbled> user name not specified"); if (!date_seen) throw new SAXException("<scribbled> date not specified"); } // end markScribbled final void appendPseud(char[] ch, int start, int length) { m_pseud.append(ch, start, length); } // end appendPseud final void appendText(char[] ch, int start, int length) { m_text.append(ch, start, length); } // end appendText final OutputStream openAttachment(Attributes attrs) throws SAXException { boolean length_seen = false, type_seen = false, filename_seen = false; for (int i = 0; i < attrs.getLength(); i++) { // grind the attributes into information String name = resolveName(attrs.getLocalName(i), attrs.getQName(i)); if (name.equals("length")) { // get the length try { // handle parsing the length if (length_seen) throw new SAXException("<attachment> length specified twice"); m_att_length = Integer.parseInt(attrs.getValue(i)); length_seen = true; } // end if catch (NumberFormatException e) { // this sucks! throw new SAXException("invalid numeric format for <attachment> length"); } // end catch } // end if else if (name.equals("type")) { // get the MIME type if (type_seen) throw new SAXException("<attachment> MIME type specified twice"); m_att_type = attrs.getValue(i); type_seen = true; } // end else if else if (name.equals("filename")) { // get the filename if (filename_seen) throw new SAXException("<attachment> filename specified twice"); m_att_filename = attrs.getValue(i); filename_seen = true; } // end else if // else ignore any unknown attributes } // end for if (!length_seen) throw new SAXException("<attachment> length not specified"); if (!type_seen) throw new SAXException("<attachment> MIME type not specified"); if (!filename_seen) throw new SAXException("<attachment> filename not specified"); boolean need_delete = false; try { // create the attachment data file and open it m_att_data = File.createTempFile("Attachment", null); need_delete = true; return new FileOutputStream(m_att_data); } // end try catch (IOException e) { // record the event and continue recordEvent("unable to open temporary file for attachment \"" + m_att_filename + "\"", e); m_att_length = -1; m_att_type = null; m_att_filename = null; if (need_delete) m_att_data.delete(); m_att_data = null; } // end catch return null; } // end openAttachment final void postThis() throws SAXException { /* private String m_att_type = null; private String m_att_filename = null; */ // get the creator UID int creator_uid = getUIDForUser(m_author); if (creator_uid == -1) { // add a note to the beginning of the text and change the creator UID m_text.insert(0, "<i>(Originally posted by \"" + m_author + "\")</i>\r\n\r\n"); m_line_count += 2; creator_uid = m_uid; } // end if // get the scribble UID int scribble_uid = -1; if (m_scribble_by != null) { // get the UID of the scribbling user scribble_uid = getUIDForUser(m_scribble_by); if (scribble_uid == -1) scribble_uid = m_uid; } // end if Statement stmt_x = null; PreparedStatement stmt = null; try { // lock the tables we'll need stmt_x = m_conn.createStatement(); stmt_x.executeUpdate( "LOCK TABLES confs WRITE, confsettings WRITE, topics WRITE, topicsettings WRITE, " + "posts WRITE, postdata WRITE, postattach WRITE;"); // get the new index for the post stmt = m_conn.prepareStatement("SELECT top_message + 1 FROM topics WHERE topicid = ?;"); stmt.setInt(1, m_cur_topic); ResultSet rs = stmt.executeQuery(); if (rs.next()) m_index = rs.getInt(1); else m_index = 0; SQLUtil.shutdown(rs); // prepare the initial post entry SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement( "INSERT INTO posts (parent, topicid, num, linecount, creator_uid, posted, " + "hidden, pseud) VALUES (?, ?, ?, ?, ?, ?, ?, ?);"); stmt.setLong(1, getPostMapping(m_parent_in)); stmt.setInt(2, m_cur_topic); stmt.setInt(3, m_index); stmt.setInt(4, m_line_count); stmt.setInt(5, creator_uid); SQLUtil.setFullDateTime(stmt, 6, m_date); stmt.setInt(7, m_hidden ? 1 : 0); stmt.setString(8, m_pseud.toString()); stmt.executeUpdate(); // get the new post ID long pid; rs = stmt.executeQuery("SELECT LAST_INSERT_ID();"); if (rs.next()) pid = rs.getLong(1); else throw new SAXException("internal error: cannot get new post ID"); setPostMapping(m_id_in, pid); // add the post data SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement("INSERT INTO postdata(postid, data) VALUES (?, ?);"); stmt.setLong(1, pid); stmt.setString(2, m_text.toString()); stmt.executeUpdate(); if (m_scribble_date != null) { // update the post to reflect scribble dates SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement( "UPDATE posts SET scribble_uid = ?, scribble_date = ? WHERE postid = ?;"); stmt.setInt(1, scribble_uid); SQLUtil.setFullDateTime(stmt, 2, m_scribble_date); stmt.setLong(3, pid); stmt.executeUpdate(); } // end if // update the topic to reflect the last update SQLUtil.shutdown(stmt); stmt = m_conn .prepareStatement("UPDATE topics SET top_message = ?, lastupdate = ? WHERE topicid = ?;"); stmt.setInt(1, m_index); SQLUtil.setFullDateTime(stmt, 2, m_date); stmt.setInt(3, m_cur_topic); stmt.executeUpdate(); // update the topic settings to reflect the last post date SQLUtil.shutdown(stmt); stmt = m_conn .prepareStatement("UPDATE topicsettings SET last_post = ? WHERE topicid = ? AND uid = ?;"); SQLUtil.setFullDateTime(stmt, 1, m_date); stmt.setInt(2, m_cur_topic); stmt.setInt(3, creator_uid); if (stmt.executeUpdate() < 1) { // We didn't have a topic settings record yet; create one. SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement( "INSERT INTO topicsettings (topicid, uid, last_post) VALUES (?, ?, ?);"); stmt.setInt(1, m_cur_topic); stmt.setInt(2, creator_uid); SQLUtil.setFullDateTime(stmt, 3, m_date); stmt.executeUpdate(); } // end if // Update the "last update" date of the conference. SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement("UPDATE confs SET lastupdate = ? WHERE confid = ?;"); SQLUtil.setFullDateTime(stmt, 1, m_date); stmt.setInt(2, m_conf_id); // Update the "last post" timestamp in conference settings. SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement( "UPDATE confsettings SET last_read = ?, last_post = ? WHERE confid = ? " + "AND uid = ?;"); SQLUtil.setFullDateTime(stmt, 1, m_date); SQLUtil.setFullDateTime(stmt, 2, m_date); stmt.setInt(3, m_conf_id); stmt.setInt(4, creator_uid); if (stmt.executeUpdate() < 1) { // no "confsettings" record found - create one SQLUtil.shutdown(stmt); stmt = m_conn.prepareStatement("INSERT INTO confsettings (confid, uid, last_read, last_post) " + "VALUES (?, ?, ?, ?);"); stmt.setInt(1, m_conf_id); stmt.setInt(2, creator_uid); SQLUtil.setFullDateTime(stmt, 3, m_date); SQLUtil.setFullDateTime(stmt, 4, m_date); } // end if if (m_att_type != null) { // there's an attachment to be stored! First see if we need to compress the data. InputStream stm = null; int stg_method, stmlen; try { // handle all the IO manipulations here if (m_engine.isNoCompressMimeType(m_att_type)) { // don't compress, just copy the file data directly stm = new FileInputStream(m_att_data); stmlen = m_att_length; stg_method = 0; } // end if else { // create the buffer to compress into ByteArrayOutputStream bytestm = new ByteArrayOutputStream(m_att_length); GZIPOutputStream gzipstm = new GZIPOutputStream(bytestm); IOUtil.copy(new FileInputStream(m_att_data), gzipstm); gzipstm.finish(); byte[] buffer = bytestm.toByteArray(); stmlen = buffer.length; stm = new ByteArrayInputStream(buffer); IOUtil.shutdown(gzipstm); stg_method = 1; } // end else // add the attachment record to the database! stmt = m_conn .prepareStatement("INSERT INTO postattach(postid, datalen, last_hit, stgmethod, " + "filename, mimetype, data) VALUES (?, ?, ?, ?, ?, ?, ?);"); stmt.setLong(1, pid); stmt.setInt(2, m_att_length); SQLUtil.setFullDateTime(stmt, 3, new java.util.Date()); stmt.setInt(4, stg_method); stmt.setString(5, m_att_filename); stmt.setString(6, m_att_type); stmt.setBinaryStream(7, stm, stmlen); stmt.executeUpdate(); } // end try catch (IOException e) { // bail out from attachment if we screw this up recordEvent("error getting/compressing attachment data", e); return; } // end catch } // end if } // end try catch (SQLException e) { // record a database error here recordEvent("error posting message", e); } // end catch finally { // close everything down SQLUtil.unlockTables(m_conn); SQLUtil.shutdown(stmt_x); SQLUtil.shutdown(stmt); } // end finally } // end postThis } // end class PostData /*-------------------------------------------------------------------------------- * Inner class containing XML listener *-------------------------------------------------------------------------------- */ private class Listener extends DefaultHandler { /*==================================================================== * Attributes *==================================================================== */ private TopicData m_topicdata = null; private PostData m_postdata = null; private OutputStream m_attachment = null; private byte[] m_leftover = null; /*==================================================================== * Constructor *==================================================================== */ Listener() { super(); } // end constructor /*==================================================================== * Internal operations *==================================================================== */ private final void handleAttachmentChars(char[] ch, int start, int length) throws SAXException { if (m_attachment == null) return; // no attachment - don't bother byte[] code_data = null; try { // convert from an array of characters to an array of bytes String tmp = new String(ch, start, length); code_data = tmp.getBytes("US-ASCII"); } // end try catch (UnsupportedEncodingException e) { // this should never happen throw new SAXException("?!?!?!? WTF? US-ASCII is OK!", e); } // end catch int len_leftover = (m_leftover != null) ? m_leftover.length : 0; if (len_leftover == 0) m_leftover = null; int t = (len_leftover + code_data.length) / 4; int ilen = t * 4; int len_new_leftover = (len_leftover + code_data.length) - ilen; byte[] input_data = null; if (ilen > 0) { // create a perfect input group of 4-byte blocks input_data = new byte[ilen]; if (len_leftover > 0) System.arraycopy(m_leftover, 0, input_data, 0, len_leftover); System.arraycopy(code_data, 0, input_data, len_leftover, ilen - len_leftover); } // end if if (len_new_leftover > 0) { // set the leftovers aside m_leftover = new byte[len_new_leftover]; System.arraycopy(code_data, code_data.length - len_new_leftover, m_leftover, 0, len_new_leftover); } // end if else // no leftovers m_leftover = null; if (ilen == 0) return; // no data to write try { // decode the base-64 data and write it! m_attachment.write(Base64.decodeBase64(input_data)); } // end try catch (IOException e) { // error during the decode or write throw new SAXException("error decoding and writing attachment data", e); } // end catch } // end handleAttachmentChars private final void finishAttachment() throws SAXException { if ((m_leftover != null) && (m_leftover.length == 0)) m_leftover = null; if (m_leftover == null) return; try { // decode the base-64 data and write it! m_attachment.write(Base64.decodeBase64(m_leftover)); } // end try catch (IOException e) { // error during the decode or write throw new SAXException("error decoding and writing attachment data", e); } // end catch finally { // null out the leftover array m_leftover = null; } // end finally } // end finishAttachment /*==================================================================== * Overrides from class DefaultHandler *==================================================================== */ public void setDocumentLocator(Locator locator) { m_locator = locator; } // end setDocumentLocator public void startDocument() { m_state = ST_PRESTART; m_cur_topic = -1; } // end startDocument public void endDocument() throws SAXException { if (m_state != ST_END) throw new SAXException("no content found"); m_locator = null; } // end endDocument public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { String name = resolveName(localName, qName); switch (m_state) { case ST_PRESTART: if (!(name.equals("vcif"))) throw new SAXException("<vcif> expected"); m_state = ST_INSTART; break; case ST_INSTART: if (!(name.equals("topic"))) throw new SAXException("<topic> expected"); m_topicdata = new TopicData(attributes); m_state = ST_INTOPIC_1; break; case ST_INTOPIC_1: if (!(name.equals("topicname"))) throw new SAXException("<topicname> expected"); m_state = ST_INTOPIC_NAME; break; case ST_INTOPIC: if (!(name.equals("post"))) throw new SAXException("<post> expected"); if (m_cur_topic > 0) m_postdata = new PostData(attributes); m_state = ST_INPOST; break; case ST_INPOST: if (name.equals("scribbled")) { // mark post as scribbled if (m_postdata != null) m_postdata.markScribbled(attributes); m_state = ST_SCRIBBLETAG; } // end if else if (name.equals("pseud")) m_state = ST_PSEUD; else if (name.equals("text")) m_state = ST_TEXT; else if (name.equals("attachment")) { // open the attachment if (m_postdata != null) m_attachment = m_postdata.openAttachment(attributes); m_state = ST_ATTACHMENT; } // end else if else throw new SAXException("<scribbled>, <pseud>, <text>, or <attachment> expected"); break; default: throw new SAXException( "startElement state error (state = " + m_state + ", elt = \"" + name + "\")"); } // end switch } // end startElement public void endElement(String uri, String localName, String qName) throws SAXException { String name = resolveName(localName, qName); switch (m_state) { case ST_INSTART: if (!(name.equals("vcif"))) throw new SAXException("</vcif> expected"); m_state = ST_END; break; case ST_INTOPIC_NAME: if (!(name.equals("topicname"))) throw new SAXException("</topicname> expected"); if (m_topicdata == null) throw new SAXException("internal error: no topic data"); m_topicdata.resolveTopic(); m_topicdata = null; m_state = ST_INTOPIC; break; case ST_INTOPIC: if (!(name.equals("topic"))) throw new SAXException("</topic> expected"); m_cur_topic = -1; m_state = ST_INSTART; break; case ST_INPOST: if (!(name.equals("post"))) throw new SAXException("</post> expected"); if (m_postdata != null) { // post complete - make it try { // post the message! m_postdata.postThis(); } // end try finally { // make sure we dispose the postdata m_postdata.dispose(); m_postdata = null; } // end finally } // end if m_state = ST_INTOPIC; break; case ST_SCRIBBLETAG: if (!(name.equals("scribbled"))) throw new SAXException("</scribbled> expected"); m_state = ST_INPOST; break; case ST_PSEUD: if (!(name.equals("pseud"))) throw new SAXException("</pseud> expected"); m_state = ST_INPOST; break; case ST_TEXT: if (!(name.equals("text"))) throw new SAXException("</text> expected"); m_state = ST_INPOST; break; case ST_ATTACHMENT: if (!(name.equals("attachment"))) throw new SAXException("</attachment> expected"); try { // finish the attachment finishAttachment(); } // end try finally { // close it all down IOUtil.shutdown(m_attachment); m_attachment = null; m_state = ST_INPOST; } // end finally break; default: throw new SAXException("endElement state error (state = " + m_state + ", elt = \"" + name + "\")"); } // end switch } // end endElement public void characters(char[] ch, int start, int length) throws SAXException { switch (m_state) { case ST_INTOPIC_NAME: if (m_topicdata != null) m_topicdata.appendNameData(ch, start, length); break; case ST_PSEUD: if (m_postdata != null) m_postdata.appendPseud(ch, start, length); break; case ST_TEXT: if (m_postdata != null) m_postdata.appendText(ch, start, length); break; case ST_ATTACHMENT: handleAttachmentChars(ch, start, length); break; default: break; // ignore characters } // end switch } // end characters public void warning(SAXParseException e) throws SAXException { recordEvent("parser warning", e); super.warning(e); } // end warning public void error(SAXParseException e) throws SAXException { recordEvent("parser error", e); super.error(e); } // end error public void fatalError(SAXParseException e) throws SAXException { recordEvent("parser fatal error", e); super.fatalError(e); } // end fatalError } // end class Listener /*-------------------------------------------------------------------------------- * Static data members *-------------------------------------------------------------------------------- */ private static Logger logger = Logger.getLogger(ConferencingImporter.class); private static final DateFormat s_datefmt; private static final int ST_PRESTART = 0; private static final int ST_INSTART = 1; private static final int ST_END = 2; private static final int ST_INTOPIC_1 = 3; private static final int ST_INTOPIC_NAME = 4; private static final int ST_INTOPIC = 5; private static final int ST_INPOST = 6; private static final int ST_SCRIBBLETAG = 7; private static final int ST_PSEUD = 8; private static final int ST_TEXT = 9; private static final int ST_ATTACHMENT = 10; /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ private int m_uid; // UID we're creating stuff with private int m_conf_id; // conference ID we're importing to private int m_match_method; // conference match method private boolean m_create_new; // create new topic if it doesn't exist? private Connection m_conn; // SQL database connection private EngineBackend m_engine; // the engine private LinkedList m_events = null; // the events reported by the parser private Locator m_locator = null; // locator for events private int m_state; // current state of the parser private int m_cur_topic; // current topic ID private HashMap m_post_map; // mapping from old to new post IDs /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ ConferencingImporter(int uid, int conf_id, int match_method, boolean create_new, Connection conn, EngineBackend engine) { m_uid = uid; m_conf_id = conf_id; m_match_method = match_method; m_create_new = create_new; m_conn = conn; m_engine = engine; m_post_map = new HashMap(); } // end constructor /*-------------------------------------------------------------------------------- * Internal operations *-------------------------------------------------------------------------------- */ private static final String resolveName(String local_name, String q_name) { if ((local_name != null) && (local_name.length() > 0)) return local_name; else return q_name; } // end resolveName private final int getUIDForUser(String username) { PreparedStatement stmt = null; ResultSet rs = null; try { // straightforward query stmt = m_conn.prepareStatement("SELECT uid FROM users WHERE username = ?;"); stmt.setString(1, username); rs = stmt.executeQuery(); if (rs.next()) return rs.getInt(1); else return -1; } // end try catch (SQLException e) { // whoops: big error! recordEvent("error getting UID for user name \"" + username + "\"", e); return -1; } // end catch finally { // shut everything down SQLUtil.shutdown(rs); SQLUtil.shutdown(stmt); } // end finally } // end getUIDForUser private final void recordEvent(String message) { StringBuffer buf = new StringBuffer("["); buf.append(m_locator.getLineNumber()).append(", ").append(m_locator.getColumnNumber()).append("] "); buf.append(message); if (m_events == null) m_events = new LinkedList(); m_events.addLast(buf.toString()); } // end recordEvent private final void recordEvent(String message, Throwable t) { StringBuffer buf = new StringBuffer("["); buf.append(m_locator.getLineNumber()).append(", ").append(m_locator.getColumnNumber()).append("] "); buf.append(message).append(" - ").append(t.getMessage()); if (m_events == null) m_events = new LinkedList(); m_events.addLast(buf.toString()); } // end recordEvent private final long getPostMapping(long old) { if (old == 0) return 0; Long rc = (Long) (m_post_map.get(new Long(old))); return (rc == null) ? 0 : rc.longValue(); } // end getPostMapping private final void setPostMapping(long old, long nu) { m_post_map.put(new Long(old), new Long(nu)); } // end setPostMapping /*-------------------------------------------------------------------------------- * External operations *-------------------------------------------------------------------------------- */ final List importMessages(InputStream xmlstm) throws DataException { try { // create a SAX parser and let it loose on the input data with our listener SAXParserFactory fact = SAXParserFactory.newInstance(); fact.setNamespaceAware(false); fact.setValidating(false); SAXParser parser = fact.newSAXParser(); parser.parse(xmlstm, new Listener()); } // end try catch (ParserConfigurationException e) { // configuration error throw new DataException("Error configuring XML parser for message import: " + e.getMessage(), e); } // end catch catch (SAXException e) { // give an error message throw new DataException("Error importing messages: " + e.getMessage(), e); } // end catch catch (IOException e) { // I/O error in parsing! throw new DataException("Error importing messages: " + e.getMessage(), e); } // end catch if (m_events == null) return Collections.EMPTY_LIST; ArrayList rc = new ArrayList(m_events); m_events = null; return Collections.unmodifiableList(rc); } // end importMessages /*-------------------------------------------------------------------------------- * Static initializer *-------------------------------------------------------------------------------- */ static { // create an ISO 8601 date formatter SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); df.setTimeZone(new SimpleTimeZone(0, "UTC")); df.setLenient(false); s_datefmt = df; } // end static initializer } // end class ConferencingImporter