package com.transmem.action; import java.util.logging.Logger; import javax.servlet.ServletException; import java.io.IOException; import java.sql.SQLException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import com.transmem.nlp.*; import com.transmem.data.db.Databases; import com.transmem.data.db.Projects; import com.transmem.data.db.Articles; import com.transmem.data.db.Sources; import com.transmem.data.db.Sentences; import com.transmem.data.db.Languages; import com.transmem.data.db.Users; /** * Action class for share the translation of an article with others by storing * the sentences and translations into the corpus. * * This action is invoked by Ajax engine, so it should return differently. * * @version 0.1 * @author Ted Wen * @date Jan. 2007 */ public class ShareTranslation extends BaseAction { private Logger log_ = Logger.getLogger(ShareTranslation.class.getName()); private ILinguist source_, target_; private ResultSet rs_, rs1_, rs2_; public ShareTranslation() { super(); } /** * Accept user's translation for the current sentence. * * <p class="doc"> * <ul> * <li>Request parameters * <ul> * <li><b>aid</b> - string of article ID</li> * <li><b>domain</b> - domain of the aricle</li> * <li><b>permit</b> - who to expose the sentences</li> * </ul> * </li> * <li>Session attributes * <ul> * <li><b>user</b> - Users object created after login or register.</li> * <li><b>articles</b> - array list of articles.</li> * </ul> * </li> * <li>Response page * <ul> * <li> - not used as a whole web page but for Ajax response</li> * </ul> * </li> * </ul> * </p> * * @param param - HttpServletRequest object * @param response - HttpServletResponse object */ public void execute(ServletParams param) throws ServletException, IOException { log_.entering("ShareTranslation","execute"); Session session = param.getSession(); Users usr = session.getUser(); if (usr == null) { param.sendError(MessageCode.ERR_NOT_LOGIN); return; } int uid = usr.getUserID(); int group = usr.getGroup(); String said = param.getParameter("aid"); if (said == null) { log_.warning("ShareTranslation called without aid parameter"); return; } ArrayList<Articles> articles = session.getArticleList(); if (articles == null) { log_.severe("session.getArticleList() return null"); param.sendError(MessageCode.ERR_NO_ARTICLE); return; } int aid = 0; Articles article = null; try { aid = Integer.parseInt(said); for (Articles a : articles) { if (a.getArticleID()==aid) { article = a; break; } } } catch (NumberFormatException e) { log_.severe("Invalid aid:"+said); return; } if (article == null) { log_.severe("Article given by ID "+said+" not found in list"); return; } String langpair = article.getLangPair();//ENZH String scode = langpair.substring(0,2); //EN String tcode = langpair.substring(2); //ZH String domain = param.getParameter("domain"); String permit = param.getParameter("permit"); int npermit = 0; if (permit==null || permit.equals("P")) npermit = 0; else if (permit.equals("G")) npermit = group; else npermit = -1; log_.info("About to save aid="+aid+",lang="+langpair+",domain="+domain+",permit="+permit+",user="+uid); Connection conn = null; try { createIndexer(scode, tcode); log_.info("createIndexer("+scode+","+tcode+") done"); conn = getConnection(param,Databases.CATEGORY_USER,true); conn.setAutoCommit(false); //get table name for the langpair String tableName = getTableName(conn, langpair); boolean swap = tableName.indexOf(scode)>2; log_.info("Table name = "+tableName+", swap = "+swap); //delete sentences from corpus if already there ArrayList<Long> sids = revokeSentences(conn, tableName, aid); log_.info("SIDs revoked : "+sids.size()); //load sentences for the article and save to T_ENZH and T_Sources ArrayList<Sentences> sents = querySentences(conn, aid); log_.info("Sentences in article : "+sents.size()); //copy sentences to corpus String seqname = "S"+tableName.substring(1); //create resultsets for corpus, indexes createResultSets(conn, scode, tcode, tableName); //log_.info("createResultSets() finished"); for (int i=0; i<sents.size(); i++) { long sid = 0; if (i >= sids.size()) sid = getSequenceLong(conn, seqname); else sid = sids.get(i).longValue(); //saveSentence(conn, sid, sents.get(i)); Sentences sent = sents.get(i); if (sent.getSentence()==null || sent.getTranslation()==null) continue; //log_.info("About to save sentence "+i+", sid="+sid); saveUnit(sid, sent, swap, aid, uid, domain, npermit); //log_.info("Sentence "+i+" saved, calling index()"); index(this.rs1_, source_, sid, swap?sent.getTranslation():sent.getSentence()); //log_.info("Index for sentence "+i+" finished"); //index(this.rs2_, source_, sid, swap?sent.getSentence():sent.getTranslation()); } //save into t_sources log_.info("Saving source: aid="+aid+", title="+article.getTitle()); saveSource(conn, aid, article.getTitle()); //done log_.info("Sentences saved for aid "+aid); conn.commit(); } catch (SQLException x) { try { conn.rollback(); } catch (SQLException e) {} log_.severe("SQLException when getConnection(USER)"+x); param.sendError(MessageCode.ERR_DB_CONNECT); } catch (LanguageException le) { log_.severe(le.toString()); param.sendError("linguistic methods failed: "+le.toString()); } finally { try { if (this.rs_ != null) this.rs_.close(); if (this.rs1_ != null) this.rs1_.close(); if (this.rs2_ != null) this.rs2_.close(); if (conn != null) conn.close(); } catch (SQLException e) {} } } protected ArrayList<Sentences> querySentences(Connection conn, int aid) throws SQLException { Sentences st = new Sentences(conn); return st.queryByArticle(aid); } /** * Collect the SIDs used by previous upload for reuse, and delete those for repopulation. * @param conn * @param tableName * @param aid - Article ID * @return array list of example sentence IDs for reuse */ protected ArrayList<Long> revokeSentences(Connection conn, String tableName, int aid) throws SQLException { ArrayList<Long> sids = new ArrayList<Long>(); String sql = "SELECT F_SID FROM "+tableName+" WHERE F_From="+aid; Statement stmt = null; try { stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { sids.add(new Long(rs.getLong(1))); } rs.close(); stmt.close(); log_.info("selected "+sids.size()+" sentences"); //delete these sentences if (sids.size() > 0) { sql = "DELETE FROM "+tableName+" WHERE F_From="+aid; stmt = conn.createStatement(); stmt.executeUpdate(sql); stmt.close(); log_.info("deleted these from the table"); } //delete from t_sources as well Sources src = new Sources(conn); src.delete(aid); log_.info("and deleted from t_source table"); } catch (SQLException e) { log_.warning(e.toString()); } return sids; } protected String getTableName(Connection conn, String langpair) throws SQLException { Languages ls = new Languages(conn); return ls.queryTableName(langpair); } //--- indexing and sentence saving units can be separate into another class --------- protected void createIndexer(String scode, String tcode) throws LanguageException { LanguageManager.loadLangNames(); source_ = LanguageManager.createLinguist(scode, LanguageManager.INDEXER); target_ = LanguageManager.createLinguist(tcode, LanguageManager.INDEXER); } /** * Create three resultset: corpus table, source index table, target index table. * The source index table maps the source language in the corpus, not the sentence source. * So if the corpus is ENZH and the sentence is Chinese-English, then the source of sentence * is the target of the corpus. In other words, rs1_ is always EN, rs2_ is always ZH_. */ protected void createResultSets(Connection conn, String scode, String tcode, String tablename) throws SQLException { String sql = "SELECT * FROM "+tablename+" WHERE F_SID = 0"; Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); this.rs_ = stmt.executeQuery(sql); Statement stmt1 = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); this.rs1_ = stmt1.executeQuery("SELECT * FROM "+tablename+"X WHERE F_Word='x'"); Statement stmt2 = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); String xtab2 = "T_"+tablename.substring(4,6)+tablename.substring(2,4)+"X"; log_.info("2nd index table name is "+xtab2); this.rs2_ = stmt2.executeQuery("SELECT * FROM "+xtab2+" WHERE F_Word='x'"); } protected void index(ResultSet rs, ILinguist linguist, long sid, String sent) throws SQLException, LanguageException { //System.out.println(sent); String[] words = linguist.indexkeys(sent); if (words.length > 0) { Map<String,String> dup = new HashMap<String,String>(); int i = 0; for (String word : words) { //System.out.print(word+" "); if (!dup.containsKey(word)) { dup.put(word, word); rs.moveToInsertRow(); rs.updateString("F_Word", word); rs.updateLong("F_SID", sid); rs.updateInt("F_Offset", i++); rs.insertRow(); } } //System.out.println(); } } protected void saveUnit(long sid, Sentences sent, boolean swap, int aid, int uid, String domain, int permit) throws SQLException { //System.out.println("SID = "+sid); this.rs_.moveToInsertRow(); this.rs_.updateLong("F_SID", sid); this.rs_.updateString("F_Source", swap?sent.getTranslation():sent.getSentence()); this.rs_.updateString("F_Target", swap?sent.getSentence():sent.getTranslation()); this.rs_.updateString("F_Domain", domain); this.rs_.updateInt("F_From", aid); this.rs_.updateInt("F_Permit", permit); this.rs_.updateInt("F_Owner", uid); this.rs_.insertRow(); } protected void saveSource(Connection conn, int from, String sname) throws SQLException { assert(conn != null); PreparedStatement ps = null; try { String sql = "INSERT INTO T_Sources(F_SourceID,F_Name) VALUES(?,?)"; ps = conn.prepareStatement(sql); ps.setInt(1, from); String s = (sname==null)?" ":sname; ps.setString(2, s); ps.executeUpdate(); } catch (SQLException e) { throw new SQLException(e.getMessage()); } finally { if (ps != null) try { ps.close(); } catch (SQLException x) {} } } }