/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.hibernate.eclipse.hqleditor;
import java.util.ResourceBundle;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
import org.eclipse.jface.text.source.ICharacterPairMatcher;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.eclipse.ui.texteditor.TextOperationAction;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.hibernate.console.ConsoleConfiguration;
import org.hibernate.console.KnownConfigurations;
import org.hibernate.console.QueryPage;
import org.hibernate.eclipse.console.AbstractQueryEditor;
import org.hibernate.eclipse.console.HibernateConsoleMessages;
import org.hibernate.eclipse.console.HibernateConsolePlugin;
import org.hibernate.eclipse.console.views.IQueryParametersPage;
import org.hibernate.eclipse.console.views.QueryPageTabView;
import org.hibernate.eclipse.console.views.QueryParametersPage;
/**
* HQL Editor
*/
public class HQLEditor extends AbstractQueryEditor {
public static final String PLUGIN_NAME = HibernateConsolePlugin.ID;
public static final String HELP_CONTEXT_ID = PLUGIN_NAME + ".hqleditorhelp"; //$NON-NLS-1$
/** The HQL code scanner, which is used for colorizing the edit text. */
private HQLCodeScanner fHQLCodeScanner;
/** The document setup participant object, which is used partition the edit text. */
private HQLEditorDocumentSetupParticipant docSetupParticipant;
/** The projection (code folding) support object. */
private ProjectionSupport fProjectionSupport;
/**
* Constructs an instance of this class. This is the default constructor.
*/
public HQLEditor() {
super();
}
/**
* Creates and installs the editor actions.
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
*/
protected void createActions() {
super.createActions();
ResourceBundle bundle = getResourceBundle();
IAction a = new TextOperationAction( bundle,
"HQLEditor_ContentAssistProposal_", this, ISourceViewer.CONTENTASSIST_PROPOSALS ); //$NON-NLS-1$
a.setActionDefinitionId( ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS );
setAction( "ContentAssistProposal", a ); //$NON-NLS-1$
a = new TextOperationAction( bundle, "HQLEditor_ContentAssistTip_", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION ); //$NON-NLS-1$
a.setActionDefinitionId( ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION );
setAction( "ContentAssistTip", a ); //$NON-NLS-1$
a = new TextOperationAction( bundle, "HQLEditor_ContentFormat_", this, ISourceViewer.FORMAT ); //$NON-NLS-1$
setAction( "ContentFormat", a ); //$NON-NLS-1$
/*a = new HQLConnectAction( bundle, "HQLEditor.connectAction." ); //$NON-NLS-1$
setAction( "HQLEditor.connectAction", a ); //$NON-NLS-1$
*/
/*a = new HQLDisconnectAction( bundle, "HQLEditor.disconnectAction." ); //$NON-NLS-1$
setAction( "HQLEditor.disconnectAction", a ); //$NON-NLS-1$
*/
// a = new ExecuteQueryAction( this ); //$NON-NLS-1$
// setAction( "HQLEditor.runAction", a ); //$NON-NLS-1$
/*a = new HQLSetStatementTerminatorAction( bundle, "HQLEditor.setStatementTerminatorAction." ); //$NON-NLS-1$
setAction( "HQLEditor.setStatementTerminatorAction", a ); //$NON-NLS-1$
*/
}
private ResourceBundle getResourceBundle() {
return ResourceBundle.getBundle( HibernateConsoleMessages.BUNDLE_NAME );
}
/**
* Creates the SWT controls for the editor.
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl( Composite parent ) {
parent.setLayout( new GridLayout(1,false) );
createToolbar(parent);
super.createPartControl( parent );
if (getSourceViewer() != null ){
getSourceViewer().addTextListener(new ITextListener(){
public void textChanged(TextEvent event) {
updateExecButton();
}});
}
// move to base class?
Control control = parent.getChildren()[1];
control.setLayoutData( new GridData( GridData.FILL_BOTH ) );
setProjectionSupport( createProjectionSupport() );
/* Now that we have enabled source folding, make sure everything is
* expanded.
*/
ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
viewer.doOperation( ProjectionViewer.TOGGLE );
/* Set a help context ID to enable F1 help. */
getSite().getWorkbenchWindow().getWorkbench().getHelpSystem().setHelp( parent, HELP_CONTEXT_ID );
// the following is needed to make sure the editor area gets focus when editing after query execution
// TODO: find a better way since this is triggered on every mouse click and key stroke in the editor area
// one more remark: without this code -> JBIDE-4446
StyledText textWidget = getSourceViewer().getTextWidget();
textWidget.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
getSite().getPage().activate(HQLEditor.this);
}
});
textWidget.addMouseListener(new MouseAdapter() {
public void mouseDown(MouseEvent e) {
getSite().getPage().activate(HQLEditor.this);
}
});
initTextAndToolTip(HibernateConsoleMessages.ExecuteQueryAction_run_hql);
}
/**
* Creates, configures, and returns a <code>ProjectionSupport</code>
* object for this editor.
*
* @return the <code>ProjectSupport</code> object to use with this editor
*/
protected ProjectionSupport createProjectionSupport() {
ProjectionViewer viewer = (ProjectionViewer) getSourceViewer();
ProjectionSupport projSupport = new ProjectionSupport( viewer, getAnnotationAccess(), getSharedColors() );
projSupport.addSummarizableAnnotationType( "org.eclipse.ui.workbench.texteditor.error" ); //$NON-NLS-1$
projSupport.addSummarizableAnnotationType( "org.eclipse.ui.workbench.texteditor.warning" ); //$NON-NLS-1$
projSupport.install();
return projSupport;
}
/**
* Creates the source viewer to be used by this editor.
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite,
* org.eclipse.jface.text.source.IVerticalRuler, int)
*/
protected ISourceViewer createSourceViewer( Composite parent, IVerticalRuler ruler, int styles ) {
HQLSourceViewer viewer = new HQLSourceViewer( parent, ruler, getOverviewRuler(), isOverviewRulerVisible(),
styles );
// ensure decoration support has been created and configured.
getSourceViewerDecorationSupport(viewer);
return viewer;
}
/**
* Creates the source viewer configuation to be used by this editor.
*
* @return the new source viewer configuration object
*/
protected HQLSourceViewerConfiguration createSourceViewerConfiguration() {
HQLSourceViewerConfiguration config = new HQLSourceViewerConfiguration( this );
return config;
}
/**
* Sets the input of the outline page after this class has set input.
*
* @param input the new input for the editor
* @see org.eclipse.ui.editors.text.TextEditor#doSetInput(org.eclipse.ui.IEditorInput)
*/
public void doSetInput( IEditorInput input ) throws CoreException {
super.doSetInput( input );
/* Make sure the document partitioner is set up. The document setup
* participant sets up document partitioning, which is used for text
* colorizing and other text features.
*/
IDocumentProvider docProvider = this.getDocumentProvider();
if (docProvider != null) {
IDocument doc = docProvider.getDocument( input );
if (doc != null) {
HQLEditorDocumentSetupParticipant docSetupParticipant = getDocumentSetupParticipant();
docSetupParticipant.setup( doc );
}
}
}
/**
* Sets up this editor's context menu before it is made visible.
*
* @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(org.eclipse.jface.action.IMenuManager)
*/
protected void editorContextMenuAboutToShow( IMenuManager menu ) {
super.editorContextMenuAboutToShow( menu );
menu.add( new Separator() );
addAction( menu, "ContentAssistProposal" ); //$NON-NLS-1$
addAction( menu, "ContentAssistTip" ); //$NON-NLS-1$
addAction( menu, "ContentFormat" ); //$NON-NLS-1$
menu.add( new Separator() );
/*if (getConnectionInfo() == null) {
addAction( menu, "HQLEditor.connectAction" ); //$NON-NLS-1$
}
else {
addAction( menu, "HQLEditor.disconnectAction" ); //$NON-NLS-1$
}*/
addAction( menu, "HQLEditor.runAction" ); //$NON-NLS-1$
//addAction( menu, "HQLEditor.setStatementTerminatorAction" ); //$NON-NLS-1$
}
/**
* Gets an adapter for the given class. Returns the HQL content outline page
* if the get request is for an outline page. Otherwise returns a projection
* adapter if one hasn't already been created.
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
* @see org.eclipse.jface.text.source.projection.ProjectionSupport#getAdapter(org.eclipse.jface.text.source.ISourceViewer,
* java.lang.Class)
*/
@SuppressWarnings("rawtypes")
public Object getAdapter( Class classForWhichAdapterNeeded ) {
Object adapter = null;
if(IQueryParametersPage.class.equals( classForWhichAdapterNeeded )) {
return new QueryParametersPage(this);
}
/* Get and return the content outline page, if that's what's requested. */
if (IContentOutlinePage.class.equals( classForWhichAdapterNeeded )) {
// HQLEditorContentOutlinePage outlinePage = getOutlinePage();
// if (outlinePage == null) {
// outlinePage = createContentOutlinePage();
// setOutlinePage( outlinePage );
// if (getEditorInput() != null) {
// outlinePage.setInput( getEditorInput() );
// }
// }
// adapter = outlinePage;
adapter = null;
}
/* Delegate getting the adapter to the projection support object,
* if there is one. Projection refers to the ability to visibly collapse
* and expand sections of the document.
*/
else if (adapter == null) {
ProjectionSupport projSupport = getProjectionSupport();
if (projSupport != null) {
adapter = projSupport.getAdapter( getSourceViewer(), classForWhichAdapterNeeded );
}
}
/* If we still don't have an adapter let the superclass handle it. */
if (adapter == null) {
adapter = super.getAdapter( classForWhichAdapterNeeded );
}
return adapter;
}
/**
* Gets the document setup participant object associated with this editor.
* The setup participant sets the partitioning type for the document.
*
* @return the current document setup participant
*/
public HQLEditorDocumentSetupParticipant getDocumentSetupParticipant() {
if (docSetupParticipant == null) {
docSetupParticipant = new HQLEditorDocumentSetupParticipant();
}
return docSetupParticipant;
}
/**
* Gets the <code>ProjectionSupport</code> object associated with this
* editor.
*
* @return the current <code>ProjectionSupport</code> object
*/
protected ProjectionSupport getProjectionSupport() {
return fProjectionSupport;
}
/**
* Gets the HQL source code text scanner. Creates a default one if it
* doesn't exist yet.
*
* @return the HQL source code text scanner
*/
public HQLCodeScanner getHQLCodeScanner() {
if (fHQLCodeScanner == null) {
fHQLCodeScanner = new HQLCodeScanner( getHQLColorProvider() );
}
return fHQLCodeScanner;
}
/**
* Gets the color provider for colorizing HQL source code.
*
* @return the HQL color provider
*/
public HQLColors getHQLColorProvider() {
return new HQLColors();
}
/**
* Initializes the editor.
*
* @see org.eclipse.ui.editors.text.TextEditor#initializeEditor()
*/
protected void initializeEditor() {
super.initializeEditor();
setSourceViewerConfiguration( createSourceViewerConfiguration() );
setRangeIndicator( new DefaultRangeIndicator() );
}
public final static String HQL_EDITOR_MATCHING_BRACKETS = "matchingBrackets"; //$NON-NLS-1$
public final static String HQL_EDITOR_MATCHING_BRACKETS_COLOR = "matchingBracketsColor"; //$NON-NLS-1$
@Override
protected void configureSourceViewerDecorationSupport(
SourceViewerDecorationSupport support) {
super.configureSourceViewerDecorationSupport(support);
char[] matchChars = {'(', ')', '[', ']', '{', '}'}; //which brackets to match
ICharacterPairMatcher matcher = new DefaultCharacterPairMatcher(matchChars ,
IDocumentExtension3.DEFAULT_PARTITIONING);
support.setCharacterPairMatcher(matcher);
support.setMatchingCharacterPainterPreferenceKeys(HQL_EDITOR_MATCHING_BRACKETS,HQL_EDITOR_MATCHING_BRACKETS_COLOR);
//Enable bracket highlighting in the preference store
IPreferenceStore store = getPreferenceStore();
store.setDefault(HQL_EDITOR_MATCHING_BRACKETS, true);
store.setDefault(HQL_EDITOR_MATCHING_BRACKETS_COLOR, "128,128,128"); //$NON-NLS-1$
}
/**
* Sets the document setup participant object associated with this editor to
* the given object. The setup participant sets the partitioning type for
* the document.
*
* @return the current document setup participant
*/
public void setDocumentSetupParticipant( HQLEditorDocumentSetupParticipant docSetupParticipant ) {
this.docSetupParticipant = docSetupParticipant;
}
/**
* Sets the <code>ProjectionSupport</code> object associated with this
* editor.
*
* @param projSupport the <code>ProjectionSupport</code> object to use
*/
protected void setProjectionSupport( ProjectionSupport projSupport ) {
fProjectionSupport = projSupport;
}
public ITextViewer getTextViewer() {
return getSourceViewer();
}
protected QueryPage queryPage = null;
public void executeQuery(ConsoleConfiguration cfg) {
final IWorkbenchPage activePage = getEditorSite().getPage();
try {
activePage.showView(QueryPageTabView.ID);
} catch (PartInitException e) {
// ignore
}
if (queryPage == null || !getPinToOneResTab()) {
queryPage = cfg.executeHQLQuery(getQueryString(), getQueryInputModel().getCopyForQuery());
} else {
KnownConfigurations.getInstance().getQueryPageModel().remove(queryPage);
queryPage = cfg.executeHQLQuery(getQueryString(), getQueryInputModel().getCopyForQuery());
}
}
@Override
protected String getConnectedImageFilePath() {
return "icons/images/hql_editor_connect.gif"; //$NON-NLS-1$
}
@Override
protected String getSaveAsFileExtension() {
return "*.hql"; //$NON-NLS-1$
}
/**
* @return the query without single line comments
*/
@Override
public String getQueryString() {
String queryString = getEditorText();
StringBuilder clearHQL = new StringBuilder();
int state = 0;
for (int j = 0; j < queryString.length(); j++) {
if ((queryString.charAt(j) == '\n')
|| ((queryString.charAt(j) == '\r')
&& (j + 1 < queryString.length())
&& (queryString.charAt(j + 1) == '\r'))) {
state = 0;
}
switch (state) {
case -1:// skip all till the end of the line
break;
case 0:// initial state
switch (queryString.charAt(j)) {
case '-':
if (queryString.length() > j + 1 && queryString.charAt(j + 1) == '-') {
state = -1;
}
break;
case '\'':
state = 1;
break;
}
break;
case 1:// quoted string
/*
* Escape character for the quote is doubled quote:
* Example: 'This is escaped quote ('') inside 1 string'.
* Our parser switches to state 0 and back to state 1, hence works correct
* without additional efforts.
*/
if (queryString.charAt(j) == '\'') {/*there is no way to escape it in HQL string*/
state = 0;
}
break;
}
if (state != -1) {
clearHQL.append(queryString.charAt(j));
}
}
return clearHQL.toString();
}
}