package com.openMap1.mapper.actions; import java.util.Vector; import java.util.Hashtable; import java.util.Iterator; import java.io.InputStream; import java.awt.Color; import org.eclipse.jface.action.Action; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.core.resources.IFile; import com.openMap1.mapper.query.DataSource; import com.openMap1.mapper.query.MatchResult; import com.openMap1.mapper.query.QueryManager; import com.openMap1.mapper.query.QueryParser; import com.openMap1.mapper.query.CellContent; import com.openMap1.mapper.views.QueryResultView; import com.openMap1.mapper.views.DataSourceView; import com.openMap1.mapper.views.WorkBenchUtil; import com.openMap1.mapper.presentation.QueryEditor; import com.openMap1.mapper.core.MapperException; import com.openMap1.mapper.util.FileUtil; import com.openMap1.mapper.util.GenUtil; import com.openMap1.mapper.util.XMLUtil; /** * Action to run the current query on the active data sources in the Data * Source View, and either * (a) show the results in the Query Results View; or * (b) save the rests in one csv file per data source * * This is the abstract superclass of two classes, to do each of these. * * @author robert * */ public abstract class RunQueryAction extends Action{ private DataSourceView dataSourceView = null; private QueryEditor queryEditor = null; private String queryText = null; private int maxQuerySize = 10000; // who would write a query bigger than 10000 characters? protected boolean tracing = false; // all errors detected when parsing, defining strategy, or executing the query protected Vector<String[]> errors; public RunQueryAction() { super(); setImageDescriptor(PlatformUI.getWorkbench().getSharedImages(). getImageDescriptor(ISharedImages.IMG_OBJS_INFO_TSK)); // setActionDefinitionId("RunQuery"); } /** * the Data Source View must be set before run() * @param dataSourceView */ public void setDataSourceView(DataSourceView dataSourceView) {this.dataSourceView = dataSourceView;} /** * * @param text The query text, which must be set before running */ public void setQueryText(String text) {queryText = text;} public void setQueryEditor(QueryEditor queryEditor) {this.queryEditor = queryEditor;} static int SHOW_RESULTS_IN_VIEW = 0; static int SAVE_RESULTS_IN_FILES = 1; static int SHOW_SQL = 2; static int SAVE_RDB_XML = 3; abstract int function(); public void run() { // initialise the list of errors errors = new Vector<String[]>(); // check there are some active query sources if (dataSourceView == null) { recordError("", "Cannot run query; there is no data source view open",errors); writeErrors(errors); return; } /* Try to refresh all data sources. If any cannot be refreshed, they * are made inactive. true means allow the user to try a new user name and password, for a relational source */ dataSourceView.refreshAllActiveSources(true); Vector<DataSource> activeQuerySources = dataSourceView.getActiveDataSources(); if (activeQuerySources.size() == 0) { recordError("","Cannot run query; there are no active data sources",errors); writeErrors(errors); return; } trace("Active data sources: " + activeQuerySources.size()); // get hold of the query text if (queryEditor != null) try { // save the current state of the query queryEditor.doSave(null); // get the saved file IEditorInput input = queryEditor.getEditorInput(); if (input instanceof FileEditorInput) { IFile savedFile = ((FileEditorInput)input).getFile(); InputStream stream = savedFile.getContents(); byte[] bytes = new byte[maxQuerySize]; int chars = stream.read(bytes); setQueryText(new String(bytes,0,chars)); if ((function() == SHOW_RESULTS_IN_VIEW)||(function() == SHOW_SQL)||(function() == SAVE_RDB_XML)) evaluateAndMergeQuery(queryText,activeQuerySources,WorkBenchUtil.getQueryResultView(true),errors, tracing); else if (function() == SAVE_RESULTS_IN_FILES) evaluateAndSaveQuery(queryText,activeQuerySources); if (function() == SHOW_SQL) showSQLQueries(); if (function() == SAVE_RDB_XML) saveRDBXML(); } else throw new MapperException("Editor input is not from a file"); } catch (Exception ex) { if (ex.getMessage() == null) GenUtil.surprise(ex,"RunQueryAction.run"); recordError("", "Cannot evaluate query: " +ex.getMessage(),errors); } } protected void evaluateAndSaveQuery(String queryText,Vector<DataSource> dataSources) throws MapperException { throw new MapperException("method should have been overridden"); } /** * * @param queryText the text of a query * @param dataSources Vector of data sources it is to be evaluated against * @throws MapperException */ public static void evaluateAndMergeQuery(String queryText,Vector<DataSource> dataSources, QueryResultView queryResultView,Vector<String[]> errors, boolean tracing) throws MapperException { boolean mergeDuplicates = true; QueryParser queryParser = QueryManager.evaluateQuery(queryText,dataSources, errors,tracing,mergeDuplicates); // the count column must be numerically sortable; only use it when there is just one data source boolean addCountColumn = (dataSources.size() == 1); Hashtable<String,Vector<CellContent>> mergedResults = new Hashtable<String,Vector<CellContent>>(); for (Iterator<DataSource> iq = dataSources.iterator();iq.hasNext();) { DataSource dataSource = iq.next(); if (dataSource.hasResult()) { mergeOneResult(dataSource,mergedResults,addCountColumn); } } if ((mergedResults.size() > 0) && (errors.size() == 0)) displayUnorderedResult(mergedResults, queryParser,queryResultView,queryText,addCountColumn); else if (mergedResults.size() == 0) recordError("", "Query delivered no result rows from any data source",errors); if (errors.size() > 0) writeErrors(errors); } /** * (for fast matching) * display the results of a query on one or two data sources, un-merged, * limiting to a maximum number of records * @param queryText * @param queryParser * @param dataSources * @param queryResultView * @param maxRowsPerSource */ public static void displayUnMergedResults(String queryText,QueryParser queryParser,Vector<DataSource> dataSources, QueryResultView queryResultView,int maxRowsPerSource) { Hashtable<String,Vector<CellContent>> addedResults = new Hashtable<String,Vector<CellContent>>(); for (Iterator<DataSource> iq = dataSources.iterator();iq.hasNext();) { DataSource dataSource = iq.next(); if (dataSource.hasResult()) { addOneResult(dataSource,addedResults,maxRowsPerSource); } } if (addedResults.size() > 0) try { // the count column is not used for match results boolean addCountColumn = false; displayUnorderedResult(addedResults, queryParser,queryResultView,queryText,addCountColumn); } catch (MapperException ex) { WorkBenchUtil.showMessage("Error", ex.getMessage()); ex.printStackTrace(); } } /** * add the query results from one data source to the set of merged results from all sources - for query results; * merging if necessary with identical result rows from other sources * @param qs * @param mergedResults * @param addCountColumn */ private static void mergeOneResult(DataSource qs,Hashtable<String,Vector<CellContent>> mergedResults,boolean addCountColumn) { for (Iterator<String> it = qs.keyedResults().keySet().iterator();it.hasNext();) { String rowKey = it.next(); Vector<CellContent> row = qs.keyedResults().get(rowKey); Vector<CellContent> existingRow = mergedResults.get(rowKey); /* this exact row has not occurred before from any source. Mark it * with the short name of this source in its first cell, and store it in the merged results */ if (existingRow == null) { Vector<CellContent> newRow = new Vector<CellContent>(); // cell with code of this data source CellContent firstCell = new CellContent(qs.getCode(),Color.black); newRow.add(firstCell); // cells with query result content for (Iterator<CellContent> ir = row.iterator();ir.hasNext();) newRow.add(ir.next()); // only add a count column when there is just one data source, so no previous row like this one if (addCountColumn) { Integer count = qs.getResultCount(rowKey); CellContent lastCell = new CellContent(count.toString(),Color.black); newRow.add(lastCell); } // store the row mergedResults.put(rowKey, newRow); } /* If the row has been found previously, append the short name of this source * onto the short names of * previous sources which have given the identical row. */ else if (existingRow != null) { existingRow.get(0).setText(existingRow.get(0).getText() + qs.getCode()); } } } /** * add the result rows from one data source, with no possible merge with * result rows from other data sources, up to a maximum number of rows * @param qs the data source * @param addedResults * @param maxRows */ private static void addOneResult(DataSource qs,Hashtable<String,Vector<CellContent>> addedResults, int maxRows) { int resultRows = 0; for (Iterator<String> it = qs.keyedResults().keySet().iterator();it.hasNext();) { String rowKey = it.next(); if (resultRows > maxRows) return; // mark the row as coming from this data source Vector<CellContent> row = qs.keyedResults().get(rowKey); Vector<CellContent> newRow = MatchResult.addFrontCell(row,qs.getCode()); addedResults.put(rowKey, newRow); resultRows++; } } private static void displayUnorderedResult(Hashtable<String,Vector<CellContent>> mergedResults, QueryParser queryParser, QueryResultView queryResultView, String queryText, boolean addCountColumn) throws MapperException { queryResultView.setQueryText(queryText); queryResultView.makeTableColumns(queryParser.getColumnHeaders(true), false, addCountColumn); // false = not a fast match result display queryResultView.showNewUnorderedResult(mergedResults); WorkBenchUtil.page().activate(queryResultView); } private static void writeErrors(Vector<String[]> errors) { QueryResultView queryResultView = WorkBenchUtil.getQueryResultView(true); queryResultView.writeErrors(errors); WorkBenchUtil.page().activate(queryResultView); } private static void recordError(String code,String message, Vector<String[]> errors) { String[] errorRow = new String[2]; errorRow[0] = code; errorRow[1] = message; errors.add(errorRow); } private void trace(String s) {if (tracing) System.out.println(s);} /** * */ private void showSQLQueries() { int shown = 0; for (Iterator<DataSource> it = dataSourceView.getActiveDataSources().iterator();it.hasNext();) { DataSource ds = it.next(); if (ds.isRelational()) { WorkBenchUtil.showMessage("SQL for relational data source " + ds.getCode(), ds.getSqlText()); shown++; } } if (shown == 0) WorkBenchUtil.showMessage("Error", "There are no active relational data sources to show their query SQL"); } /** * */ private void saveRDBXML() throws MapperException { int shown = 0; for (Iterator<DataSource> it = dataSourceView.getActiveDataSources().iterator();it.hasNext();) { DataSource ds = it.next(); if (ds.isRelational()) { String[] types = {"*.xml"}; String filePath = FileUtil.getFilePathFromUser(queryEditor,types , ("Save XML from RDBMS source " + ds.getCode()), true); if ((filePath != null) && (filePath.length() > 1)) XMLUtil.writeOutput(ds.getRootNode().getOwnerDocument(), filePath, true); shown++; } } if (shown == 0) WorkBenchUtil.showMessage("Error", "There are no active relational data sources to save their XML"); } }