X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=jarrg-ian.git;a=blobdiff_plain;f=src%2Fnet%2Fchiark%2Fyarrg%2FMarketUploader.java;h=5dc7e6cca0e7429d21c1bff0553510dc62fe6e5f;hp=49521d030f01434e934678c8063788b5917b67de;hb=85a3df43ff414a1c12420eddf0ee372d9682aee8;hpb=596d7809b48b99a8c963995d16341c130452508c diff --git a/src/net/chiark/yarrg/MarketUploader.java b/src/net/chiark/yarrg/MarketUploader.java index 49521d0..5dc7e6c 100644 --- a/src/net/chiark/yarrg/MarketUploader.java +++ b/src/net/chiark/yarrg/MarketUploader.java @@ -34,1130 +34,1342 @@ import java.beans.*; * an error dialog is shown, and processing returns, the button * becoming re-enabled. */ -public class MarketUploader implements TopLevelWindowListener, GUIInitializedListener { - private JFrame frame = null; - private Window window = null; - private JButton findMarket = null; - private JLabel resultSummary = null; - private JLabel arbitrageResult = null; - private int unknownPCTBcommods = 0; - private long startTime = 0; - - private final static String PCTB_LIVE_HOST_URL = "http://pctb.crabdance.com/"; - private final static String PCTB_TEST_HOST_URL = "http://pctb.ilk.org/"; - private String PCTB_HOST_URL; - - // Yarrg protocol parameters - private final static String YARRG_CLIENTNAME = "jpctb greenend"; - private final static String YARRG_CLIENTVERSION = +public class MarketUploader +implements Runnable, TopLevelWindowListener, GUIInitializedListener { + private JFrame frame = null; + private Window window = null; + private JButton findMarket = null; + private JLabel resultSummary = null; + private JLabel arbitrageResult = null; + private int unknownPCTBcommods = 0; + private long startTime = 0; + + private final static String PCTB_LIVE_HOST_URL = "http://pctb.crabdance.com/"; + private final static String PCTB_TEST_HOST_URL = "http://pctb.ilk.org/"; + private String PCTB_HOST_URL; + + // Yarrg protocol parameters + private final static String YARRG_CLIENTNAME = "jpctb greenend"; + private final static String YARRG_CLIENTVERSION = net.chiark.yarrg.Version.version; - private final static String YARRG_CLIENTFIXES = "bug-094"; - private final static String YARRG_LIVE_URL = "http://upload.yarrg.chiark.net/commod-update-receiver"; - private final static String YARRG_TEST_URL = "http://upload.yarrg.chiark.net/test/commod-update-receiver"; - private String YARRG_URL; - - private boolean uploadToYarrg; - private boolean uploadToPCTB; - private boolean showArbitrage; - - private String islandName = null; - private String oceanName = null; - private java.util.concurrent.CountDownLatch latch = null; - - private AccessibleContext sidePanel; - private HashMap commodMap; - public PrintStream dtxt = null; - - private PropertyChangeListener changeListener = new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent e) { - if(e.getNewValue() != null && - e.getPropertyName().equals(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { - Accessible islandInfo = descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; - String text = islandInfo.getAccessibleContext().getAccessibleText().getAtIndex(AccessibleText.SENTENCE,0); - int index = text.indexOf(":"); - String name = text.substring(0,index); - islandName = name; - // if (dtxt!=null) dtxt.println(islandName); - sidePanel.removePropertyChangeListener(this); - latch.countDown(); - } - } - }; - - private int parseQty(String str) { - if (str.equals(">1000")) { - return 1001; - } else { - return Integer.parseInt(str); - } - } - - private void progresslog(String s) { - if (dtxt == null) return; - long now = new Date().getTime(); - dtxt.println("progress "+(now - startTime)+"ms "+s); - } - - private void debug_write_stringdata(String what, String data) throws FileNotFoundException,IOException { - if (dtxt==null) return; - PrintStream strm = new PrintStream(new File("jarrg-debug-"+what)); - strm.print(data); - strm.close(); - } - - private void debug_write_bytes(String what, byte[] data) throws FileNotFoundException,IOException { - if (dtxt==null) return; - FileOutputStream strm = new FileOutputStream(new File("jarrg-debug-"+what)); - strm.write(data); - strm.close(); - } - - private void progressNote(ProgressMonitor pm, String s) { - String arb = null; - if (arbitrageResult != null) - arb = arbitrageResult.getText(); - if (arb != null && arb.length() != 0) - s = "" + arb + "
" + s; - pm.setNote(s); - } + private final static String YARRG_CLIENTFIXES = "bug-094"; + private final static String YARRG_LIVE_URL = + "http://upload.yarrg.chiark.net/commod-update-receiver"; + private final static String YARRG_TEST_URL = + "http://upload.yarrg.chiark.net/test/commod-update-receiver"; + private String YARRG_URL; + + private boolean uploadToYarrg; + private boolean uploadToPCTB; + private boolean showArbitrage; + + private String islandName = null; + private String oceanName = null; + private java.util.concurrent.CountDownLatch latch = null; + + private AccessibleContext sidePanel; + private HashMap commodMap; + public PrintStream dtxt = null; + + private PropertyChangeListener changeListener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if(e.getNewValue() != null && + e.getPropertyName().equals + (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { + Accessible islandInfo = + descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; + String text = + islandInfo.getAccessibleContext().getAccessibleText() + .getAtIndex(AccessibleText.SENTENCE,0); + int index = text.indexOf(":"); + String name = text.substring(0,index); + islandName = name; + // if (dtxt!=null) dtxt.println(islandName); + sidePanel.removePropertyChangeListener(this); + latch.countDown(); + } + } + }; + + /* + * UTILITY METHODS AND SUBCLASSES + * + * Useable on any thread. + * + */ + private int parseQty(String str) { + if (str.equals(">1000")) { + return 1001; + } else { + return Integer.parseInt(str); + } + } + + private void progresslog(String s) { + if (dtxt == null) return; + long now = new Date().getTime(); + dtxt.println("progress "+(now - startTime)+"ms "+s); + } + + private void debug_write_stringdata(String what, String data) + throws FileNotFoundException,IOException { + if (dtxt==null) return; + PrintStream strm = new PrintStream(new File("jarrg-debug-"+what)); + strm.print(data); + strm.close(); + } + + private void debug_write_bytes(String what, byte[] data) + throws FileNotFoundException,IOException { + if (dtxt==null) return; + FileOutputStream strm = new FileOutputStream(new File("jarrg-debug-"+what)); + strm.write(data); + strm.close(); + } - /** - * An abstract market offer, entailing a commodity being bought or sold by - * a shoppe, for a certain price in a certain quantity. Not instantiable. - * - * @see Buy - * @see Sell - */ - abstract class Offer { - public int commodity, price, quantity, shoppe; - /** - * Create an offer from record, determining the shoppe Id from - * stallMap and the commodity Id from commodMap. - * priceIndex should be the index of the price in the record - * (the quantity will be priceIndex + 1). - * - * @param record the record with data to create the offer from - * @param stallMap a map containing the ids of the various stalls - * @param commodMap a map containing the ids of the various commodities - * @param priceIndex the index of the price in the record - */ - public Offer(ArrayList record, LinkedHashMap stallMap, HashMap commodMap, - int priceIndex) { - Integer commodId = commodMap.get(record.get(0)); - if(commodId == null) { - throw new IllegalArgumentException(); - } - commodity = commodId.intValue(); - price = Integer.parseInt(record.get(priceIndex)); - String qty = record.get(priceIndex+1); - quantity = parseQty(qty); - shoppe = stallMap.get(record.get(1)).intValue(); - } + /** + * An abstract market offer, entailing a commodity being bought or sold by + * a shoppe, for a certain price in a certain quantity. Not instantiable. + * + * @see Buy + * @see Sell + */ + abstract class Offer { + public int commodity, price, quantity, shoppe; + /** + * Create an offer from record, determining the shoppe Id from + * stallMap and the commodity Id from commodMap. + * priceIndex should be the index of the price in the record + * (the quantity will be priceIndex + 1). + * + * @param record the record with data to create the offer from + * @param stallMap a map containing the ids of the various stalls + * @param commodMap a map containing the ids of the various commodities + * @param priceIndex the index of the price in the record + */ + public Offer(ArrayList record, + LinkedHashMap stallMap, + HashMap commodMap, + int priceIndex) { + Integer commodId = commodMap.get(record.get(0)); + if(commodId == null) { + throw new IllegalArgumentException(); + } + commodity = commodId.intValue(); + price = Integer.parseInt(record.get(priceIndex)); + String qty = record.get(priceIndex+1); + quantity = parseQty(qty); + shoppe = stallMap.get(record.get(1)).intValue(); + } - /** - * Returns a human-readable version of this offer, useful for debugging - * - * @return human-readable offer - */ - public String toString() { - return "[C:" + commodity + ",$" + price + ",Q:" + quantity + ",S:" + shoppe + "]"; - } - } + /** + * Returns a human-readable version of this offer, useful for debugging + * + * @return human-readable offer + */ + public String toString() { + return "[C:" + commodity + ",$" + price + ",Q:" + + quantity + ",S:" + shoppe + "]"; + } + } - /** - * An offer from a shoppe or stall to buy a certain quantity of a commodity - * for a certain price. If placed in an ordered Set, sorts by commodity index ascending, - * then by buy price descending, and finally by stall id ascending. - */ - class Buy extends Offer implements Comparable { - /** - * Creates a new Buy offer from the given record - * using the other parameters to determine stall id and commodity id of the offer. - * - * @param record the record with data to create the offer from - * @param stallMap a map containing the ids of the various stalls - * @param commodMap a map containing the ids of the various commodities - */ - public Buy(ArrayList record, LinkedHashMap stallMap, HashMap commodMap) { - super(record,stallMap,commodMap,2); - } + /** + * An offer from a shoppe or stall to buy a certain quantity of a + * commodity for a certain price. If placed in an ordered Set, + * sorts by commodity index ascending, then by buy price + * descending, and finally by stall id ascending. + */ + class Buy extends Offer implements Comparable { + /** + * Creates a new Buy offer from the given + * record using the other parameters to determine + * stall id and commodity id of the offer. + * + * @param record the record with data to create the offer from + * @param stallMap a map containing the ids of the various stalls + * @param commodMap a map containing the ids of the various commodities + */ + public Buy(ArrayList record, + LinkedHashMap stallMap, + HashMap commodMap) { + super(record,stallMap,commodMap,2); + } - /** - * Sorts by commodity index ascending, then price descending, then stall id ascending. - */ - public int compareTo(Buy buy) { - // organize by: commodity index, price, stall index - if(commodity == buy.commodity) { - // organize by price, then by stall index - if(price == buy.price) { - // organize by stall index - return shoppe>buy.shoppe ? 1 : -1; - } else if(price > buy.price) { - return -1; - } else { - return 1; - } - } else if(commodity > buy.commodity) { - return 1; - } else { - return -1; - } - } + /** + * Sorts by commodity index ascending, then price descending, + * then stall id ascending. + */ + public int compareTo(Buy buy) { + // organize by: commodity index, price, stall index + if(commodity == buy.commodity) { + // organize by price, then by stall index + if(price == buy.price) { + // organize by stall index + return shoppe>buy.shoppe ? 1 : -1; + } else if(price > buy.price) { + return -1; + } else { + return 1; } + } else if(commodity > buy.commodity) { + return 1; + } else { + return -1; + } + } + } - /** - * An offer from a shoppe or stall to sell a certain quantity of a commodity - * for a certain price. If placed in an ordered Set, sorts by commodity index ascending, - * then by sell price ascending, and finally by stall id ascending. - */ - class Sell extends Offer implements Comparable { - /** - * Creates a new Sell offer from the given record - * using the other parameters to determine stall id and commodity id of the offer. - * - * @param record the record with data to create the offer from - * @param stallMap a map containing the ids of the various stalls - * @param commodMap a map containing the ids of the various commodities - */ - public Sell(ArrayList record, LinkedHashMap stallMap, HashMap commodMap) { - super(record,stallMap,commodMap,4); - } + /** + * An offer from a shoppe or stall to sell a certain quantity of + * a commodity for a certain price. If placed in an ordered Set, + * sorts by commodity index ascending, then by sell price + * ascending, and finally by stall id ascending. + */ + class Sell extends Offer implements Comparable { + /** + * Creates a new Sell offer from the given + * record using the other parameters to determine + * stall id and commodity id of the offer. + * + * @param record the record with data to create the offer from + * @param stallMap a map containing the ids of the various stalls + * @param commodMap a map containing the ids of the various commodities + */ + public Sell(ArrayList record, + LinkedHashMap stallMap, + HashMap commodMap) { + super(record,stallMap,commodMap,4); + } - /** - * Sorts by commodity index ascending, then price ascending, then stall id ascending. - */ - public int compareTo(Sell sell) { - // organize by: commodity index, price, stall index - if(commodity == sell.commodity) { - // organize by price, then by stall index - if(price == sell.price) { - // organize by stall index - return shoppe>sell.shoppe ? 1 : -1; - } else if(price > sell.price) { - return 1; - } else { - return -1; - } - } else if(commodity > sell.commodity) { - return 1; - } else { - return -1; - } - } + /** + * Sorts by commodity index ascending, then price ascending, then + * stall id ascending. + */ + public int compareTo(Sell sell) { + // organize by: commodity index, price, stall index + if(commodity == sell.commodity) { + // organize by price, then by stall index + if(price == sell.price) { + // organize by stall index + return shoppe>sell.shoppe ? 1 : -1; + } else if(price > sell.price) { + return 1; + } else { + return -1; } + } else if(commodity > sell.commodity) { + return 1; + } else { + return -1; + } + } + } + + private void progressNote(ProgressMonitor pm, String s) { + String arb = null; + new UI() { public void body() { + if (arbitrageResult != null) + arb = arbitrageResult.getText(); + if (arb != null && arb.length() != 0) + s = "" + arb + "
" + s; + pm.setNote(s); + }}.exec(); + } - /** - * Entry point. Read our preferences. - */ - public MarketUploader() { - Preferences prefs = Preferences.userNodeForPackage(getClass()); - - if (prefs.getBoolean("writeDebugFiles", false)) { - try { - dtxt = new PrintStream(new File("jarrg-debug-log.txt")); - } catch (java.io.FileNotFoundException e) { - System.err.println("JARRG: Error opening debug log: "+e); - } - } + /* + * ENTRY POINT AND STARTUP + * + * Main thread and/or event thread + */ + /* + * Entry point. Read our preferences. + */ + public MarketUploader() { + Preferences prefs = Preferences.userNodeForPackage(getClass()); + + if (prefs.getBoolean("writeDebugFiles", false)) { + try { + dtxt = new PrintStream(new File("jarrg-debug-log.txt")); + } catch (java.io.FileNotFoundException e) { + System.err.println("JARRG: Error opening debug log: "+e); + } + } - if (prefs.getBoolean("useLiveServers", true)) { - YARRG_URL = YARRG_LIVE_URL; - PCTB_HOST_URL = PCTB_LIVE_HOST_URL; - } else { - YARRG_URL = YARRG_TEST_URL; - PCTB_HOST_URL = PCTB_TEST_HOST_URL; - } - - uploadToYarrg=prefs.getBoolean("uploadToYarrg", true); - uploadToPCTB=prefs.getBoolean("uploadToPCTB", true); - showArbitrage=prefs.getBoolean("showArbitrage", true); - - EventQueueMonitor.addTopLevelWindowListener(this); - if (EventQueueMonitor.isGUIInitialized()) { - createGUI(); - } else { - EventQueueMonitor.addGUIInitializedListener(this); - } - } - - /** - * Set up the GUI, with its window and one-button - * interface. Only initialize if we're running alongside - * a Window named "Puzzle Pirates" though. - */ - private void createGUI() { - if (frame != null && window != null) { - if (window.getAccessibleContext().getAccessibleName().equals("Puzzle Pirates")) frame.setVisible(true); - return; - } - frame = new JFrame("Jarrg Uploader"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - GridLayout layout = new GridLayout(2,1); - frame.getContentPane().setLayout(layout); - //frame.setPreferredSize(new Dimension(200, 60)); - - findMarket = new JButton("Upload Market Data"); - findMarket.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - findMarket.setEnabled(false); - new Thread() { - public void run() { - startTime = new Date().getTime(); - resultSummary.setText(""); - arbitrageResult.setText(""); - unknownPCTBcommods = 0; - try { - runUpload(); - } catch(Exception e) { - error(e.toString()); - e.printStackTrace(); - resultSummary.setText("failed"); - } finally { - if(sidePanel != null) { - // remove it if it's still attached - sidePanel.removePropertyChangeListener(changeListener); - } - } - findMarket.setEnabled(true); - } - }.start(); - } - }); - frame.add(findMarket); - - resultSummary = new JLabel("ready"); - frame.add(resultSummary); + if (prefs.getBoolean("useLiveServers", true)) { + YARRG_URL = YARRG_LIVE_URL; + PCTB_HOST_URL = PCTB_LIVE_HOST_URL; + } else { + YARRG_URL = YARRG_TEST_URL; + PCTB_HOST_URL = PCTB_TEST_HOST_URL; + } - arbitrageResult = new JLabel(""); - - if (showArbitrage) { - layout.setRows(layout.getRows() + 1); - frame.add(arbitrageResult); - } + uploadToYarrg=prefs.getBoolean("uploadToYarrg", true); + uploadToPCTB=prefs.getBoolean("uploadToPCTB", true); + showArbitrage=prefs.getBoolean("showArbitrage", true); + + if (dtxt!=null) dtxt.println("main on dispatch thread: "+ + EventQueue.isDispatchThread()); + EventQueue.invokeLater(this); + } + + /* + * We arrange to wait for the GUI to be initialised, then look at + * every top-level window, and if it + */ + public void run() { + if (dtxt!=null) dtxt.println("MarketUploader run()..."); + if (EventQueueMonitor.isGUIInitialized()) { + if (dtxt!=null) dtxt.println("MarketUploader GUI already ready"); + guiInitialized(); + } else { + if (dtxt!=null) dtxt.println("MarketUploader waiting for GUI"); + EventQueueMonitor.addGUIInitializedListener(this); + } + } + + public void guiInitialized() { + Window ws[]= EventQueueMonitor.getTopLevelWindows(); + EventQueueMonitor.addTopLevelWindowListener(this); + for (int i=0; imsg. - * - * @param msg a String describing the error that occured. - */ - private void error(String msg) { - JOptionPane.showMessageDialog(frame,msg,"Error",JOptionPane.ERROR_MESSAGE); - } + frame.pack(); + } + + /* + * ERROR REPORTING AND GENERAL UTILITIES + * + * Synchronous modal dialogues + * error and error_html may be called from any thread + */ + + private Class UI implements Runnable { + public virtual void body(); + public void run() { body(); }; + public void exec() { + if (EventQueue.isDispatchThread()) { + r.run(); + } else { + invokeAndWait(r); + } + }; + }; + + private void error(String msg) { + new UI() { public booolean body() { + resultSummary.setText("failed"); + JOptionPane.showMessageDialog(frame,msg,"Error", + JOptionPane.ERROR_MESSAGE); + return true; + }}.exec(); + } - private void error_html(String msg, String html) { - Pattern body = Pattern.compile("(.*)", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); - Matcher m = body.matcher(html); - if (m.find()) { - html = m.group(1); - Pattern fixup = Pattern.compile("<(\\w+) */>");; - m = fixup.matcher(html); - html = m.replaceAll("<$1>"); - m = Pattern.compile("[\\r\\n]+").matcher(html); - html = m.replaceAll(" "); - } - String whole_msg = "

Error

"+msg+"

PCTB Server said:

"+html+"
"; - if (dtxt!=null) dtxt.println("###" + whole_msg + "###"); - - JOptionPane.showMessageDialog(frame,whole_msg,"Error",JOptionPane.ERROR_MESSAGE); - } + private void error_html(String msg, String html) { + Pattern body = Pattern.compile("(.*)", + Pattern.DOTALL | Pattern.CASE_INSENSITIVE); + Matcher m = body.matcher(html); + if (m.find()) { + html = m.group(1); + Pattern fixup = Pattern.compile("<(\\w+) */>");; + m = fixup.matcher(html); + html = m.replaceAll("<$1>"); + m = Pattern.compile("[\\r\\n]+").matcher(html); + html = m.replaceAll(" "); + } + String whole_msg = "

Error

"+msg + +"

PCTB Server said:

"+html+"
"; + if (dtxt!=null) dtxt.println("###" + whole_msg + "###"); + + error(whole_msg); + } - /** - * Run the data collection process, and upload the results. This is the method - * that calls most of the other worker methods for the process. If an error occurs, - * the method will call the error method and return early, freeing up the button - * to be clicked again. - * - * @exception Exception if an error we didn't expect occured - */ - private class YarrgTimestampFetcher extends Thread { - public String ts = null; - public void run() { - try { - ts = getYarrgTimestamp(); - progresslog("(async) yarrg timestamp ready."); - } catch(Exception e) { - error("Error getting YARRG timestamp: "+e); - } - } - }; + /* + * GUI MANIPULATION CALLBACKS + */ + + private PropertyChangeListener changeListener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent e) { + if(e.getNewValue() != null && + e.getPropertyName().equals + (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY)) { + Accessible islandInfo = + descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0,0,1,2});; + String text = islandInfo.getAccessibleContext().getAccessibleText() + .getAtIndex(AccessibleText.SENTENCE,0); + int index = text.indexOf(":"); + String name = text.substring(0,index); + islandName = name; + // if (dtxt!=null) dtxt.println(islandName); + sidePanel.removePropertyChangeListener(this); + latch.countDown(); + } + } + }; + + private void getIsland() { + // runs on event thread + + // If the league tracker is there, we can skip the faff + // and ask for its tooltip, since we're on a boat + + Accessible leagueTrackerContainer = + descendNodes(window,new int[] {0,1,0,0,2,1}); + Accessible leagueTrackerItself = + descendByClass(leagueTrackerContainer, + "com.threerings.yohoho.sea.client.LeagueTracker"); + Accessible leagueTracker = descend(leagueTrackerItself, 1); + try { + islandName = ((JLabel)leagueTracker).getToolTipText(); + } catch (NullPointerException e) { + // evidently we're actually on an island + + islandName = null; + AccessibleContext chatArea = + descendNodes(window,new int[] {0,1,0,0,0,2,0,0,2}) + .getAccessibleContext(); + // attach the property change listener to the outer sunshine + // panel if the "ahoy" tab is not active, otherwise attach it to + // the scroll panel in the "ahoy" tab. + if(!"com.threerings.piracy.client.AttentionListPanel". + equals(descendNodes(window,new int[] {0,1,0,0,2,2,0}) + .getClass().getCanonicalName())) { + sidePanel = descendNodes(window,new int[] {0,1,0,0,2,2}) + .getAccessibleContext(); + } else { + sidePanel = descendNodes(window,new int[] {0,1,0,0,2,2,0,0,0}) + .getAccessibleContext(); + } + sidePanel.addPropertyChangeListener(changeListener); + latch = new java.util.concurrent.CountDownLatch(1); + // make the Players Online ("/who") panel appear + AccessibleEditableText chat = chatArea.getAccessibleEditableText(); + chat.setTextContents("/w"); + int c = chatArea.getAccessibleAction().getAccessibleActionCount(); + for(int i=0;i> data = null; + String headings_expected[] = new String[] + { "Commodity", "Trading outlet", "Buy price", + "Will buy", "Sell price", "Will sell" }; + + new UI() { public void body() { + ArrayList> headers = + getData(accesstable.getAccessibleColumnHeader()); + if (headers.size() != 1) { + error("Table headings not one row! " + headers.toString()); + return; + } + if (headers.get(0).size() < 6 || + headers.get(0).size() > 7) { + error("Table headings not six or seven columns! " + headers.toString()); + return; + } + for (int col=0; col> data = getData(accesstable); + }}.exec(); + if (!data) return; - String headings_expected[] = new String[] - { "Commodity", "Trading outlet", "Buy price", "Will buy", "Sell price", "Will sell" }; - ArrayList> headers = getData(accesstable.getAccessibleColumnHeader()); - if (headers.size() != 1) { - error("Table headings not one row! " + headers.toString()); - return; - } - if (headers.get(0).size() < 6 || - headers.get(0).size() > 7) { - error("Table headings not six or seven columns! " + headers.toString()); - return; - } - for (int col=0; col> data = getData(accesstable); + StringBuilder yarrgsb = new StringBuilder(); + String yarrgdata; // string containing what we'll feed to yarrg + + for (ArrayList row : data) { + if (row.size() > 6) { + row.remove(6); + } + for (String rowitem : row) { + yarrgsb.append(rowitem != null ? rowitem : ""); + yarrgsb.append("\t"); + } + yarrgsb.setLength(yarrgsb.length()-1); // chop + yarrgsb.append("\n"); + } - if (showArbitrage) { - progresslog("arbitrage..."); - calculateArbitrage(data); - progresslog("arbitrage done."); - } + yarrgdata = yarrgsb.toString(); - if (uploadToYarrg && yarrgts != null) { - progresslog("yarrg prepare..."); - progressNote(pm, "Yarrg: Preparing data"); - pm.setProgress(10); + progressNote(pm, "Yarrg: Uploading"); + progresslog("yarrg upload..."); - StringBuilder yarrgsb = new StringBuilder(); - String yarrgdata; // string containing what we'll feed to yarrg - - for (ArrayList row : data) { - if (row.size() > 6) { - row.remove(6); - } - for (String rowitem : row) { - yarrgsb.append(rowitem != null ? rowitem : ""); - yarrgsb.append("\t"); - } - yarrgsb.setLength(yarrgsb.length()-1); // chop - yarrgsb.append("\n"); - } - - yarrgdata = yarrgsb.toString(); - - progressNote(pm, "Yarrg: Uploading"); - progresslog("yarrg upload..."); - - doneyarrg = runYarrg(yarrgts, oceanName, islandName, yarrgdata); - progresslog("yarrg done."); - } + doneyarrg = runYarrg(yarrgts, oceanName, islandName, yarrgdata); + progresslog("yarrg done."); + } - if (uploadToPCTB) { - progresslog("pctb prepare..."); - progressNote(pm, "PCTB: Getting stall names"); - pm.setProgress(20); - if(pm.isCanceled()) { - return; - } - TreeSet buys = new TreeSet(); - TreeSet sells = new TreeSet(); - LinkedHashMap stallMap = getStallMap(data); - pm.setProgress(40); - progressNote(pm, "PCTB: Sorting offers"); - if(pm.isCanceled()) { - return; - } - // get commod map + if (uploadToPCTB) { + progresslog("pctb prepare..."); + progressNote(pm, "PCTB: Getting stall names"); + pm.setProgress(20); + if(pm.isCanceled()) { + return; + } + TreeSet buys = new TreeSet(); + TreeSet sells = new TreeSet(); + LinkedHashMap stallMap = getStallMap(data); + pm.setProgress(40); + progressNote(pm, "PCTB: Sorting offers"); + if(pm.isCanceled()) { + return; + } + // get commod map - progresslog("pctb commodmap..."); - HashMap commodMap = getCommodMap(); - if(commodMap == null) { - return; - } - progresslog("pctb commodmap done."); - int[] offerCount = getBuySellMaps(data,buys,sells,stallMap,commodMap); - // if (dtxt!=null) dtxt.println(sells); - // if (dtxt!=null) dtxt.println("\n\n\n"+buys); - - ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - pm.setProgress(60); - progressNote(pm, "PCTB: Sending data"); - if(pm.isCanceled()) { - return; - } - GZIPOutputStream out = new GZIPOutputStream(outStream); - DataOutputStream dos = new DataOutputStream(out); - dos.writeBytes("005y\n"); - dos.writeBytes(stallMap.size()+"\n"); - dos.writeBytes(getAbbrevStallList(stallMap)); - writeBuySellOffers(buys,sells,offerCount,out); - out.finish(); - progresslog("pctb send..."); - - byte[] ba = outStream.toByteArray(); - debug_write_bytes("pctb-marketdata.gz", ba); - - InputStream in = sendInitialData(new ByteArrayInputStream(ba)); - progresslog("pctb sent."); - if (in == null) return; - pm.setProgress(80); - if(pm.isCanceled()) { - return; - } - progressNote(pm, "PCTB: Waiting ..."); - progresslog("pctb finish..."); - donepctb = finishUpload(in); - progresslog("pctb done."); - } - pm.setProgress(100); - - if ((uploadToPCTB && !donepctb) || - (uploadToYarrg && !doneyarrg)) { - resultSummary.setText("trouble"); - } else if (unknownPCTBcommods != 0) { - resultSummary.setText("PCTB lacks "+unknownPCTBcommods+" commod"); - } else if (donepctb || doneyarrg) { - resultSummary.setText("Done " + islandName); - } else { - resultSummary.setText("uploaded nowhere!"); - } - progresslog("done."); - } - - /** - * Get the offer data out of the table and cache it in an ArrayList. - * - * @param table the AccessibleTable containing the market data - * @return an array of record arrays, each representing a row of the table - */ - private ArrayList> getData(AccessibleTable table) { - ArrayList> data = new ArrayList>(); - for (int i = 0; i < table.getAccessibleRowCount(); i++) { - ArrayList row = new ArrayList(); - for (int j = 0; j < table.getAccessibleColumnCount(); j++) { - row.add(table.getAccessibleAt(i, j).getAccessibleContext().getAccessibleName()); - } - data.add(row); - } - return data; - } - - /** - * @return the table containing market data if it exists, otherwise null - */ - public AccessibleTable findMarketTable() { - Accessible node1 = window; - Accessible node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,0,1,0,0}); // commod market - // commod market: {0,1,0,0,0,0,1,0,0,1,0} {0,1,0,0,0,0,1,0,1,0,0,1,0,0}) - // if (dtxt!=null) dtxt.println(node); - if (!(node instanceof JTable)) { - node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,1,0,0,1,0,0}); // commod market - } - if (!(node instanceof JTable)) return null; - AccessibleTable table = node.getAccessibleContext().getAccessibleTable(); - // if (dtxt!=null) dtxt.println(table); - return table; - } + progresslog("pctb commodmap..."); + HashMap commodMap = getCommodMap(); + if(commodMap == null) { + return; + } + progresslog("pctb commodmap done."); + int[] offerCount = getBuySellMaps(data,buys,sells,stallMap,commodMap); + // if (dtxt!=null) dtxt.println(sells); + // if (dtxt!=null) dtxt.println("\n\n\n"+buys); + + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + pm.setProgress(60); + progressNote(pm, "PCTB: Sending data"); + if(pm.isCanceled()) { + return; + } + GZIPOutputStream out = new GZIPOutputStream(outStream); + DataOutputStream dos = new DataOutputStream(out); + dos.writeBytes("005y\n"); + dos.writeBytes(stallMap.size()+"\n"); + dos.writeBytes(getAbbrevStallList(stallMap)); + writeBuySellOffers(buys,sells,offerCount,out); + out.finish(); + progresslog("pctb send..."); + + byte[] ba = outStream.toByteArray(); + debug_write_bytes("pctb-marketdata.gz", ba); + + InputStream in = sendInitialData(new ByteArrayInputStream(ba)); + progresslog("pctb sent."); + if (in == null) return; + pm.setProgress(80); + if(pm.isCanceled()) { + return; + } + progressNote(pm, "PCTB: Waiting ..."); + progresslog("pctb finish..."); + donepctb = finishUpload(in); + progresslog("pctb done."); + } + pm.setProgress(100); + + if ((uploadToPCTB && !donepctb) || + (uploadToYarrg && !doneyarrg)) { + resultSummary.setText("trouble"); + } else if (unknownPCTBcommods != 0) { + resultSummary.setText("PCTB lacks "+unknownPCTBcommods+" commod"); + } else if (donepctb || doneyarrg) { + resultSummary.setText("Done " + islandName); + } else { + resultSummary.setText("uploaded nowhere!"); + } + progresslog("done."); + } - /** - * Utility method to descend through several levels of Accessible children - * at once. - * - * @param parent the node on which to start the descent - * @param path an array of ints, each int being the index of the next - * accessible child to descend. - * @return the Accessible reached by following the descent path, - * or null if the desired path was invalid. - */ - private Accessible descendNodes(Accessible parent, int[] path) { - for(int i=0;iArrayList. + * + * @param table the AccessibleTable containing the market data + * @return an array of record arrays, each representing a row of the table + */ + private ArrayList> getData(AccessibleTable table) { + ArrayList> data = new ArrayList>(); + for (int i = 0; i < table.getAccessibleRowCount(); i++) { + ArrayList row = new ArrayList(); + for (int j = 0; j < table.getAccessibleColumnCount(); j++) { + row.add(table.getAccessibleAt(i, j) + .getAccessibleContext().getAccessibleName()); + } + data.add(row); + } + return data; + } - /** - * Descends one level to the specified child of the parent Accessible "node". - * - * @param parent the node with children - * @param childNum the index of the child of parent to return - * @return the childNum child of parent or null - * if the child is not found. - */ - private Accessible descend(Accessible parent, int childNum) { - if (childNum >= parent.getAccessibleContext().getAccessibleChildrenCount()) return null; - return parent.getAccessibleContext().getAccessibleChild(childNum); - } + /** + * @return the table containing market data if it exists, + * otherwise null + */ + public AccessibleTable findMarketTable() { + Accessible node1 = window; + Accessible node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,0,1,0,0}); + // commod market + // commod market: {0,1,0,0,0,0,1,0,0,1,0} {0,1,0,0,0,0,1,0,1,0,0,1,0,0}) + // if (dtxt!=null) dtxt.println(node); + if (!(node instanceof JTable)) { + node = descendNodes(node1,new int[] {0,1,0,0,0,0,1,0,1,0,0,1,0,0}); + // commod market + } + if (!(node instanceof JTable)) return null; + AccessibleTable table = node.getAccessibleContext().getAccessibleTable(); + // if (dtxt!=null) dtxt.println(table); + return table; + } - public static void main(String[] args) { - new MarketUploader(); - } - - /** - * Set the global window variable after the YPP window is created, - * remove the top level window listener, and start the GUI - */ - public void topLevelWindowCreated(Window w) { - window = w; - EventQueueMonitor.removeTopLevelWindowListener(this); - createGUI(); - } + /** + * Utility method to descend through several levels of Accessible children + * at once. + * + * @param parent the node on which to start the descent + * @param path an array of ints, each int being the index of the next + * accessible child to descend. + * @return the Accessible reached by following the + * descent path, or null if the desired path was + * invalid. + */ + private Accessible descendNodes(Accessible parent, int[] path) { + for(int i=0;itrue if all commodities are displayed, otherwise false - */ - private boolean isDisplayAll() { - Accessible button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,0,0,1}); - if(!(button instanceof JButton)) { - button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,1,0,0,0,1}); - } - String display = button.getAccessibleContext().getAccessibleName(); - if(!display.equals("All")) { - return false; - } - return true; - } + /** + * Descends one level to the specified child of the parent + * Accessible "node". + * + * @param parent the node with children + * @param childNum the index of the child of parent to return + * @return the childNum child of parent + * or null if the child is not found. + */ + private Accessible descend(Accessible parent, int childNum) { + if (parent == null) return null; + int children = parent.getAccessibleContext().getAccessibleChildrenCount(); + if (childNum >= children) { + if (dtxt!=null) dtxt.println("DESCEND "+childNum+" > " + +children+" NOT FOUND"); + return null; + } + Accessible child = parent.getAccessibleContext() + .getAccessibleChild(childNum); + if (dtxt!=null) dtxt.println("DESCEND "+childNum+" " + +child.getClass().getName()+" OK"); + return child; + } + + /** + * Descends one level to the child which has the specified class. + * + * @param parent the node with children + * @param classname the name of the class, as a string + * @return the child or null if the child is not found. + */ + private Accessible descendByClass(Accessible parent, String classname) { + if (parent == null) return null; + AccessibleContext ac = parent.getAccessibleContext(); + int children = ac.getAccessibleChildrenCount(); + for (int i=0; itrue if all commodities are displayed, + * otherwise false + */ + private boolean isDisplayAll() { + Accessible button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,0,0,1}); + if(!(button instanceof JButton)) { + button = descendNodes(window,new int[] {0,1,0,0,0,0,1,0,1,0,0,0,1}); + } + String display = button.getAccessibleContext().getAccessibleName(); + if(!display.equals("All")) { + return false; + } + return true; + } - public void topLevelWindowDestroyed(Window w) {} - - public void guiInitialized() { - createGUI(); - } + /** + * Gets the list of commodities and their associated commodity ids. + * + * @return a map where the key is the commodity and the value is + * the commodity id. + */ + private HashMap getCommodMap() { + if(commodMap != null) { + return commodMap; + } + HashMap map = new HashMap(); + String xml; + try { + URL host = new URL(PCTB_HOST_URL + "commodmap.php"); + BufferedReader br = + new BufferedReader(new InputStreamReader(host.openStream())); + StringBuilder sb = new StringBuilder(); + String str; + while((str = br.readLine()) != null) { + sb.append(str); + } + if (dtxt != null) + debug_write_stringdata("pctb-commodmap.xmlish", sb.toString()); + int first = sb.indexOf("
") + 5;
+      int last = sb.indexOf("");
+      xml = sb.substring(first,last);
+      Reader reader = new CharArrayReader(xml.toCharArray());
+      Document d = DocumentBuilderFactory.newInstance()
+	.newDocumentBuilder().parse(new InputSource(reader));
+      NodeList maps = d.getElementsByTagName("CommodMap");
+      for(int i=0;i getCommodMap() {
-		if(commodMap != null) {
-			return commodMap;
-		}
-		HashMap map = new HashMap();
-		String xml;
-		try {
-			URL host = new URL(PCTB_HOST_URL + "commodmap.php");
-			BufferedReader br = new BufferedReader(new InputStreamReader(host.openStream()));
-			StringBuilder sb = new StringBuilder();
-			String str;
-			while((str = br.readLine()) != null) {
-				sb.append(str);
-			}
-			if (dtxt != null) debug_write_stringdata("pctb-commodmap.xmlish", sb.toString());
-			int first = sb.indexOf("
") + 5;
-			int last = sb.indexOf("");
-			xml = sb.substring(first,last);
-			Reader reader = new CharArrayReader(xml.toCharArray());
-			Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(reader));
-			NodeList maps = d.getElementsByTagName("CommodMap");
-			for(int i=0;iLinkedHashMap
+   *	where the key is the stall name and the value is the generated
+   *	stall id (position in the list).  

The reason this method + * returns a LinkedHashMap instead of a simple HashMap is the + * need for iterating over the stall names in insertion order for + * output to the server. + * + * @param offers the list of records from the commodity buy/sell interface + * @return an iterable ordered map of the stall names and + * generated stall ids + */ + private LinkedHashMap getStallMap + (ArrayList> offers) { + int count = 0; + LinkedHashMap map = new LinkedHashMap(); + for(ArrayList offer : offers) { + String shop = offer.get(1); + if(!map.containsKey(shop)) { + count++; + map.put(shop,count); + } + } + return map; + } - /** - * Given the list of offers, this method will find all the unique stall names - * and return them in a LinkedHashMap where the key is the stall name - * and the value is the generated stall id (position in the list). - *

- * The reason this method returns a LinkedHashMap instead of a simple HashMap is the need - * for iterating over the stall names in insertion order for output to the server. - * - * @param offers the list of records from the commodity buy/sell interface - * @return an iterable ordered map of the stall names and generated stall ids - */ - private LinkedHashMap getStallMap(ArrayList> offers) { - int count = 0; - LinkedHashMap map = new LinkedHashMap(); - for(ArrayList offer : offers) { - String shop = offer.get(1); - if(!map.containsKey(shop)) { - count++; - map.put(shop,count); - } - } - return map; + /** + * Gets a sorted list of Buys and Sells from the list of + * records. buys and sells should be + * pre-initialized and passed into the method to receive the + * data. Returns a 2-length int array with the number of buys + * and sells found. + * + * @param offers the data found from the market table in-game + * @param buys an empty initialized TreeSet<Offer> to + * hold the Buy offers. + * @param sells an empty initialized TreeSet<Offer> to + * hold the Sell offers. + * @param stalls the map of stalls to their ids + * @param commodMap the map of commodities to their ids + * @return a 2-length int[] array containing the number of buys + * and sells, respectively + */ + private int[] getBuySellMaps(ArrayList> offers, + TreeSet buys, + TreeSet sells, + LinkedHashMap stalls, + HashMap commodMap) { + int[] buySellCount = new int[2]; + for(ArrayList offer : offers) { + try { + if(offer.get(2) != null) { + buys.add(new Buy(offer,stalls,commodMap)); + buySellCount[0]++; } - - /** - * Gets a sorted list of Buys and Sells from the list of records. buys and sells - * should be pre-initialized and passed into the method to receive the data. - * Returns a 2-length int array with the number of buys and sells found. - * - * @param offers the data found from the market table in-game - * @param buys an empty initialized TreeSet<Offer> to - * hold the Buy offers. - * @param sells an empty initialized TreeSet<Offer> to - * hold the Sell offers. - * @param stalls the map of stalls to their ids - * @param commodMap the map of commodities to their ids - * @return a 2-length int[] array containing the number of buys and sells, respectively - */ - private int[] getBuySellMaps(ArrayList> offers, TreeSet buys, - TreeSet sells, LinkedHashMap stalls, HashMap commodMap) { - int[] buySellCount = new int[2]; - for(ArrayList offer : offers) { - try { - if(offer.get(2) != null) { - buys.add(new Buy(offer,stalls,commodMap)); - buySellCount[0]++; - } - if(offer.get(4) != null) { - sells.add(new Sell(offer,stalls,commodMap)); - buySellCount[1]++; - } - } catch(IllegalArgumentException e) { - unknownPCTBcommods++; - if (dtxt!=null) dtxt.println("Error: Unsupported Commodity \"" + offer.get(0) + "\""); - } - } - if (buySellCount[0]==0 && buySellCount[1]==0) { - error("No (valid) offers for PCTB?!"); - throw new IllegalArgumentException(); - } - return buySellCount; + if(offer.get(4) != null) { + sells.add(new Sell(offer,stalls,commodMap)); + buySellCount[1]++; } + } catch(IllegalArgumentException e) { + unknownPCTBcommods++; + if (dtxt!=null) dtxt.println("Error: Unsupported Commodity \"" + + offer.get(0) + "\""); + } + } + if (buySellCount[0]==0 && buySellCount[1]==0) { + error("No (valid) offers for PCTB?!"); + throw new IllegalArgumentException(); + } + return buySellCount; + } - /** - * Prepares the list of stalls for writing to the output stream. - * The String returned by this method is ready to be written - * directly to the stream. - *

- * All shoppe names are left as they are. Stall names are abbreviated just before the - * apostrophe in the possessive, with an "^" and a letter matching the stall's type - * appended. Example: "Burninator's Ironworking Stall" would become "Burninator^I". - * - * @param stallMap the map of stalls and stall ids in an iterable order - * @return a String containing the list of stalls in format ready - * to be written to the output stream. - */ - private String getAbbrevStallList(LinkedHashMap stallMap) { - // set up some mapping - HashMap types = new HashMap(); - types.put("Apothecary Stall", "A"); - types.put("Distilling Stall", "D"); - types.put("Furnishing Stall", "F"); - types.put("Ironworking Stall", "I"); - types.put("Shipbuilding Stall", "S"); - types.put("Tailoring Stall", "T"); - types.put("Weaving Stall", "W"); + /** + * Prepares the list of stalls for writing to the output stream. + * The String returned by this method is ready to be + * written directly to the stream.

All shoppe names are left + * as they are. Stall names are abbreviated just before the + * apostrophe in the possessive, with an "^" and a letter + * matching the stall's type appended. Example: "Burninator's + * Ironworking Stall" would become "Burninator^I". + * + * @param stallMap the map of stalls and stall ids in an iterable order + * @return a String containing the list of stalls in + * format ready to be written to the output stream. + */ + private String getAbbrevStallList(LinkedHashMap stallMap) { + // set up some mapping + HashMap types = new HashMap(); + types.put("Apothecary Stall", "A"); + types.put("Distilling Stall", "D"); + types.put("Furnishing Stall", "F"); + types.put("Ironworking Stall", "I"); + types.put("Shipbuilding Stall", "S"); + types.put("Tailoring Stall", "T"); + types.put("Weaving Stall", "W"); - StringBuilder sb = new StringBuilder(); - for(String name : stallMap.keySet()) { - int index = name.indexOf("'s"); - String finalName = name; - String type = null; - if (index > 0) { - finalName = name.substring(0,index); - if(index + 2 < name.length()) { - String end = name.substring(index+2,name.length()).trim(); - type = types.get(end); - } - } - if(type==null) { - sb.append(name+"\n"); - } else { - sb.append(finalName+"^"+type+"\n"); - } - } - return sb.toString(); + StringBuilder sb = new StringBuilder(); + for(String name : stallMap.keySet()) { + int index = name.indexOf("'s"); + String finalName = name; + String type = null; + if (index > 0) { + finalName = name.substring(0,index); + if(index + 2 < name.length()) { + String end = name.substring(index+2,name.length()).trim(); + type = types.get(end); } + } + if(type==null) { + sb.append(name+"\n"); + } else { + sb.append(finalName+"^"+type+"\n"); + } + } + return sb.toString(); + } - /** - * Writes a list of offers in correct format to the output stream. - *

- * The format is thus: (all numbers are 2-byte integers in little-endian format) - * (number of offers of this type, aka buy/sell) - * (commodity ID) (number of offers for this commodity) [shopID price qty][shopID price qty]... - * - * @param out the output stream to write the data to - * @param offers the offers to write - */ - private void writeOffers(OutputStream out, TreeSet offers) throws IOException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - if(offers.size() == 0) { - // nothing to write, and "0" has already been written - return; - } - int commodity = offers.first().commodity; - int count = 0; - for(Offer offer : offers) { - if(commodity != offer.commodity) { - // write out buffer - writeBufferedOffers(out,buffer.toByteArray(),commodity,count); - buffer.reset(); - commodity = offer.commodity; - count = 0; - } - writeLEShort(offer.shoppe,buffer); // stall index - writeLEShort(offer.price,buffer); // buy price - writeLEShort(offer.quantity,buffer); // buy qty - count++; - } - writeBufferedOffers(out,buffer.toByteArray(),commodity,count); - } + /** + * Writes a list of offers in correct format to the output stream. + *

+ * The format is thus: (all numbers are 2-byte integers in + * little-endian format) (number of offers of this type, aka + * buy/sell) (commodity ID) (number of offers for this commodity) + * [shopID price qty][shopID price qty]... + * + * @param out the output stream to write the data to + * @param offers the offers to write + */ + private void writeOffers(OutputStream out, TreeSet offers) + throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + if(offers.size() == 0) { + // nothing to write, and "0" has already been written + return; + } + int commodity = offers.first().commodity; + int count = 0; + for(Offer offer : offers) { + if(commodity != offer.commodity) { + // write out buffer + writeBufferedOffers(out,buffer.toByteArray(),commodity,count); + buffer.reset(); + commodity = offer.commodity; + count = 0; + } + writeLEShort(offer.shoppe,buffer); // stall index + writeLEShort(offer.price,buffer); // buy price + writeLEShort(offer.quantity,buffer); // buy qty + count++; + } + writeBufferedOffers(out,buffer.toByteArray(),commodity,count); + } - /** - * Writes the buffered data to the output strea for one commodity. - * - * @param out the stream to write to - * @param buffer the buffered data to write - * @param commodity the commmodity id to write before the buffered data - * @param count the number of offers for this commodity to write before the data - */ - private void writeBufferedOffers(OutputStream out, byte[] buffer, int commodity, int count) throws IOException { - writeLEShort(commodity,out); // commod index - writeLEShort(count,out); // offer count - out.write(buffer); // the buffered offers - } + /** + * Writes the buffered data to the output strea for one commodity. + * + * @param out the stream to write to + * @param buffer the buffered data to write + * @param commodity the commmodity id to write before the buffered data + * @param count the number of offers for this commodity to write + * before the data + */ + private void writeBufferedOffers(OutputStream out, byte[] buffer, + int commodity, int count) + throws IOException { + writeLEShort(commodity,out); // commod index + writeLEShort(count,out); // offer count + out.write(buffer); // the buffered offers + } - /** - * Writes the buy and sell offers to the outputstream by calling other methods. - * - * @param buys list of Buy offers to write - * @param sells list of Sell offers to write - * @param offerCount 2-length int array containing the number of buys and sells to write out - * @param out the stream to write to - */ - private void writeBuySellOffers(TreeSet buys, - TreeSet sells, int[] offerCount, OutputStream out) throws IOException { - // # buy offers - writeLEShort(offerCount[0],out); - writeOffers(out,buys); - // # sell offers - writeLEShort(offerCount[1],out); - writeOffers(out,sells); - } + /** + * Writes the buy and sell offers to the outputstream by calling + * other methods. + * + * @param buys list of Buy offers to write + * @param sells list of Sell offers to write + * @param offerCount 2-length int array containing the number of + * buys and sells to write out + * @param out the stream to write to + */ + private void writeBuySellOffers(TreeSet buys, + TreeSet sells, + int[] offerCount, OutputStream out) + throws IOException { + // # buy offers + writeLEShort(offerCount[0],out); + writeOffers(out,buys); + // # sell offers + writeLEShort(offerCount[1],out); + writeOffers(out,sells); + } - private String readstreamstring(InputStream in) throws IOException { - StringBuilder sb = new StringBuilder(); - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - String str; - while((str = br.readLine()) != null) { - sb.append(str+"\n"); - } - return sb.toString(); - } - - /** - * Sends the data to the server via multipart-formdata POST, - * with the gzipped data as a file upload. - * - * @param file an InputStream open to the gzipped data we want to send - */ - private InputStream sendInitialData(InputStream file) throws IOException { - ClientHttpRequest http = new ClientHttpRequest(PCTB_HOST_URL + "upload.php"); - http.setParameter("marketdata","marketdata.gz",file,"application/gzip"); - if (!http.post()) { - String err = readstreamstring(http.resultstream()); - error("Error sending initial data:\n"+err); - return null; - } - return http.resultstream(); - } + private String readstreamstring(InputStream in) throws IOException { + StringBuilder sb = new StringBuilder(); + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String str; + while((str = br.readLine()) != null) { + sb.append(str+"\n"); + } + return sb.toString(); + } + + /** + * Sends the data to the server via multipart-formdata POST, + * with the gzipped data as a file upload. + * + * @param file an InputStream open to the gzipped data we want to send + */ + private InputStream sendInitialData(InputStream file) throws IOException { + ClientHttpRequest http = + new ClientHttpRequest(PCTB_HOST_URL + "upload.php"); + http.setParameter("marketdata","marketdata.gz",file,"application/gzip"); + if (!http.post()) { + String err = readstreamstring(http.resultstream()); + error("Error sending initial data:\n"+err); + return null; + } + return http.resultstream(); + } - /** - * Utility method to write a 2-byte int in little-endian form to an output stream. - * - * @param num an integer to write - * @param out stream to write to - */ - private void writeLEShort(int num, OutputStream out) throws IOException { - out.write(num & 0xFF); - out.write((num >>> 8) & 0xFF); - } + /** + * Utility method to write a 2-byte int in little-endian form to + * an output stream. + * + * @param num an integer to write + * @param out stream to write to + */ + private void writeLEShort(int num, OutputStream out) throws IOException { + out.write(num & 0xFF); + out.write((num >>> 8) & 0xFF); + } - /** - * Reads the response from the server, and selects the correct parameters - * which are sent in a GET request to the server asking it to confirm - * the upload and accept the data into the database. Notably, the island id - * and ocean id are determined, while other parameter such as the filename - * are determined from the hidden form fields. - * - * @param in stream of data from the server to read - */ - private boolean finishUpload(InputStream in) throws IOException { - String html = readstreamstring(in); - debug_write_stringdata("pctb-initial.html", html); - Matcher m; - - Pattern params = Pattern.compile("(?s).+?.+?"); - m = params.matcher(html); - if(!m.find()) { - error_html("The PCTB server returned unusual data. Maybe you're using an old version of the uploader?", - html); - return false; - } - String forceReload = m.group(1); - String filename = m.group(2); - - Pattern oceanNumPat = Pattern.compile(""); - m = oceanNumPat.matcher(html); - if (!m.find()) { - error_html("Unable to find the ocean in the server's list of oceans!", html); - return false; - } - String oceanNum = m.group(1); - - Pattern oceanIslandNum = Pattern.compile("islands\\[" + oceanNum + "\\]\\[\\d+\\]=new Option\\(\"" + islandName + "\",(\\d+)"); - m = oceanIslandNum.matcher(html); - if(!m.find()) { - error_html("This does not seem to be a valid island! Unable to upload.", html); - return false; - } - String islandNum = m.group(1); - - URL get = new URL(PCTB_HOST_URL + "upload.php?action=setisland&ocean=" + oceanNum + "&island=" - + islandNum + "&forcereload=" + forceReload + "&filename=" + filename); - String complete = readstreamstring(get.openStream()); - debug_write_stringdata("pctb-final.html", complete); - Pattern done = Pattern.compile("Your data has been integrated into the database. Thank you!"); - m = done.matcher(complete); - if(m.find()) { - return true; - } else { - error_html("Something was wrong with the final upload parameters!", complete); - return false; - } - } - - private InputStream post_for_yarrg(ClientHttpRequest http) throws IOException { - if (!http.post()) { - String err = readstreamstring(http.resultstream()); - error("

Error reported by YARRG server

\n" + err); - return null; - } - return http.resultstream(); + /** + * Reads the response from the server, and selects the correct parameters + * which are sent in a GET request to the server asking it to confirm + * the upload and accept the data into the database. Notably, the island id + * and ocean id are determined, while other parameter such as the filename + * are determined from the hidden form fields. + * + * @param in stream of data from the server to read + */ + private boolean finishUpload(InputStream in) throws IOException { + String html = readstreamstring(in); + debug_write_stringdata("pctb-initial.html", html); + Matcher m; + + Pattern params = Pattern.compile + ("(?s)"+ + ".+?"+ + ".+?"); + m = params.matcher(html); + if(!m.find()) { + error_html("The PCTB server returned unusual data."+ + " Maybe you're using an old version of the uploader?", + html); + return false; } - - private String getYarrgTimestamp() throws IOException { - ClientHttpRequest http = new ClientHttpRequest (YARRG_URL); - http.setParameter("clientname", YARRG_CLIENTNAME); - http.setParameter("clientversion", YARRG_CLIENTVERSION); - http.setParameter("clientfixes", YARRG_CLIENTFIXES); - http.setParameter("requesttimestamp", "y"); - InputStream in = post_for_yarrg(http); - if (in == null) return null; - BufferedReader br = new BufferedReader(new InputStreamReader(in)); - String tsresult = br.readLine(); - return tsresult.substring(3, tsresult.length()-1); + String forceReload = m.group(1); + String filename = m.group(2); + + Pattern oceanNumPat = + Pattern.compile(""); + m = oceanNumPat.matcher(html); + if (!m.find()) { + error_html("Unable to find the ocean in the server's list of oceans!", + html); + return false; } - - private boolean runYarrg(String timestamp, String ocean, String island, String yarrgdata) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - BufferedOutputStream bufos = new BufferedOutputStream(new GZIPOutputStream(bos)); - bufos.write(yarrgdata.getBytes() ); - bufos.close(); - byte[] compressed = bos.toByteArray(); - debug_write_bytes("yarrg-deduped.tsv.gz", compressed); - ByteArrayInputStream file = new ByteArrayInputStream(compressed); - - ClientHttpRequest http = new ClientHttpRequest (YARRG_URL); - http.setParameter("clientname", YARRG_CLIENTNAME); - http.setParameter("clientversion", YARRG_CLIENTVERSION); - http.setParameter("clientfixes", YARRG_CLIENTFIXES); - http.setParameter("timestamp", timestamp); - http.setParameter("ocean", ocean); - http.setParameter("island", island); - http.setParameter("data", "deduped.tsv.gz", file, "application/octet-stream"); - InputStream in = post_for_yarrg(http); - if (in == null) return false; - String output = readstreamstring(in); - if (!output.startsWith("OK")) { - error("

Unexpected output from YARRG server

\n" + output); - return false; - } - debug_write_stringdata("yarrg-result.txt", output); - return true; + String oceanNum = m.group(1); + + Pattern oceanIslandNum = + Pattern.compile("islands\\[" + oceanNum + + "\\]\\[\\d+\\]=new Option\\(\"" + islandName + + "\",(\\d+)"); + m = oceanIslandNum.matcher(html); + if(!m.find()) { + error_html("This does not seem to be a valid island! Unable to upload.", + html); + return false; } - - private int calculateArbitrageCommodity(ArrayList> arb_bs) { - // if (dtxt!=null) dtxt.println("ARBITRAGE?"); - int profit = 0; - SortedSet buys = arb_bs.get(0); - SortedSet sells = arb_bs.get(1); - while (true) { - int[] buy, sell; - try { - // NB "sell" means they sell, ie we buy - sell = sells.last(); - buy = buys.first(); - } catch (NoSuchElementException e) { - break; - } - - int unitprofit = buy[0] - sell[0]; - int count = buy[1] < sell[1] ? buy[1] : sell[1]; - // if (dtxt!=null) dtxt.println(" sell @"+sell[0]+" x"+sell[1]+" buy @"+buy[0]+" x"+buy[1] - // +" => x"+count+" @"+unitprofit); - - if (unitprofit <= 0) - break; + String islandNum = m.group(1); + + URL get = new URL(PCTB_HOST_URL + + "upload.php?action=setisland&ocean=" + oceanNum + + "&island=" + islandNum + + "&forcereload=" + forceReload + + "&filename=" + filename); + String complete = readstreamstring(get.openStream()); + debug_write_stringdata("pctb-final.html", complete); + Pattern done = Pattern.compile + ("Your data has been integrated into the database. Thank you!"); + m = done.matcher(complete); + if(m.find()) { + return true; + } else { + error_html("Something was wrong with the final upload parameters!", + complete); + return false; + } + } + + private InputStream post_for_yarrg(ClientHttpRequest http) + throws IOException { + if (!http.post()) { + String err = readstreamstring(http.resultstream()); + error("

Error reported by YARRG server

\n" + err); + return null; + } + return http.resultstream(); + } + + private String getYarrgTimestamp() throws IOException { + ClientHttpRequest http = new ClientHttpRequest (YARRG_URL); + http.setParameter("clientname", YARRG_CLIENTNAME); + http.setParameter("clientversion", YARRG_CLIENTVERSION); + http.setParameter("clientfixes", YARRG_CLIENTFIXES); + http.setParameter("requesttimestamp", "y"); + InputStream in = post_for_yarrg(http); + if (in == null) return null; + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String tsresult = br.readLine(); + return tsresult.substring(3, tsresult.length()-1); + } + + private boolean runYarrg(String timestamp, String ocean, String island, + String yarrgdata) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream bufos = + new BufferedOutputStream(new GZIPOutputStream(bos)); + bufos.write(yarrgdata.getBytes() ); + bufos.close(); + byte[] compressed = bos.toByteArray(); + debug_write_bytes("yarrg-deduped.tsv.gz", compressed); + ByteArrayInputStream file = new ByteArrayInputStream(compressed); + + ClientHttpRequest http = new ClientHttpRequest (YARRG_URL); + http.setParameter("clientname", YARRG_CLIENTNAME); + http.setParameter("clientversion", YARRG_CLIENTVERSION); + http.setParameter("clientfixes", YARRG_CLIENTFIXES); + http.setParameter("timestamp", timestamp); + http.setParameter("ocean", ocean); + http.setParameter("island", island); + http.setParameter("data", "deduped.tsv.gz", file, + "application/octet-stream"); + InputStream in = post_for_yarrg(http); + if (in == null) return false; + String output = readstreamstring(in); + if (!output.startsWith("OK")) { + error("

Unexpected output from YARRG server

\n" + output); + return false; + } + debug_write_stringdata("yarrg-result.txt", output); + return true; + } + + private int calculateArbitrageCommodity(ArrayList> arb_bs) { + // if (dtxt!=null) dtxt.println("ARBITRAGE?"); + int profit = 0; + SortedSet buys = arb_bs.get(0); + SortedSet sells = arb_bs.get(1); + while (true) { + int[] buy, sell; + try { + // NB "sell" means they sell, ie we buy + sell = sells.last(); + buy = buys.first(); + } catch (NoSuchElementException e) { + break; + } + + int unitprofit = buy[0] - sell[0]; + int count = buy[1] < sell[1] ? buy[1] : sell[1]; + // if (dtxt!=null) dtxt.println(" sell @"+sell[0]+" x"+sell[1] + // +" buy @"+buy[0]+" x"+buy[1] + // +" => x"+count+" @"+unitprofit); + + if (unitprofit <= 0) + break; - profit += count * unitprofit; - buy[1] -= count; - sell[1] -= count; - if (buy[1]==0) buys.remove(buy); - if (sell[1]==0) sells.remove(sell); - } - // if (dtxt!=null) dtxt.println(" PROFIT "+profit); - return profit; + profit += count * unitprofit; + buy[1] -= count; + sell[1] -= count; + if (buy[1]==0) buys.remove(buy); + if (sell[1]==0) sells.remove(sell); } - - private class arbitrageOfferComparator implements Comparator { - public int compare(Object o1, Object o2) { - int p1 = ((int[])o1)[0]; - int p2 = ((int[])o2)[0]; - return p2 - p1; - } + // if (dtxt!=null) dtxt.println(" PROFIT "+profit); + return profit; + } + + private class arbitrageOfferComparator implements Comparator { + public int compare(Object o1, Object o2) { + int p1 = ((int[])o1)[0]; + int p2 = ((int[])o2)[0]; + return p2 - p1; } + } - private @SuppressWarnings("unchecked") + private @SuppressWarnings("unchecked") void calculateArbitrage(ArrayList> data) { - int arbitrage = 0; - ArrayList> arb_bs = null; - String lastcommod = null; - Comparator compar = new arbitrageOfferComparator(); - - for (ArrayList row : data) { - String thiscommod = row.get(0); - // if (dtxt!=null) dtxt.println("ROW "+row.toString()); - if (lastcommod == null || !thiscommod.equals(lastcommod)) { - if (lastcommod != null) - arbitrage += calculateArbitrageCommodity(arb_bs); - // if (dtxt!=null) dtxt.println("ROW rdy"); - arb_bs = new ArrayList>(2); - arb_bs.add(0, new TreeSet(compar)); - arb_bs.add(1, new TreeSet(compar)); - // if (dtxt!=null) dtxt.println("ROW init"); - lastcommod = thiscommod; - } - for (int bs = 0; bs < 2; bs++) { - String pricestr = row.get(bs*2 + 2); - if (pricestr == null) - continue; - int[] entry = new int[2]; - // if (dtxt!=null) dtxt.println("ROW BS "+bs); - entry[0] = parseQty(pricestr); - entry[1] = parseQty(row.get(bs*2 + 3)); - arb_bs.get(bs).add(entry); - } - } - arbitrage += calculateArbitrageCommodity(arb_bs); - if (arbitrage != 0) { - arbitrageResult.setText("arbitrage: "+arbitrage+" poe"); - } else { - arbitrageResult.setText("no arbitrage"); - } + int arbitrage = 0; + ArrayList> arb_bs = null; + String lastcommod = null; + Comparator compar = new arbitrageOfferComparator(); + + for (ArrayList row : data) { + String thiscommod = row.get(0); + // if (dtxt!=null) dtxt.println("ROW "+row.toString()); + if (lastcommod == null || !thiscommod.equals(lastcommod)) { + if (lastcommod != null) + arbitrage += calculateArbitrageCommodity(arb_bs); + // if (dtxt!=null) dtxt.println("ROW rdy"); + arb_bs = new ArrayList>(2); + arb_bs.add(0, new TreeSet(compar)); + arb_bs.add(1, new TreeSet(compar)); + // if (dtxt!=null) dtxt.println("ROW init"); + lastcommod = thiscommod; + } + for (int bs = 0; bs < 2; bs++) { + String pricestr = row.get(bs*2 + 2); + if (pricestr == null) + continue; + int[] entry = new int[2]; + // if (dtxt!=null) dtxt.println("ROW BS "+bs); + entry[0] = parseQty(pricestr); + entry[1] = parseQty(row.get(bs*2 + 3)); + arb_bs.get(bs).add(entry); + } + } + arbitrage += calculateArbitrageCommodity(arb_bs); + if (arbitrage != 0) { + arbitrageResult.setText("arbitrage: "+arbitrage + +" poe"); + } else { + arbitrageResult.setText("no arbitrage"); } + } }