/*
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.eclipse.dsl.editor;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.drools.compiler.lang.dsl.AntlrDSLMappingEntry;
import org.drools.compiler.lang.dsl.DSLMappingEntry;
import org.drools.compiler.lang.dsl.DSLMappingFile;
import org.drools.compiler.lang.dsl.DSLTokenizedMappingFile;
import org.drools.compiler.lang.dsl.MappingError;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.builder.IDroolsModelMarker;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
/**
* This is the tablular DSL configuration editor.
*/
public class DSLEditor extends EditorPart {
private Table table;
private TableViewer tableViewer;
private NLGrammarModel model; //this is the model that does all the work (from drools-compiler)
private boolean dirty = false; //editing or deleting will make it dirty
private Text exprText; //for language expression
private Text mappingText; //for target rule expression
private Text descriptionText; //just a comment field
private Text objText; // for the object name
private Combo sortCombo; // for the sort field
public void doSave(IProgressMonitor monitor) {
FileEditorInput input = (FileEditorInput) getEditorInput();
File outputFile = input.getFile().getLocation().toFile();
saveFile( monitor,
outputFile,
input );
}
private void saveFile(IProgressMonitor monitor,
File outputFile,
FileEditorInput input) {
try {
validate( input );
FileWriter writer = new FileWriter( outputFile );
DSLMappingFile.saveMapping( writer,
model );
makeClean();
writer.close();
input.getFile().getProject().refreshLocal( IResource.DEPTH_INFINITE,
monitor );
} catch ( IOException e ) {
throw new IllegalStateException( "Unable to save DSL configuration file. (IOException: " + e.getMessage() + ")" );
} catch ( CoreException e ) {
throw new IllegalStateException( "Unable to resync workbench after DSL save. (CoreException: " + e.getMessage() + ")" );
}
}
@SuppressWarnings("unchecked")
private void validate(FileEditorInput input) {
removeProblemsFor( input.getFile() );
List<MappingError> errs = new ArrayList<MappingError>();
for (DSLMappingEntry item : model.getEntries()) {
errs.addAll( item.getErrors() );
}
if ( errs.size() > 0 ) {
for (MappingError mapEr : errs) {
createMarker( input.getFile(),
mapEr.getMessage() + " From [" + mapEr.getTemplateText() + "]",
-1 );
}
}
}
private void createMarker(final IResource res,
final String message,
final int lineNumber) {
try {
IWorkspaceRunnable r = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IMarker marker = res.createMarker( IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER );
marker.setAttribute( IMarker.MESSAGE,
message );
marker.setAttribute( IMarker.SEVERITY,
IMarker.SEVERITY_WARNING );
marker.setAttribute( IMarker.LINE_NUMBER,
lineNumber );
}
};
res.getWorkspace().run( r,
null,
IWorkspace.AVOID_UPDATE,
null );
} catch ( CoreException e ) {
DroolsEclipsePlugin.log( e );
}
}
private void removeProblemsFor(IResource resource) {
try {
if ( resource != null && resource.exists() ) {
resource.deleteMarkers( IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER,
false,
IResource.DEPTH_INFINITE );
}
} catch ( CoreException e ) {
DroolsEclipsePlugin.log( e );
}
}
void makeClean() {
this.dirty = false;
firePropertyChange( PROP_DIRTY );
}
public void doSaveAs() {
// TODO Implement this.
}
public void init(IEditorSite site,
IEditorInput editorInput) throws PartInitException {
setSite( site );
setInput( editorInput );
setVisibleName( editorInput );
try {
InputStream stream = null;
if (editorInput instanceof FileEditorInput) {
FileEditorInput input = (FileEditorInput) editorInput;
stream = input.getFile().getContents();
} else if (editorInput instanceof IStorageEditorInput) {
IStorageEditorInput input = (IStorageEditorInput) editorInput;
stream = input.getStorage().getContents();
}
model = new NLGrammarModel();
DSLMappingFile file = new DSLTokenizedMappingFile();
file.parseAndLoad( new InputStreamReader( stream ) );
model.addEntries( file.getMapping().getEntries() );
stream.close();
} catch ( CoreException e ) {
throw new IllegalStateException( "Unable to load DSL configuration file. (CoreException: " + e.getMessage() + ")" );
} catch ( IOException e ) {
throw new IllegalStateException( "Unabel to close stream fo DSL config file. (IOException: " + e.getMessage() + ")" );
}
}
private void setVisibleName(IEditorInput input) {
setPartName( input.getName() );
setContentDescription( "Editing Domain specific language: [" + input.getName() + "]" );
}
public boolean isDirty() {
return dirty;
}
/**
* Sets the dirty flag, and notifies the workbench.
*/
void makeDirty() {
dirty = true;
firePropertyChange( PROP_DIRTY );
}
public void sortModel() {
if ( sortCombo.getSelectionIndex() == DSLMappingSorter.EXPRESSION ) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.EXPRESSION ) );
} else if ( sortCombo.getSelectionIndex() == DSLMappingSorter.OBJECT ) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.OBJECT ) );
} else if ( sortCombo.getSelectionIndex() == DSLMappingSorter.SCOPE ) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.SCOPE ) );
} else if ( sortCombo.getSelectionIndex() == DSLMappingSorter.MAPPING ) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.MAPPING ) );
}
}
public boolean isSaveAsAllowed() {
// TODO implement SaveAs
return false;
}
public void createPartControl(Composite parent) {
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_BOTH );
parent.setLayoutData( gridData );
// Set numColumns to 3 in the overall grid
GridLayout layout = new GridLayout( 3,
false );
layout.marginWidth = 4;
parent.setLayout( layout );
//create the overall desc field (comments).
createDescriptionField( parent );
// create the table
createTable( parent );
// Create and setup the TableViewer
createTableViewer();
//set up the table "binding" with the model
tableViewer.setContentProvider( new DSLContentProvider( tableViewer,
model ) );
tableViewer.setLabelProvider( new DSLLabelProvider() );
refreshModel();
//setup the fields below the table
createExpressionViewField( parent );
createEditButton( parent );
createMappingViewField( parent );
createDeleteButton( parent );
createObjectViewField( parent );
createAddButton( parent );
createSortField( parent );
createSortButton( parent );
createCopyButton( parent );
//listeners on the table...
createTableListeners();
}
/**
* Setup table listeners for GUI events.
*/
private void createTableListeners() {
//setup views into current selected
table.addSelectionListener( new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
populate();
}
public void widgetDefaultSelected(SelectionEvent e) {
populate();
}
private void populate() {
DSLMappingEntry selected = getCurrentSelected();
exprText.setText( selected.getMappingKey() );
mappingText.setText( selected.getMappingValue() );
objText.setText( selected.getMetaData().getMetaData() == null ? "" : selected.getMetaData().getMetaData() );
}
} );
//double click support
table.addMouseListener( new MouseListener() {
public void mouseDoubleClick(MouseEvent e) {
showEditPopup();
}
public void mouseDown(MouseEvent e) {
}
public void mouseUp(MouseEvent e) {
}
} );
}
private void createDescriptionField(Composite parent) {
Label descLbl = new Label( parent,
SWT.NONE );
descLbl.setText( "Description:" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
descLbl.setLayoutData( gridData );
descriptionText = new Text( parent,
SWT.BORDER );
descriptionText.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
descriptionText.setText( model.getDescription() == null ? "" : model.getDescription() );
descriptionText.addModifyListener( new ModifyListener() {
public void modifyText(ModifyEvent e) {
String text = descriptionText.getText();
if ( !text.equals( model.getDescription() ) ) {
model.setDescription( text );
makeDirty();
}
}
} );
}
private void createMappingViewField(Composite parent) {
Label mapping = new Label( parent,
SWT.NONE );
mapping.setText( "Mapping:" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
mapping.setLayoutData( gridData );
mappingText = new Text( parent,
SWT.BORDER );
mappingText.setEditable( false );
mappingText.setLayoutData( new GridData( GridData.FILL_HORIZONTAL ) );
}
private void createExpressionViewField(Composite parent) {
Label expr = new Label( parent,
SWT.NONE );
expr.setText( "Expression:" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
expr.setLayoutData( gridData );
exprText = new Text( parent,
SWT.BORDER );
exprText.setEditable( false );
gridData = new GridData( GridData.FILL_HORIZONTAL );
exprText.setLayoutData( gridData );
}
private void createObjectViewField(Composite parent) {
Label obj = new Label( parent,
SWT.NONE );
obj.setText( "Object:" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
obj.setLayoutData( gridData );
objText = new Text( parent,
SWT.BORDER );
objText.setEditable( false );
gridData = new GridData( GridData.FILL_HORIZONTAL );
objText.setLayoutData( gridData );
}
private void createSortField(Composite parent) {
Label sort = new Label( parent,
SWT.NONE );
sort.setText( "Sort by:" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
sort.setLayoutData( gridData );
sortCombo = new Combo( parent,
SWT.READ_ONLY );
sortCombo.add( "Object",
DSLMappingSorter.OBJECT );
sortCombo.add( "Language Expression",
DSLMappingSorter.EXPRESSION );
sortCombo.add( "Rule Language Mapping",
DSLMappingSorter.MAPPING );
sortCombo.add( "Scope",
DSLMappingSorter.SCOPE );
gridData = new GridData( GridData.FILL_HORIZONTAL );
sortCombo.setLayoutData( gridData );
}
/** Refreshes the table do make sure it is up to date with the model. */
private void refreshModel() {
tableViewer.setInput( model );
}
private void createEditButton(Composite parent) {
// Create and configure the "Add" button
Button add = new Button( parent,
SWT.PUSH | SWT.CENTER );
add.setText( "Edit" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
add.setLayoutData( gridData );
add.addSelectionListener( new SelectionAdapter() {
// Add a task to the ExampleTaskList and refresh the view
public void widgetSelected(SelectionEvent e) {
showEditPopup();
}
} );
}
private void showEditPopup() {
DSLMappingEntry selected = getCurrentSelected();
if (selected != null) {
MappingEditor editor = new MappingEditor( getSite().getShell() );
editor.create();
editor.getShell().setText( "Edit language mapping" );
editor.setTitle( "Edit an existing language mapping item." );
editor.setTitleImage( getTitleImage() );
editor.setNLMappingItem( selected );
editor.open();
if ( !editor.isCancelled() ) {
refreshModel();
makeDirty();
exprText.setText(selected.getMappingKey());
mappingText.setText(selected.getMappingValue());
objText.setText(selected.getMetaData().getMetaData() == null ? "" : selected.getMetaData().getMetaData());
}
}
}
private void createDeleteButton(Composite parent) {
// Create and configure the "Add" button
Button add = new Button( parent,
SWT.PUSH | SWT.CENTER );
add.setText( "Remove" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
add.setLayoutData( gridData );
add.addSelectionListener( new SelectionAdapter() {
// Add a task to the ExampleTaskList and refresh the view
public void widgetSelected(SelectionEvent e) {
for (DSLMappingEntry entry: getAllSelected()) {
model.removeEntry( entry );
}
refreshModel();
makeDirty();
exprText.setText( "" );
mappingText.setText( "" );
objText.setText( "" );
}
} );
}
private void createSortButton(Composite parent) {
// Create and configure the "Add" button
Button sort = new Button( parent,
SWT.PUSH | SWT.CENTER );
sort.setText( "Sort" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
sort.setLayoutData( gridData );
sort.addSelectionListener( new SelectionAdapter() {
// Add a task to the ExampleTaskList and refresh the view
public void widgetSelected(SelectionEvent e) {
sortModel();
refreshModel();
makeDirty();
}
} );
}
/**
* Return the selected item from the table grid thingy.
*/
private DSLMappingEntry getCurrentSelected() {
return (DSLMappingEntry) ((IStructuredSelection) tableViewer.getSelection()).getFirstElement();
}
private List<DSLMappingEntry> getAllSelected() {
List<DSLMappingEntry> result = new ArrayList<DSLMappingEntry>();
for (Object e: ((IStructuredSelection) tableViewer.getSelection()).toList()) {
result.add((DSLMappingEntry) e);
}
return result;
}
private void createAddButton(Composite parent) {
// Create and configure the "Add" button
Button add = new Button( parent,
SWT.PUSH | SWT.CENTER );
add.setText( "Add" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
add.setLayoutData( gridData );
add.addSelectionListener( new SelectionAdapter() {
// Add an item, should pop up the editor
public void widgetSelected(SelectionEvent e) {
DSLMappingEntry newItem = new AntlrDSLMappingEntry();
MappingEditor editor = new MappingEditor( getSite().getShell() );//shell);
editor.create();
editor.getShell().setText( "New language mapping" );
editor.setTitle( "Create a new language element mapping." );
editor.setTitleImage( getTitleImage() );
editor.setNLMappingItem( newItem );
editor.open();
if ( !editor.isCancelled() ) {
model.addEntry( newItem );
refreshModel();
makeDirty();
}
}
} );
}
private void createCopyButton(Composite parent) {
// Create and configure the "Add" button
Button copy = new Button( parent,
SWT.PUSH | SWT.CENTER );
copy.setText( "Copy" );
GridData gridData = new GridData( GridData.HORIZONTAL_ALIGN_BEGINNING );
gridData.widthHint = 80;
copy.setLayoutData( gridData );
copy.addSelectionListener( new SelectionAdapter() {
// Add an item, should pop up the editor
public void widgetSelected(SelectionEvent e) {
DSLMappingEntry curr = getCurrentSelected();
if (curr != null) {
DSLMappingEntry newItem = new AntlrDSLMappingEntry( curr.getSection(),
curr.getMetaData(),
curr.getMappingKey(),
curr.getMappingValue(),
null,
null);
MappingEditor editor = new MappingEditor( getSite().getShell() );//shell);
editor.create();
editor.getShell().setText( "New language mapping" );
editor.setTitle( "Create a new language element mapping from a copy." );
editor.setTitleImage( getTitleImage() );
editor.setNLMappingItem( newItem );
editor.open();
if ( !editor.isCancelled() ) {
model.addEntry( newItem );
refreshModel();
makeDirty();
}
}
}
} );
}
/**
* Create the viewer.
*/
private void createTableViewer() {
tableViewer = new TableViewer( table );
tableViewer.setUseHashlookup( true );
//following is if we want default sorting... my thought is no...
}
/**
* Create the Table
*/
private void createTable(Composite parent) {
int style = SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.HIDE_SELECTION;
table = new Table( parent,
style );
GridData gridData = new GridData( GridData.FILL_BOTH );
gridData.grabExcessVerticalSpace = true;
gridData.horizontalSpan = 3;
table.setLayoutData( gridData );
table.setLinesVisible( true );
table.setHeaderVisible( true );
TableColumn column;
//Expression col
column = new TableColumn( table,
SWT.LEFT,
0 );
column.setText( "Language Expression" );
column.setWidth( 350 );
// Add listener to column so sorted when clicked
column.addSelectionListener( new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.EXPRESSION ) );
}
} );
// 3rd column with task Owner
column = new TableColumn( table,
SWT.LEFT,
1 );
column.setText( "Rule Language Mapping" );
column.setWidth( 200 );
// Add listener to column so sorted when clicked
column.addSelectionListener( new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.MAPPING ) );
}
} );
// 4th column with task PercentComplete
column = new TableColumn( table,
SWT.LEFT,
2 );
column.setText( "Object" );
column.setWidth( 80 );
// 5th column with task PercentComplete
column = new TableColumn( table,
SWT.LEFT,
3 );
column.setText( "Scope" );
column.setWidth( 80 );
// Add listener to column so tasks are sorted when clicked
column.addSelectionListener( new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
tableViewer.setSorter( new DSLMappingSorter( DSLMappingSorter.SCOPE ) );
}
} );
}
public void setFocus() {
}
public void dispose() {
super.dispose();
}
}