package com.openMap1.mapper.presentation;
import java.util.Hashtable;
import java.util.Iterator;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import com.openMap1.mapper.actions.RunQuerySaveXMLAction;
import com.openMap1.mapper.actions.RunQueryShowSQLAction;
import com.openMap1.mapper.actions.RunQueryToFileAction;
import com.openMap1.mapper.actions.RunQueryToViewAction;
import com.openMap1.mapper.actions.InsertQueryTextAction;
import com.openMap1.mapper.views.LabelledEClass;
import com.openMap1.mapper.views.WorkBenchUtil;
import com.openMap1.mapper.views.DataSourceView;
import com.openMap1.mapper.views.ClassModelView;
import com.openMap1.mapper.util.ModelUtil;
public class QueryEditor extends TextEditor implements ISelectionChangedListener{
private Hashtable<String,LabelledEClass> menuStartClasses = new Hashtable<String,LabelledEClass>();
private RunQueryToViewAction runQueryViewAct;
private RunQueryToFileAction runQueryFileAct;
private RunQueryShowSQLAction runQueryShowSQLAct;
private RunQuerySaveXMLAction runQuerySaveXMLAction;
private boolean tracing = false;
private int maximumMenuDepth = 2;
//---------------------------------------------------------------------------------------
// Noting classes suitable for query when selected
//---------------------------------------------------------------------------------------
/**
* If a class is selected in the class model view, and that class is mapped in all
* query sources of the query source view, note that class for use in query
* editor menus.
*/
public void selectionChanged(SelectionChangedEvent event)
{
trace("selection changed");
ISelection selection = event.getSelection();
DataSourceView dsv = WorkBenchUtil.getDataSourceView(true);
if (selection instanceof IStructuredSelection && ((IStructuredSelection)selection).size() == 1) {
Object object = ((IStructuredSelection)selection).getFirstElement();
LabelledEClass lec = null;
if (object instanceof EClass) lec = new LabelledEClass((EClass)object);
else if (object instanceof LabelledEClass) lec = (LabelledEClass) object;
if ((lec != null) && (dsv != null) && (dsv.classMappedInAllActiveDataSources(lec)))
menuStartClasses.put(lec.eClass().getName(), lec);
}
}
/**
* if the data source view shows any sources, they must all be mapped to
* the same class model.
* That class model is used in menus to add fields to this query.
* Ensure the correct class model is showing in the class model view,
* and make this editor a listener for selections of classes in that view
*/
private void connectToClassModel()
{
trace("Connect to class model");
DataSourceView dsv = WorkBenchUtil.getDataSourceView(false);
ClassModelView cmv = WorkBenchUtil.getClassModelView(false);
if ((dsv != null) && (cmv != null) && (dsv.getClassModelPackage() != null))
{cmv.initiateForQueryEditor(this, dsv.getClassModelPackage(), dsv.getClassModelURIString());}
}
/**
* When this editor gets focus, check the correct class model is showing and connect to it
*/
public void setFocus()
{
connectToClassModel();
}
/**
* When this editor initialises, check the correct class model is showing and connect to it
*/
public void init(IEditorSite site, IEditorInput input) throws PartInitException
{
super.init(site,input);
connectToClassModel();
}
//---------------------------------------------------------------------------------------
// Creating actions and the cascade menu of fields to insert
//---------------------------------------------------------------------------------------
protected void createActions()
{
super.createActions();
runQueryViewAct = new RunQueryToViewAction();
setAction("RunQuery",runQueryViewAct);
runQueryFileAct = new RunQueryToFileAction();
setAction("SaveQueryResult",runQueryFileAct);
runQueryShowSQLAct = new RunQueryShowSQLAction();
setAction("ShowSQL",runQueryShowSQLAct);
runQuerySaveXMLAction = new RunQuerySaveXMLAction();
setAction("SaveXML",runQuerySaveXMLAction);
}
protected void editorContextMenuAboutToShow(IMenuManager menu) {
runQueryViewAct.setDataSourceView(WorkBenchUtil.getDataSourceView(true));
runQueryViewAct.setQueryEditor(this);
runQueryFileAct.setDataSourceView(WorkBenchUtil.getDataSourceView(true));
runQueryFileAct.setQueryEditor(this);
runQueryShowSQLAct.setDataSourceView(WorkBenchUtil.getDataSourceView(true));
runQueryShowSQLAct.setQueryEditor(this);
runQuerySaveXMLAction.setDataSourceView(WorkBenchUtil.getDataSourceView(true));
runQuerySaveXMLAction.setQueryEditor(this);
try {
addAction(menu, "RunQuery");
addAction(menu, "SaveQueryResult");
addAction(menu, "ShowSQL");
addAction(menu, "SaveXML");
addTextInsertActions(menu);
super.editorContextMenuAboutToShow(menu);
}
catch (Exception ex) {System.out.println ("Menu exception: " + ex.getMessage());}
}
/**
* add the cascading sub-menus of classes, properties and associations
*/
private void addTextInsertActions(IMenuManager menu)
{
trace("add menu items");
MenuManager insertTextSubMenu = new MenuManager("Insert");
for (Iterator <String> it = menuStartClasses.keySet().iterator();it.hasNext();)
{
String className = it.next();
trace("add " + className);
LabelledEClass theClass = menuStartClasses.get(className);
MenuManager classSubMenu = new MenuManager(className);
insertTextSubMenu.add(classSubMenu);
// add menu items for the properties of this class
trace("adding properties" );
addPropertyMenuItems(classSubMenu,theClass,className);
// add sub-menus for associations from this class
Hashtable <String,EClass> visitedClasses = new Hashtable <String,EClass>();
visitedClasses.put(className, theClass.eClass());
addAssociationMenuItems(classSubMenu,theClass, visitedClasses, className,maximumMenuDepth);
}
// the 'Insert' sub-menu will only get shown if there are some class sub-menus below it
menu.add(insertTextSubMenu);
}
/**
* Add menu items and Actions to put text into the query for the properties
* of a class
* @param classSubMenu sub-menu for the class reached by the menu trail
* @param ec EClass the class reached by the menu trail
* @param trailName text to be put into the query, up to an including the current class name
*/
private void addPropertyMenuItems(MenuManager classSubMenu, LabelledEClass lec, String trailName)
{
DataSourceView qsv = WorkBenchUtil.getDataSourceView(true);
if (qsv == null) return;
for (Iterator <EAttribute> it = lec.eClass().getEAllAttributes().iterator();it.hasNext();)
{
String propName = it.next().getName();
// only add a property if it has property mappings in all active query sources
if (qsv.propertyMappedInAllActiveDataSources(lec,propName))
{
String fieldName = trailName + "." + propName + " ";
classSubMenu.add(new InsertQueryTextAction(this, propName, fieldName));
}
}
}
/**
* Add sub-menus for the associations of a class, leading to menu items and actions
* for the properties of classes reached by those associations
* @param classSubMenu sub-menu for the class reached by the menu trail
* @param ec EClass the class reached by the menu trail
* @param visitedClasses Vector of classes visited already in the trail
* of associations, not to be visited again
* @param trailName text to be put into the query, up to an including the current class name
*/
private void addAssociationMenuItems(MenuManager classSubMenu, LabelledEClass lec,
Hashtable <String,EClass> visitedClasses, String trailName,int menuDepth)
{
trace("Adding associations " + trailName);
DataSourceView qsv = WorkBenchUtil.getDataSourceView(true);
if (qsv == null) return;
for (Iterator<EReference> it = lec.eClass().getEAllReferences().iterator(); it.hasNext();)
{
EReference er = it.next();
String roleName = er.getName();
EClass otherSuper = er.getEReferenceType();
// iterate over all subclasses at the other end which inherit the association
for (Iterator<EClass> is = ModelUtil.getAllSubClasses(otherSuper).iterator();is.hasNext();)
{
EClass other = is.next();
LabelledEClass lother = new LabelledEClass(other);
// do not include any association which would get back to a class visited already
if (visitedClasses.get(other.getName()) == null)
{
/* only add a sub-menu for an association if it has mappings in all
* active query sources */
if (qsv.associationMappedInAllActiveDataSources(lec,roleName,other))
{
// menu item names are '(role)class'
String subMenuName = "(" + roleName + ")" + other.getName();
String newTrailName = trailName + "." + subMenuName;
MenuManager linkSubMenu = new MenuManager(subMenuName);
classSubMenu.add(linkSubMenu);
// add menu items for the properties of the class reached by the link
addPropertyMenuItems(linkSubMenu,lother,newTrailName);
/* add sub-menus for the further associations of the class reached by the link,
* to a maximum depth. */
if (menuDepth > 0)
addAssociationMenuItems(linkSubMenu,lother,
newVisitedClasses(visitedClasses,other), newTrailName, menuDepth -1);
}
}
}
}
}
/**
* @return a Hashtable of classes visited so far, extended by one more class
* - without altering the original
*/
private Hashtable <String,EClass> newVisitedClasses
(Hashtable <String,EClass> visitedClasses, EClass nextClass)
{
Hashtable <String,EClass> newVisitedClasses = new Hashtable <String,EClass>();
newVisitedClasses.put(nextClass.getName(), nextClass);
for (Iterator<String> ix = visitedClasses.keySet().iterator(); ix.hasNext();)
{
String cName = ix.next();
newVisitedClasses.put(cName, visitedClasses.get(cName));
}
return newVisitedClasses;
}
//---------------------------------------------------------------------------------------
// Inserting text after menu selections
//---------------------------------------------------------------------------------------
/**
* insert a string of new text at the cursor
* @param text
*/
public void insertTextAtCursor(String text)
{
String currentQueryText = getDocumentProvider().getDocument(getEditorInput()).get();
String cursor = getCursorPosition();
System.out.println(cursor);
// do not allow text inserts in or before the initial 'Select'
if (insertPosition(cursor) > 5)
{
String newQueryText = insertAtCursor(currentQueryText,text,insertPosition(cursor));
getDocumentProvider().getDocument(getEditorInput()).set(newQueryText);
}
}
/**
* number of characters of existing text before the new text insert
* @param cursor
* @return
*/
private int insertPosition(String cursor)
{
int pos = -1;
// strip off the initial '1 : ', convert to int and subtract 1
if ((cursor != null) && (cursor.startsWith("1 : ")))
pos = new Integer(cursor.substring(4)).intValue() -1;
return pos;
}
private String insertAtCursor(String prevText,String insertText,int insertPos)
{
return (prevText.substring(0,insertPos) + insertText + prevText.substring(insertPos));
}
private void trace(String s) {if (tracing) System.out.println(s);}
}