/** * 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.rulebuilder.editors; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.drools.compiler.DrlParser; import org.drools.eclipse.DroolsEclipsePlugin; import org.drools.eclipse.dsl.editor.DSLAdapter; import org.drools.eclipse.editors.DRLDocumentProvider; import org.drools.eclipse.editors.DRLRuleEditor; import org.drools.eclipse.util.ProjectClassLoader; import org.drools.ide.common.client.modeldriven.SuggestionCompletionEngine; import org.drools.ide.common.server.rules.SuggestionCompletionLoader; import org.drools.ide.common.server.util.BRDRLPersistence; import org.drools.ide.common.server.util.BRXMLPersistence; import org.drools.lang.dsl.DSLTokenizedMappingFile; import org.eclipse.core.internal.resources.Container; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.editors.text.TextEditor; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.ide.ResourceUtil; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.DocumentProviderRegistry; import org.eclipse.ui.texteditor.IDocumentProvider; public class RuleEditor extends FormEditor implements IResourceChangeListener { private BrlPage guidedEditor; private TextEditor xmlEditor = new TextEditor(); private SuggestionCompletionEngine completion; private SuggestionCompletionLoader loader; private FileEditorInput packageEditorInput; private final Document drlDocument; private IResourceChangeListener packageFileTracker = new IResourceChangeListener() { public void resourceChanged(IResourceChangeEvent event) { if (packageEditorInput != null) { IResourceDelta delta = getRootDelta( event.getDelta() ); IPath p1 = delta.getFullPath(); IPath p2 = packageEditorInput.getFile().getFullPath(); if ( p1.equals( p2 ) ) { reloadCompletionEngine(); } } } }; public RuleEditor() { super(); ResourcesPlugin.getWorkspace().addResourceChangeListener( this ); ResourcesPlugin.getWorkspace().addResourceChangeListener( packageFileTracker, IResourceChangeEvent.POST_CHANGE ); drlDocument = new Document(); } protected FormToolkit createToolkit(Display display) { // Create a toolkit that shares colors between editors. return new FormToolkit( DroolsEclipsePlugin.getDefault().getRuleBuilderFormColors( display ) ); } protected void addPages() { guidedEditor = new BrlPage( this ); try { addPage( guidedEditor ); addPage( xmlEditor, getEditorInput() ); DRLRuleEditor drlEditor = new DRLRuleEditor() { protected IDocumentProvider createDocumentProvider() { return new DRLDocumentProvider() { public boolean isReadOnly(Object element) { return true; } public boolean isModifiable(Object element) { return false; } protected IDocument getParentDocument(Object element) { return drlDocument; } }; } }; addPage( drlEditor, xmlEditor.getEditorInput() ); IPath packagePath = getCurrentDirectoryPath( getEditorInput() ); if (packagePath != null) { packagePath = packagePath.append( "drools.package" ); IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile( packagePath ); IJavaProject javaProject = JavaCore.create( file.getProject() ); ClassLoader classLoader = ProjectClassLoader.getProjectClassLoader( javaProject ); loader = new SuggestionCompletionLoader( classLoader ); if ( !file.exists() ) { String defaultHeader = "//This is a package configuration file"; defaultHeader += "\n//Add imports, globals etc here which will be used by all the rule assets in this folder."; InputStream is = new ByteArrayInputStream( defaultHeader.getBytes() ); try { file.create( is, true, null ); } catch ( CoreException e ) { DroolsEclipsePlugin.log( e ); } } packageEditorInput = new FileEditorInput( file ); } reloadCompletionEngine(); setPageText( 1, "BRL Source" ); setPageText( 2, "Generated DRL (read-only)" ); updateName( false ); } catch ( PartInitException e ) { DroolsEclipsePlugin.log( e ); } } private void updateName(boolean forced) { String name = xmlEditor.getTitle(); setPartName( name ); //TODO Add support for other than .brl extensions if ( guidedEditor.getModeller() != null && guidedEditor.getModeller().getModel() != null && (guidedEditor.getModeller().getModel().name == null || forced) ) { String shortName = name.substring( 0, name.length() - ".brl".length() ); guidedEditor.getModeller().getModel().name = shortName; } updateDRLPage(); } private IPath getCurrentDirectoryPath(IEditorInput editorInput) { if (editorInput instanceof FileEditorInput) { return ((FileEditorInput) editorInput).getFile().getFullPath().removeLastSegments( 1 ).addTrailingSeparator(); } return null; } private void reloadCompletionEngine() { if (packageEditorInput == null) { completion = new SuggestionCompletionLoader( null ).getSuggestionEngine( "", new ArrayList(), new ArrayList() ); return; } try { // Load all .dsl files from current dir IPath p = (packageEditorInput).getFile().getFullPath().removeLastSegments( 1 ); Container folder = (Container) ResourcesPlugin.getWorkspace().getRoot().findMember( p, false ); IResource[] files = folder.members( false ); List dslList = new ArrayList(); List enumList = new ArrayList(); for ( int i = 0; i < files.length; i++ ) { String fn = files[i].getName(); if ( fn.endsWith( ".dsl" ) ) { String contents = getFileContents( (IFile) files[i] ); DSLTokenizedMappingFile dsl = new DSLTokenizedMappingFile(); if ( dsl.parseAndLoad( new StringReader( contents ) ) ) { dslList.add( dsl ); } else { //TODO report dsl parse error } } else if ( fn.endsWith( ".enumeration" ) ) { String contents = getFileContents( (IFile) files[i] ); enumList.add(contents); } } // Load suggestion engine String str = getFileContents( packageEditorInput.getFile() ); completion = loader.getSuggestionEngine( str, Collections.EMPTY_LIST, dslList, enumList); } catch ( Exception e ) { DroolsEclipsePlugin.log( e ); } } private String getFileContents(IFile file) { InputStream is; try { is = file.getContents(); StringBuffer out = new StringBuffer(); byte[] b = new byte[4096]; for ( int n; (n = is.read( b )) != -1; ) { out.append( new String( b, 0, n ) ); } return out.toString(); } catch ( CoreException e ) { //TODO Report problem with the file loading return ""; } catch ( IOException e ) { //TODO Report problem with the file loading return ""; } } public boolean isDirty() { return xmlEditor.isDirty() || guidedEditor.isDirty(); } protected void pageChange(int newPageIndex) { super.pageChange( newPageIndex ); IDocument document = getInputDocument(); if ( newPageIndex == 0 ) { boolean newModel = guidedEditor.getModeller().getModel() == null; if ( xmlEditor.isDirty() || newModel ) { guidedEditor.setModelXML( document.get() ); if ( newModel ) { guidedEditor.getModeller().setDirty( false ); } updateName( false ); } guidedEditor.refresh(); } else if ( newPageIndex == 1 ) { if ( guidedEditor.isDirty() ) { document.set( BRXMLPersistence.getInstance().marshal( guidedEditor.getRuleModel() ) ); } } else if ( newPageIndex == 2 ) { //Updating main document for proper workflow when page is switched back to "0" if ( guidedEditor.isDirty() ) { document.set( BRXMLPersistence.getInstance().marshal( guidedEditor.getRuleModel() ) ); } else if ( xmlEditor.isDirty() ) { guidedEditor.setModelXML( document.get() ); } updateDRLPage(); updateName( false ); } } private void updateDRLPage() { String drl = ""; try { drl = BRDRLPersistence.getInstance().marshal( guidedEditor.getRuleModel() ); IResource resource = ResourceUtil.getResource( xmlEditor.getEditorInput() ); Reader reader = DSLAdapter.getDSLContent( drl, resource ); DrlParser parser = new DrlParser(); if ( reader != null ) { drl = parser.getExpandedDRL( drl, reader ); } } catch ( Throwable t ) { StringWriter strwriter = new StringWriter(); t.printStackTrace( new PrintWriter( strwriter ) ); drl = "\nPROBLEM WITH THE DRL CONVERSION!\n\n\nDRL:\n" + drl + "\n\nSTACKTRACE:\n" + strwriter.toString(); } drlDocument.set( drl ); } public void doSave(IProgressMonitor monitor) { IDocument document = getInputDocument(); if ( xmlEditor.isDirty() ) { guidedEditor.setModelXML( document.get() ); } else if ( guidedEditor.isDirty() ) { document.set( BRXMLPersistence.getInstance().marshal( guidedEditor.getRuleModel() ) ); } xmlEditor.doSave( monitor ); guidedEditor.getModeller().setDirty( false ); guidedEditor.refresh(); } private IDocument getInputDocument() { IEditorInput input = getEditorInput(); IDocumentProvider docProvider = DocumentProviderRegistry.getDefault().getDocumentProvider( input ); IDocument document = docProvider.getDocument( input ); return document; } public void doSaveAs() { xmlEditor.doSaveAs(); guidedEditor.getModeller().setDirty( false ); updateName( true ); setInput( xmlEditor.getEditorInput() ); guidedEditor.refresh(); } public boolean isSaveAsAllowed() { return true; } public void resourceChanged(final IResourceChangeEvent event) { if ( event.getType() == IResourceChangeEvent.PRE_CLOSE ) { Display.getDefault().asyncExec( new Runnable() { public void run() { IWorkbenchPage[] pages = getSite().getWorkbenchWindow().getPages(); for ( int i = 0; i < pages.length; i++ ) { if ( ((FileEditorInput) xmlEditor.getEditorInput()).getFile().getProject().equals( event.getResource() ) ) { IEditorPart editorPart = pages[i].findEditor( xmlEditor.getEditorInput() ); pages[i].closeEditor( editorPart, true ); } } } } ); } } public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener( this ); ResourcesPlugin.getWorkspace().removeResourceChangeListener( packageFileTracker ); super.dispose(); } public void dirtyPropertyChanged() { firePropertyChange( IEditorPart.PROP_DIRTY ); guidedEditor.refresh(); } public SuggestionCompletionEngine getCompletionEngine() { return completion; } private IResourceDelta getRootDelta(IResourceDelta delta) { if ( delta.getAffectedChildren().length > 0 ) { return getRootDelta( delta.getAffectedChildren()[0] ); } return delta; } }