User:DabMachine/Code

/* Here's my code for the DabMachine. It's a quick hack, but if you're interested feel free to have a try It is hereby published under a CC-BY-SA license. -- Ravn 16:31, 1 March 2006 (UTC) To get it running, save to a file called "wikidab.java". Fill in your user name and password. Find the package org.htmlparser.util at http://htmlparser.org. Compile. Be careful. There are no warning messages before you actually modify a wiki page. === Known bugs === * Exceptions when failing to load a page are not yet caught. You may have to click on "more" another time, if it happens, but it usually does not get in your way. === Known issues === * The button colouring algorithm is still quite basic. I was planning to use LSI + Clustering later * There is no documentation yet. Nada. Zilch. Unless you happen to speak Java. <!--



import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.util.regex.*; import java.nio.charset.*; import org.htmlparser.util.Translate;

public class wikidab extends JFrame implements ActionListener {

/*Cookies are needed to log in and identify yourself in Wikipedia. If you disable this option, the DabMachine will post anonymous entries. This is not recommended, as it hinders the work of the RC patrol.*/ public static final boolean WANTCOOKIES=false; public static final String userName="XXX"; public static final String password="XXX"; /*Delay in ms between two pages being prefetched */ public static final int PREFETCH_DELAY=750; public static void main(String[] args) { try { new wikidab; } catch (Exception ex) { handleException(ex); } }  static void handleException (Exception ex) { System.err.println("An exception has occurred."); System.err.println("Please send the following output to fwd.wikidab-exceptions.20.ravn@neverbox.com"); System.err.println("and I will try to fix the error as soon as I can."); System.err.println("-- Begin of error information --"); //add wikidab version number and checksum of stack trace ex.printStackTrace; System.err.println("-- End of error information --"); } JTextField tf_dab; JButton b_go, b_more, b_skip, b_touch; JPanel p_wlh, m_dab; JTextArea ta_status, ta_wlh; JSplitPane x_horz, x_vert; JScrollPane sp_wlh, sp_dab, sp_status;

Vector v_wlh; Hashtable pageCache=new Hashtable; Vector wikiLinks, wikiButtons, wlhLinks; Vector failedList=new Vector; Hashtable linkContext; Prefetcher prefetcher; public wikidab { super ("DabMachine a0.2 by User:Ravn"); initLayout; show;

if (WANTCOOKIES) logIn(userName, password); } void initLayout { Container c=this.getContentPane; SpringLayout layout=new SpringLayout; c.setLayout(layout); tf_dab=new JTextField(40); b_go=new JButton("Look up"); JLabel l_dab=new JLabel("DAB page: "); c.add(l_dab); c.add(tf_dab); c.add(b_go); layout.putConstraint(layout.WEST, l_dab, 0, layout.WEST, c); layout.putConstraint(layout.NORTH, l_dab, 3, layout.NORTH, c); layout.putConstraint(layout.NORTH, tf_dab, 3, layout.NORTH, c); layout.putConstraint(layout.WEST, tf_dab, 10, layout.EAST, l_dab); layout.putConstraint(layout.WEST, b_go, 10, layout.EAST, tf_dab); layout.putConstraint(layout.NORTH, b_go, 0, layout.NORTH, c); p_wlh=new JPanel; p_wlh.setLayout(new BoxLayout(p_wlh, BoxLayout.Y_AXIS)); ta_wlh=new JTextArea(5,30); ta_wlh.setLineWrap(true); ta_wlh.setWrapStyleWord(true); ta_wlh.setEditable(false); b_more=new JButton("more..."); b_skip=new JButton("skip..."); b_more.addActionListener(this); b_skip.addActionListener(this); p_wlh.add(ta_wlh); p_wlh.add(b_more); p_wlh.add(b_skip); p_wlh.doLayout; m_dab=new JPanel; //m_dab.setLayout(new BoxLayout(m_dab, BoxLayout.Y_AXIS)); ta_status=new JTextArea; p_wlh.setEnabled(false); m_dab.setEnabled(false); ta_status.setEditable(false); b_go.setEnabled(false); // locked until we've logged in   ta_status.insert("Please wait. Logging in.\n",0); ta_status.insert("Then enter the name of a disambiguation page to begin.\n",0); b_go.addActionListener(this); sp_wlh=new JScrollPane(p_wlh, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); sp_dab=new JScrollPane(m_dab, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); sp_status=new JScrollPane(ta_status, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); x_horz=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp_wlh, sp_dab); x_vert=new JSplitPane(JSplitPane.VERTICAL_SPLIT, x_horz, sp_status);

c.add(x_vert); layout.putConstraint(layout.NORTH, x_vert, 10, layout.SOUTH, l_dab); layout.putConstraint(layout.WEST, c, 0, layout.WEST, x_vert); layout.putConstraint(layout.EAST, c, 0, layout.EAST, x_vert); layout.putConstraint(layout.SOUTH, c, 0, layout.SOUTH, x_vert);

x_horz.setResizeWeight(0.5); x_vert.setResizeWeight(0.8); this.setSize(800,600); } public void actionPerformed(ActionEvent e) { if (e.getSource==b_go) { resetCurrentDab; String name=tf_dab.getText; ta_status.insert("Looking up "+name+"\n",0); this.repaint; retreiveDab(name); v_wlh=getWLH(name); linkContext=new Hashtable; showWLH; startPrefetching; } else if (e.getSource==b_more) { String dab=tf_dab.getText; String wlh=((Pair)v_wlh.elementAt(wlhDelta)).first; if (wlh.equals(contextString)) { contextSize+=100; /*variable declaration see below near getContext*/ } else { contextSize=200; contextString=wlh; }     String page=getEditableText(wlh).a;      int bestFit=getLinkContext(page); //what can we do with bestFit? ta_wlh.setText(wlh+"\n15:48, 10 March 2006 (UTC)\n"+getContext(dab,page)); } else if (e.getSource==b_skip) { v_wlh.removeElementAt(wlhDelta); if (v_wlh.size>0) { if (prefetcher!=null) prefetcher.fetchMore; showWLH; } else { ta_status.insert("Done disambiguating\n",0); printFailedInfo; resetCurrentDab; refreshStatus; }   } else  { JButton b=(JButton)e.getSource; int n=wikiButtons.indexOf(b); putLinkContext(n); if (n==0) { new Remover((Pair)v_wlh.elementAt(wlhDelta), tf_dab.getText, ta_status).start; } else { new Disambiguator((Pair)v_wlh.elementAt(wlhDelta), tf_dab.getText, b.getText, ta_status).start; }     v_wlh.removeElementAt(wlhDelta); if (v_wlh.size>0) { if (prefetcher!=null) prefetcher.fetchMore; showWLH; } else { ta_status.insert("Done disambiguating\n",0); printFailedInfo; resetCurrentDab; refreshStatus; }		} }  void startPrefetching { prefetcher=new Prefetcher((Pair[])v_wlh.toArray(new Pair[0]), pageCache); } String contextString=""; int contextSize=200; String getContext(String needle, String stack) { String nl=needle.toLowerCase; String sl=stack.toLowerCase; int pos=sl.indexOf(""+nl+""); if (pos==-1) pos=sl.indexOf("");   if (pos==-1) pos=sl.indexOf("[[ "+nl+" "); //another bad but supported syntax    if (pos==-1) pos=sl.indexOf(""+nl+" "); //another bad but supported syntax    if (pos==-1) pos=sl.indexOf(" "+nl+""); //another bad but supported syntax    if (pos==-1) return "--! NO CONTEXT !-- found on this page. You may want to check if there isn't perhaps a redirect pointing to your disambiguation page.";    int min=pos-contextSize;    int max=pos+contextSize;    if (min<0) min=0;    if (max>=stack.length) max=stack.length-1;    return stack.substring(min,max);  }

int wlhDelta=0; //we don't actually want to show the first WLH, but the first available void showWLH { String dab=tf_dab.getText; int delta; Object page=null; for(delta = 0; delta < 5; delta++) { if (delta>=v_wlh.size) break; Pair wlh=(Pair)v_wlh.elementAt(0); ta_wlh.setText(wlh.second); page=pageCache.get(wlh.first); if (page!=null) break; }   if (page!=null) { wlhDelta=delta; contextSize=100; String spage=((Quad)page).a;     int bestFit=getLinkContext(spage); //what can we do with bestFit? ta_wlh.append("\n15:48, 10 March 2006 (UTC)\n"+getContext(dab,spage)); } else { wlhDelta=0; resetContextHighlight; }   ta_wlh.repaint; } int getLinkContext(String page) { Pattern pt_link=Pattern.compile("\\[\\[([^\\]\\|]*)\\|?[^\\]]*\\]\\]"); //does not match this kind of link Matcher m_link=pt_link.matcher(page); int num=wikiButtons.size; float[] hits=new float[num]; wlhLinks=new Vector; while (m_link.find) { String link=page.substring(m_link.start(1), m_link.end(1)); wlhLinks.addElement(link); if (linkContext.get(link)==null) { //linkContext.put(link, new int[num]); linkContext.put(link, new int[num][2]); }     int[][] context=(int[][])linkContext.get(link); for(int i = 0; i < num; i++) { if (context[i][1]>0) { float p=(float)context[i][0]/(float)context[i][1]; //no need to calc this every time! hits[i]+=p; }     }    }    return highlightContext(hits); } float square(float f) { return f*f; } void printFailedInfo { if (failedList.size>0) { for(int i = 0; i < failedList.size; i++) { String url=((String[])failedList.elementAt(i))[0]; ta_status.insert(url+"\n",0); }     ta_status.insert("Failed to post:\n",0); } else { ta_status.insert("All POSTs succeeded.\n",0); }   failed=0; posted=0; refreshStatus; } int highlightContext(float[] hits) { //returns best fit float maxValue=0; int bestHit=0; for(int i = 0; i < hits.length; i++) { if (hits[i]>maxValue) { maxValue=hits[i]; bestHit=i; }   }    for(int i = 0; i < hits.length; i++) { Color c;     if (maxValue>0) { int r=(int)Math.floor(127+128*(maxValue-hits[i])/maxValue); int g=(int)Math.floor(127+128*hits[i]/maxValue); int b=127; c=new Color(r,g,b); } else c=Color.orange; JButton button=(JButton)wikiButtons.elementAt(i); button.setBackground(c); }   return bestHit; } void resetContextHighlight { if (wikiButtons==null) return; for(int i = 0; i < wikiButtons.size; i++) { JButton button=(JButton)wikiButtons.elementAt(i); button.setBackground(Color.lightGray); } }  void putLinkContext(int choice) { if (wlhLinks==null) return; for(int i = 0; i < wlhLinks.size; i++) { String link=(String)wlhLinks.elementAt(i); int[][] context=(int[][])linkContext.get(link); for(int j = 0; j < context.length; j++) { if (j==choice) context[j][0]+=1; context[j][1]++; }     linkContext.put(link,context);//necessary? maybe. } }

void retreiveDab(String name) { String page=getEditableText(urlEncode(name)).a;      if (page==null) { ta_status.insert("Failed retreiving page.\n",0); this.repaint; } else { wikiLinks=getWikiLinks(page); if (wikiLinks.size==0) { ta_status.insert("No Links found. Will load pages anyway and show the 'Remove' button\n",0); refreshStatus; }     if ((page.toLowerCase.indexOf("#redirect")>-1)) { String direct=(String)wikiLinks.elementAt(0); ta_status.insert("Page is a redirect to "+direct+"\n",0); refreshStatus; retreiveDab(direct); } else { layoutWikiLinks(wikiLinks); }		} }	String urlEncode(String name) { String encname=null; try { encname=URLEncoder.encode(name,"UTF-8"); } catch (Exception ex) { handleException(ex); }		return encname; }

void layoutWikiLinks(Vector links) { m_dab.removeAll; wikiButtons=new Vector; {     JButton b=new JButton("Remove this link"); wikiButtons.addElement(b); b.addActionListener(this); m_dab.add(b); }   {      String dabterm=tf_dab.getText; JButton b=new JButton("wikt:"+dabterm); wikiButtons.addElement(b); b.addActionListener(this); m_dab.add(b); }		for (int i=0;i<links.size;i++) { JButton b=new JButton((String)links.elementAt(i)); wikiButtons.addElement(b); b.addActionListener(this); m_dab.add(b); }		m_dab.setEnabled(true); m_dab.doLayout; }	Vector getWikiLinks(String edittext) { Vector result=new Vector; Pattern pt_link=Pattern.compile("\\[\\[([^\\]\\|]*)\\|?[^\\]]*\\]\\]"); Matcher m_link=pt_link.matcher(edittext); while (m_link.find) { String s=edittext.substring(m_link.start(1), m_link.end(1)); result.add(s); /*System.out.println(s); /*DEBUG*/ }		return result; } Vector getWLH(String name) { //TODO: exclude redirects and user pages wlhDelta=0; Vector result=new Vector; String text=fetchWebsite("http://en.wikipedia.org/w/index.php?title=Special:Whatlinkshere/"+urlEncode(name)+"&limit=500&from=0"); Pattern pt_link=Pattern.compile("]*>([^<]*)");		Matcher m_link=pt_link.matcher(text);		while (m_link.find) {			String link=text.substring(m_link.start(1), m_link.end(1));			String desc=text.substring(m_link.start(2), m_link.end(2));     if ((desc.indexOf("User:")==-1)&&(desc.indexOf("User talk:")==-1)) { //skips user pages        result.add(new Pair(link, desc));      }			/*System.out.println(s); /*DEBUG*/		}		return result;	}	Quad getEditableText(String name) {    ta_status.insert("Fetching "+name+"\n",0); /*DEBUG*/    refreshStatus;    Object o=pageCache.get(name);    if (o!=null) {      return (Quad)o;    }

String text=fetchWebsite("http://en.wikipedia.org/w/index.php?title="+name+"&action=edit"); if (text!=null) { Pattern pt_box=Pattern.compile("value=\"([0-9]*)\" name=\"wpStarttime\".*value=\"([0-9]*)\" name=\"wpEdittime\".*]*>(.*) .*value=\"([0-9a-f]*)\" name=\"wpEditToken", Pattern.DOTALL);			Matcher m_box=pt_box.matcher(text);			if (m_box.find) {       Quad data=new Quad(text.substring(m_box.start(3), m_box.end(3)), text.substring(m_box.start(1), m_box.end(1)), text.substring(m_box.start(2), m_box.end(2)), text.substring(m_box.start(4), m_box.end(4)));        pageCache.put(name,data);        ta_status.insert("-> Fetched "+name+"\n",0); /*DEBUG*/        refreshStatus;			  return data;			} else {				System.out.println("No match");			}      		}		return null;  }	String fetchWebsite(String url) {    try {      URLConnection con=new URL(url).openConnection;      con.setDoInput(true);      con.setUseCaches (false);      if (WANTCOOKIES) {        con.setRequestProperty("Cookie",getCookies); retreiveCookies(con.getHeaderFields); }

BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream, "UTF-8")); String str, text=""; while (null != ((str = in.readLine))) { text=text+str+"\n"; }     in.close; return Translate.decode(text); } catch (Exception ex) { handleException(ex); /*DEBUG*/ }		return null; } void resetCurrentDab { m_dab.removeAll; failedList.removeAllElements; if (v_wlh!=null) v_wlh.removeAllElements; if (wikiLinks!=null) wikiLinks.removeAllElements; if (wikiButtons!=null) wikiButtons.removeAllElements; if (wlhLinks!=null) wlhLinks.removeAllElements; if (linkContext!=null) linkContext.clear; m_dab.setLayout(new BoxLayout(m_dab, BoxLayout.Y_AXIS)); p_wlh.setEnabled(false); m_dab.setEnabled(false); ta_status.insert("Interface reset.\n",0); this.repaint; } int posted=0, failed=0, pending=0; Hashtable cookieJar=new Hashtable; String simpleCookieJar=""; void storeCookie(String cookie) { {     cookie=cookie.substring(0,cookie.indexOf(";")); int pos=cookie.indexOf("="); String key=cookie.substring(0,pos); String value=cookie.substring(pos+1); cookieJar.put(key,value); }   //regenerate full Cookie string Enumeration nume=cookieJar.keys; String cookies=""; while (nume.hasMoreElements) { String key=(String)nume.nextElement; if (!key.equals("cookieString")) { String value=(String)cookieJar.get(key); if (cookies.length>0) cookies+="; "; cookies+=key+"="+value; }   }    cookieJar.put("cookieString",cookies); } String getCookies { //Object o=cookieJar.get("cookieString"); //return (o==null)?"":(String)o; return simpleCookieJar; } void retreiveCookies(Map headers) { Iterator keys=headers.keySet.iterator; while (keys.hasNext) { Object o=keys.next; if (o instanceof String) { if (((String)o).equals("Set-Cookie")) { Iterator i=((Collection)headers.get(o)).iterator; while (i.hasNext) { String keks=(String)i.next; storeCookie(keks); }       }      }    }  }  void refreshStatus { ta_status.setCaretPosition(0); ta_status.repaint; } void logIn (String name, String password) { try { String cookie; {       URLConnection con=new URL("http://en.wikipedia.org/wiki/Special:Userlogin").openConnection; con.setDoInput(true); cookie=con.getHeaderField("Set-Cookie"); simpleCookieJar=cookie.substring(0, cookie.indexOf(";")); //the quick variant - always use login cookie, never refresh storeCookie(cookie); }     URLConnection con=new URL("http://en.wikipedia.org/w/index.php?title=Special:Userlogin&amp;action=submitlogin&amp;type=login").openConnection; con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); con.setRequestProperty("Cookie",getCookies); con.setDoInput(true); con.setDoOutput(true); con.setUseCaches (false); DataOutputStream out = new DataOutputStream (con.getOutputStream ); String content = "wpName="+name+ "&wpPassword="+password+ "&wpRemember=1"+ "&wpLoginattempt=Log+in"; out.writeBytes (content); out.flush ; out.close ; try { retreiveCookies(con.getHeaderFields); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream, "UTF-8")); String str; while (null != ((str = in.readLine))) { //System.out.println (str); }       in.close ; ta_status.insert("Login successful\n",0); b_go.setEnabled(true); // unlock refreshStatus; } catch (IOException ex) { ta_status.insert("FAILED to login\n",0); refreshStatus; } catch (Exception ex) { handleException(ex); }         } catch (Exception ex) { handleException(ex); } }	void POST (String url, String referrer, String text, String summary, String starttime, String edittime, String edittoken) { try { URLConnection con=new URL(url).openConnection; con.setDoOutput(true); con.setDoInput(true); con.setUseCaches (false); if (WANTCOOKIES) { con.setRequestProperty("Cookie",getCookies); }     con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); con.setRequestProperty("Referer", referrer); //Spelling error as per RFC 2068 DataOutputStream out = new DataOutputStream (con.getOutputStream ); String content = "wpSection="+ "&wpStarttime="+starttime+ "&wpEdittime="+edittime+ "&wpScrolltop=0"+ "&wpTextbox1="+URLEncoder.encode(text,"UTF-8")+ "&wpSummary="+URLEncoder.encode(summary,"UTF-8")+ "&wpMinoredit=1"+ "&wpWatchthis=0"+ "&wpEditToken="+edittoken+ "&wpSave=Save+page"; out.writeBytes (content); out.flush ; out.close ;

posted++; pending++; // Get response data. try { if (WANTCOOKIES) { retreiveCookies(con.getHeaderFields); }

BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream, "UTF-8")); String str; while (null != ((str = in.readLine))) { //System.out.println (str); }       in.close ; pending--; } catch (IOException ex) { failed++; String[] failedData=new String[7]; failedData[0]=url; failedData[1]=referrer; failedData[2]=text; failedData[3]=summary; failedData[4]=starttime; failedData[5]=edittime; failedData[6]=edittoken; failedList.add(failedData); ta_status.insert("FAILED to post: "+summary+"\n",0); refreshStatus; pending--; } catch (Exception ex) { handleException(ex); }	 } catch (Exception ex) { handleException(ex); }	} class PageFetcher extends Thread { String pagename; public PageFetcher(String name) { pagename=name; }   public void run { pageCache.put(pagename,getEditableText(pagename)); } }

class Prefetcher extends Thread { Pair[] wlh; Hashtable pageCache; int fetchmax=5; //how many pages to prefetch (do not edit!) int fetchmin=0; //which page to fetch currently public Prefetcher (Pair[] wlh, Hashtable pageCache) { this.wlh=wlh; this.pageCache=pageCache; start; }   public void fetchMore { if (fetchmax=wlh.length) break; if (getMin<getMax) { while (getMin+5<getMax) { incMin; }           String name=wlh[getMin].first; if (pageCache.get(name)==null) new PageFetcher(name).start; incMin; }         Thread.sleep(PREFETCH_DELAY); } catch (Exception ex) { handleException(ex); }     }    }  }  class Disambiguator extends Thread { Pair context; String from, to; JTextArea output; public Disambiguator (Pair context, String from, String to, JTextArea output) { this.context=context; this.from=from; this.to=to; this.output=output; }		public void run { output.insert("Disambiguating "+from+" to "+to+" on page "+context.second+"\n",0); Quad info=getEditableText(context.first); String page=info.a;			String rewrite=null; { /* 1) to -> to*/       Pattern pt_link=Pattern.compile("\\[\\["+from+"\\|"+to+"\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);        Matcher m_link=pt_link.matcher(page);        rewrite=m_link.replaceAll(""+to+"");      }      { /* 2) foo -> foo*/ Pattern pt_link=Pattern.compile("\\[\\["+from+"\\|([^\\]]*)\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Matcher m_link=pt_link.matcher(page); rewrite=m_link.replaceAll("$1"); }     int pos=from.toLowerCase.indexOf(to.toLowerCase); if (pos>-1) { { /* 3a) toabc -> toabc*/         Pattern pt_link=Pattern.compile("\\[\\[("+to+")([^\\]\\|]*)\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);          Matcher m_link=pt_link.matcher(rewrite);          rewrite=m_link.replaceAll("$1$2");        }      } else {        { /* 3b) from -> from*/ Pattern pt_link=Pattern.compile("\\[\\[("+from+")\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Matcher m_link=pt_link.matcher(rewrite); rewrite=m_link.replaceAll("$1"); }     }			POST("http://en.wikipedia.org/w/index.php?title="+context.first+"&action=submit", "http://en.wikipedia.org/w/index.php?title="+context.first+"&action=edit", 				rewrite, "disambiguation from "+from+" to "+to+" - (You can help!)", info.b, info.c, info.d); output.insert("POSTed: "+posted+" - Failed: "+failed+" - Pending: "+pending+"\n",0); }	}

class Remover extends Thread { Pair context; String from; JTextArea output; public Remover (Pair context, String from, JTextArea output) { this.context=context; this.from=from; this.output=output; }		public void run { output.insert("Removing link "+from+" on page "+context.second+"\n",0); Quad info=getEditableText(context.first); String page=info.a;			String rewrite=null; { /*foo -> foo*/ Pattern pt_link=Pattern.compile("\\[\\["+from+"\\|([^\\]]*)\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Matcher m_link=pt_link.matcher(page); rewrite=m_link.replaceAll("$1"); }

{ /*from -> from*/ Pattern pt_link=Pattern.compile("\\[\\[("+from+")\\]\\]", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); Matcher m_link=pt_link.matcher(rewrite); rewrite=m_link.replaceAll("$1"); }			POST("http://en.wikipedia.org/w/index.php?title="+context.first+"&action=submit", "http://en.wikipedia.org/w/index.php?title="+context.first+"&action=edit", 				rewrite, "removing link "+from+" - (You can help!)", info.b, info.c, info.d); output.insert("POSTed: "+posted+" - Failed: "+failed+" - Pending: "+pending+"\n",0); }	}

}

class Pair { public String first, second; public Pair(String a, String b) {first=a; second=b;} } class Quad { public String a,b,c,d; public Quad(String a, String b, String c, String d) { this.a=a; this.b=b; this.c=c; this.d=d; } }

/* //-->   The java code is hidden in the source of this wikipage. Click on edit this page to view it. */