/** * Update the query coverage component. Call this method if the effective selected table may have * changed. */ private void updateQueryCoverage() { if (coverageModel_.isSelected()) { Coverage coverage; if (tcModel_ == null) { coverage = null; } else { int[] rowMap = tcModel_.getViewModel().getRowMap(); ColumnData raData = raSelector_.getColumnData(); ColumnData decData = decSelector_.getColumnData(); ColumnData srData = srSelector_.getColumnData(); if (raData != null && decData != null) { DatasQuerySequenceFactory qsf = new DatasQuerySequenceFactory(raData, decData, srData, rowMap); try { StarTable table = tcModel_.getApparentStarTable(); ConeQueryRowSequence qseq = qsf.createQuerySequence(table); double resDeg = 1.0; coverage = new ConeQueryCoverage(qseq, resDeg); } catch (IOException e) { coverage = null; } } else { coverage = null; } } queryCoverageView_.setCoverage(coverage); updateOverlapCoverage(); } else { queryCoverageView_.setCoverage(null); } }
/** * Constructs and returns a worker thread which can perform a match as specified by the current * state of this component. * * @return new worker thread * @throws RuntimeException if the current state does not fully specify a multi query job; such * exceptions will have comprehensible messages */ private MatchWorker createMatchWorker() { /* Acquire state from this panel's GUI components. */ String sUrl = urlField_.getText(); if (sUrl == null || sUrl.trim().length() == 0) { throw new IllegalArgumentException("No " + service_.getName() + " URL given"); } URL serviceUrl; try { serviceUrl = new URL(sUrl); } catch (MalformedURLException e) { throw (IllegalArgumentException) new IllegalArgumentException("Bad " + service_.getName() + " URL syntax: " + sUrl) .initCause(e); } ConeErrorPolicy erract = (ConeErrorPolicy) erractSelector_.getSelectedItem(); TopcatModel tcModel = tcModel_; if (tcModel == null) { throw new NullPointerException("No table selected"); } final StarTable inTable = tcModel.getApparentStarTable(); int[] rowMap = tcModel.getViewModel().getRowMap(); ColumnData raData = raSelector_.getColumnData(); ColumnData decData = decSelector_.getColumnData(); ColumnData srData = srSelector_.getColumnData(); if (raData == null) { throw new NullPointerException("No RA column given"); } if (decData == null) { throw new NullPointerException("No Dec column given"); } Number parNum = parallelModel_.getNumber(); int parallelism = parNum == null ? 1 : parNum.intValue(); MulticoneMode mcMode = (MulticoneMode) modeSelector_.getSelectedItem(); StarTableFactory tfact = ControlWindow.getInstance().getTableFactory(); /* Assemble objects based on this information. */ ConeSearcher searcher = service_.createSearcher(serviceUrl, tfact); Coverage coverage = coverageModel_.isSelected() ? service_.getCoverage(serviceUrl) : null; DatasQuerySequenceFactory qsf = new DatasQuerySequenceFactory(raData, decData, srData, rowMap); ConeMatcher matcher = mcMode.createConeMatcher(searcher, erract, inTable, qsf, coverage, parallelism); ResultHandler resultHandler = mcMode.createResultHandler(this, tfact.getStoragePolicy(), tcModel, inTable); /* Create MatchWorker encapsulating all of this. */ MatchWorker worker = new MatchWorker(matcher, resultHandler, tcModel, inTable); /* Perform post-construction configuration of constituent objects * as required. */ qsf.setMatchWorker(worker); resultHandler.setMatchWorker(worker); /* Return worker thread. */ return worker; }
/** * Takes a bit mask representing selected rows and causes it to become the Current Row Subset for * the given table. Usually this means creating a new Row Subset corresponding to that mask prior * to applying it. However, in the special case that the mask is identical to an existing subset, * that one will be used instead. * * @param tcModel topcat model * @param mask row selection mask * @param baseName name of the sending application */ private void applyNewSubset(final TopcatModel tcModel, BitSet mask, String appName) { /* See if this is identical to an existing subset. If so, don't * create a new one. It's arguable whether this is the behaviour * that you want, but at least until we have some way to delete * subsets it's probably best to do it like this to cut down on * subset proliferation. */ RowSubset matching = null; for (Iterator it = tcModel.getSubsets().iterator(); matching == null && it.hasNext(); ) { RowSubset rset = (RowSubset) it.next(); int nrow = Tables.checkedLongToInt(tcModel.getDataModel().getRowCount()); if (matches(mask, rset, nrow)) { matching = rset; } } /* If we've found an existing set with the same content, * apply that one. */ if (matching != null) { final RowSubset rset = matching; SwingUtilities.invokeLater( new Runnable() { public void run() { tcModel.applySubset(rset); } }); } /* Otherwise make sure we have a unique name for the new subset. */ else { int ipset = 0; for (Iterator it = tcModel.getSubsets().iterator(); it.hasNext(); ) { String setName = ((RowSubset) it.next()).getName(); if (setName.matches(appName + "-[0-9]+")) { String digits = setName.substring(appName.length() + 1); ipset = Math.max(ipset, Integer.parseInt(digits)); } } String setName = appName + '-' + (ipset + 1); /* Then construct, add and apply the new subset. */ final RowSubset rset = new BitsRowSubset(setName, mask); SwingUtilities.invokeLater( new Runnable() { public void run() { tcModel.addSubset(rset); tcModel.applySubset(rset); } }); } }
/** * Attempts to locate a table by its ID. This is currently a URL string; either one got from a * previous VOT_LOADURL message or one inherent in the table. * * @param tableId table identifier URL string * @return tableWithRows object corresponding to tableId, or null */ private TableWithRows lookupTable(String tableId) { TableWithRows tr = (TableWithRows) idMap_.get(tableId); if (tr != null) { return tr; } else { ListModel tablesList = ControlWindow.getInstance().getTablesListModel(); for (int i = 0; i < tablesList.getSize(); i++) { TopcatModel tcModel = (TopcatModel) tablesList.getElementAt(i); URL url = tcModel.getDataModel().getBaseTable().getURL(); if (URLUtils.sameResource(url, URLUtils.makeURL(tableId))) { return new TableWithRows(tcModel, null); } } } return null; }
/** * Sends a table to a specific list of PLASTIC listeners. * * @param tcModel the table model to broadcast * @param hub hub object * @param plasticId registration ID for this application * @param recipients listeners to receive it; null means do a broadcast */ private void transmitTable( TopcatModel tcModel, final PlasticHubListener hub, final URI plasticId, final URI[] recipients) throws IOException { /* Write the data as a VOTable to a temporary file preparatory to * broadcast. */ final File tmpfile = File.createTempFile("plastic", ".vot"); final String tmpUrl = URLUtils.makeFileURL(tmpfile).toString(); tmpfile.deleteOnExit(); OutputStream ostrm = new BufferedOutputStream(new FileOutputStream(tmpfile)); try { new VOTableWriter(DataFormat.TABLEDATA, true) .writeStarTable(tcModel.getApparentStarTable(), ostrm); } catch (IOException e) { tmpfile.delete(); throw e; } finally { ostrm.close(); } /* Store a record of the table that was broadcast with its * state. */ int[] rowMap = tcModel.getViewModel().getRowMap(); idMap_.put(tmpUrl, new TableWithRows(tcModel, rowMap)); /* Do the broadcast, synchronously so that we don't delete the * temporary file too early, but in another thread so that we * don't block the GUI. */ new Thread("PLASTIC table broadcast") { public void run() { List argList = Arrays.asList(new Object[] {tmpUrl, tmpUrl}); Map responses = recipients == null ? hub.request(plasticId, MessageId.VOT_LOADURL, argList) : hub.requestToSubset( plasticId, MessageId.VOT_LOADURL, argList, Arrays.asList(recipients)); /* Delete the temp file. */ tmpfile.delete(); } }.start(); }
public ResultHandler createResultHandler( final JComponent parent, StoragePolicy policy, TopcatModel inTcModel, StarTable inTable) { final String tname = service_.getLabel() + "s(" + inTcModel.getID() + ")"; return new RandomResultHandler(parent, policy, service_) { protected void processRandomResult(final StarTable table) { addTable(tname, table); } }; }
/** * Sets the input table whose rows will specify the multiple queries. * * @param tcModel input table */ private void setInputTable(TopcatModel tcModel) { if (tcModel_ != null && tcListener_ != null) { tcModel_.removeTopcatListener(tcListener_); } tcModel_ = tcModel; if (tcModel_ != null && tcListener_ != null) { tcModel_.addTopcatListener(tcListener_); } /* Set the column selectors up to select from the correct table. */ raSelector_.setTable(tcModel); decSelector_.setTable(tcModel); /* For the size selector, jump through some hoops. * Although its value has the same form as for the RA and Dec * selectors (value may be a JEL expression or column name), * in practice it will often be a constant expression, so * making sure that a default constant value is in place should * make it more obvious for users rather than leaving it blank. * Check if the existing value makes sense for the new table; * if it does, leave it, otherwise, set it to a sensible default. */ if (srSelector_.getModel() == null) { srSelector_.setTable(tcModel); service_.setSizeDefault(srSelector_); } else { String txt = srSelector_.getStringValue(); Object conv = srSelector_.getModel().getConverterModel().getSelectedItem(); srSelector_.setTable(tcModel); ComboBoxModel srColModel = srSelector_.getModel().getColumnModel(); assert srColModel instanceof ColumnDataComboBoxModel; if (srColModel instanceof ColumnDataComboBoxModel) { try { ((ColumnDataComboBoxModel) srColModel).stringToColumnData(txt); srSelector_.setStringValue(txt); srSelector_.getModel().getConverterModel().setSelectedItem(conv); } catch (CompilationException e) { service_.setSizeDefault(srSelector_); } } } }
public ResultHandler createResultHandler( final JComponent parent, StoragePolicy policy, final TopcatModel inTcModel, StarTable inTable) { final int[] rowMap = inTcModel.getViewModel().getRowMap(); return new ResultHandler() { public void processResult(StarTable streamTable) throws IOException { RowSequence rseq = streamTable.getRowSequence(); final BitSet matchMask = new BitSet(); try { while (rseq.next()) { long irow = ((Number) rseq.getCell(0)).longValue(); if (irow < Integer.MAX_VALUE) { int jrow = rowMap == null ? (int) irow : rowMap[(int) irow]; matchMask.set(jrow); } } } finally { rseq.close(); } schedule( new Runnable() { public void run() { addSubset(parent, inTcModel, matchMask); } }); } /** * Using input from the user, adds a new (or reused) Row Subset to the given TopcatModel * based on a given BitSet. * * @param parent parent component * @param tcModel topcat model * @param matchMask mask for included rows */ public void addSubset(JComponent parent, TopcatModel tcModel, BitSet matchMask) { int nmatch = matchMask.cardinality(); Box nameLine = Box.createHorizontalBox(); JComboBox nameSelector = tcModel.createNewSubsetNameSelector(); nameSelector.setSelectedItem("multi" + service_.getLabel()); nameLine.add(new JLabel("Subset name: ")); nameLine.add(nameSelector); Object msg = new Object[] { "Multiple " + service_.getName() + " successful; " + "matches found for " + nmatch + " rows.", " ", "Define new subset for matched rows", nameLine, }; int opt = JOptionPane.showOptionDialog( parent, msg, "Multi-" + service_.getName() + " Success", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); String name = getSubsetName(nameSelector); if (opt == JOptionPane.OK_OPTION && name != null) { tcModel.addSubset(new BitsRowSubset(name, matchMask)); } } /** * Returns the subset name corresponding to the currently selected value of a row subset * selector box. * * @param rsetSelector combo box returned by TopcatModel.createNewSubsetNameSelector * @return subset name as string, or null */ private String getSubsetName(JComboBox rsetSelector) { Object item = rsetSelector.getSelectedItem(); if (item == null) { return null; } else if (item instanceof String) { String name = (String) item; return name.trim().length() > 0 ? name : null; } else if (item instanceof RowSubset) { return ((RowSubset) item).getName(); } else { assert false; return item.toString(); } } }; }
/** * Transmits a request for listening applications to highlight a given table row. * * @param tcModel topcat model of table to broadcast * @param lrow row index within tcModel * @param recipients array of plastic IDs for target applications; if null, broadcast will be to * all * @return true iff message was broadcast successfully */ public boolean highlightRow(TopcatModel tcModel, long lrow, URI[] recipients) throws IOException { /* Get the hub and ID. */ register(); PlasticHubListener hub = getHub(); URI plasticId = getRegisteredId(); int irow = Tables.checkedLongToInt(lrow); /* See if the table we're broadcasting the row for is any of the * tables we've previously broadcast. If so, send the row using the * same ID. */ boolean done = false; int sendRow = -1; String tableId = null; for (Iterator it = idMap_.entrySet().iterator(); !done && it.hasNext(); ) { Map.Entry entry = (Map.Entry) it.next(); TableWithRows tr = (TableWithRows) entry.getValue(); TopcatModel tcm = (TopcatModel) tr.tcModelRef_.get(); if (tcm != null && tcm == tcModel) { int[] rowMap = tr.rowMap_; if (rowMap == null) { sendRow = irow; } else { for (int j = 0; j < rowMap.length; j++) { if (irow == rowMap[j]) { sendRow = j; break; } } } tableId = entry.getKey().toString(); done = true; } } /* If that didn't result in any sends, try using the basic URL of * the table. */ if (!done) { URL url = URLUtils.fixURL(tcModel.getDataModel().getBaseTable().getURL()); if (url != null) { sendRow = irow; tableId = url.toString(); done = true; } } /* Send the message if we've got the arguments. */ if (done && sendRow >= 0) { List args = Arrays.asList( new Object[] { tableId, new Integer(sendRow), }); if (recipients == null) { hub.requestAsynch(plasticId, MessageId.VOT_HIGHLIGHTOBJECT, args); } else { hub.requestToSubsetAsynch( plasticId, MessageId.VOT_HIGHLIGHTOBJECT, args, Arrays.asList(recipients)); } return true; } /* Otherwise return failure status. */ else { return false; } }
/** * Sends a row subset to a specific list of PLASTIC listeners. It uses the <code> * ivo://votech.org/votable/showObjects</code> message. * * @param tcModel topcat model * @param rset row subset within tcModel * @param hub hub object * @param plasticId registration ID for this application * @param recipients listeners to receive it; null means do a broadcast */ private void transmitSubset( TopcatModel tcModel, RowSubset rset, PlasticHubListener hub, URI plasticId, final URI[] recipients) throws IOException { /* See if the table we're broadcasting the set for is any of the * tables we've previously broadcast. If so, send the rows using * the same ID. */ boolean done = false; for (Iterator it = idMap_.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = (Map.Entry) it.next(); String tableId = entry.getKey().toString(); TableWithRows tr = (TableWithRows) entry.getValue(); TopcatModel tcm = (TopcatModel) tr.tcModelRef_.get(); if (tcm != null && tcm == tcModel) { List rowList = new ArrayList(); /* Assemble a list of rows, possibly modulated by the * row order when the table was sent originally. */ int[] rowMap = tr.rowMap_; if (rowMap == null) { int nrow = (int) Math.min((long) Integer.MAX_VALUE, tcModel.getDataModel().getRowCount()); for (int i = 0; i < nrow; i++) { if (rset.isIncluded(i)) { rowList.add(new Integer(i)); } } } else { int nrow = rowMap.length; for (int i = 0; i < nrow; i++) { if (rset.isIncluded(rowMap[i])) { rowList.add(new Integer(i)); } } } /* Send the request. */ List argList = Arrays.asList(new Object[] {tableId, rowList}); if (recipients == null) { hub.requestAsynch(plasticId, MessageId.VOT_SHOWOBJECTS, argList); } else { hub.requestToSubsetAsynch( plasticId, MessageId.VOT_SHOWOBJECTS, argList, Arrays.asList(recipients)); } done = true; } } /* If that didn't result in any sends, try using the basic URL of * the table. */ if (!done) { URL url = URLUtils.fixURL(tcModel.getDataModel().getBaseTable().getURL()); if (url != null) { List rowList = new ArrayList(); int nrow = (int) Math.min((long) Integer.MAX_VALUE, tcModel.getDataModel().getRowCount()); for (int i = 0; i < nrow; i++) { if (rset.isIncluded(i)) { rowList.add(new Integer(i)); } } List argList = Arrays.asList(new Object[] {url.toString(), rowList}); if (recipients == null) { hub.requestAsynch(plasticId, MessageId.VOT_SHOWOBJECTS, argList); } else { hub.requestToSubsetAsynch( plasticId, MessageId.VOT_SHOWOBJECTS, argList, Arrays.asList(recipients)); } } } }