/*
* 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.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.compiler.lang.dsl.DSLMapping;
import org.drools.compiler.lang.dsl.DSLMappingEntry;
import org.drools.compiler.lang.dsl.DSLTokenizedMappingFile;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.builder.Util;
import org.drools.eclipse.editors.completion.DSLTree;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
/**
* This holds the DSL configuration for an editor instance.
* When loading, it will find the DSL file, and load the applicable lists.
*
* This provides a link between the editor and the DSL features of the rule language.
*
* It will look for a DSL configuration, as named in the rule file, in the same directory as the rule file.
* Failing this, it will search one directory above the rule file.
* Failing that, it will search the root of the project in the workspace.
*/
public class DSLAdapter {
private String dslConfigName;
private boolean valid = false;
private List<String> conditionProposals = new ArrayList<String>();
private List<String> consequenceProposals = new ArrayList<String>();
private DSLTree dslTree = new DSLTree();
//to dig out the expander, without using the parser.
private static final Pattern EXPANDER_PATTERN = Pattern.compile( "\\n\\s*expander\\s*(.*)\\.dsl",
Pattern.DOTALL | Pattern.MULTILINE );
/**
* This will sniff out the DSL config file name from the content.
* It will then use the IFile input to search around for the file itself.
* TODO: provide an alternative that just loads off a stream (for non IDEs workbenches like jlibrary).
* @param content Rule source
* @param input File from the FileEditorInput
*/
public DSLAdapter(String content, IFile input) throws CoreException {
dslConfigName = findDSLConfigName( content, input );
if (dslConfigName == null) {
return;
}
loadConfig( input );
}
/** Get a reader to the DSL contents */
public static Reader getDSLContent(String ruleSource, IResource input) throws CoreException {
String dslFileName = findDSLConfigName( ruleSource, input );
if (dslFileName == null) {
return null;
}
IResource res = findDSLResource( input, dslFileName );
if (res instanceof IFile) {
IFile dslConf = (IFile) res;
if (dslConf.exists()) {
return new InputStreamReader(dslConf.getContents());
}
}
return null;
}
/**
* This does the hunting around the projec to find the .dsl file.
*/
private void loadConfig(IFile input) {
IResource res = findDSLResource( input, dslConfigName );
if (res instanceof IFile) {
IFile dslConf = (IFile) res;
if (dslConf.exists()) {
InputStream stream = null;
try {
stream = dslConf.getContents();
readConfig( stream );
valid = true;
} catch ( Exception e ) {
throw new IllegalStateException("Unable to open DSL config file. (Exception: " + e.getMessage() + ")");
} finally {
closeStream( stream );
}
}
}
}
private static IResource findDSLResource(IResource input, String dslFileName) {
IResource res = input.getParent().findMember( dslFileName );
if (res == null) res = input.getParent().getParent().findMember( dslFileName ); //try parent directory
if (res == null) res = input.getProject().findMember( dslFileName ); //try root of project.
return res;
}
/** This will load in the DSL config file, using the DSLMapping from drools-compiler */
void readConfig(InputStream stream) throws IOException, CoreException {
DSLTokenizedMappingFile file = new DSLTokenizedMappingFile();
file.parseAndLoad(new InputStreamReader(stream));
DSLMapping grammar = file.getMapping();
List<DSLMappingEntry> conditions = grammar.getEntries( DSLMappingEntry.CONDITION );
List<DSLMappingEntry> consequences = grammar.getEntries( DSLMappingEntry.CONSEQUENCE );
conditionProposals = buildProposals(conditions);
consequenceProposals = buildProposals(consequences);
dslTree.buildTree(grammar);
}
private List<String> buildProposals(List<DSLMappingEntry> suggestions) {
List<String> result = new ArrayList<String>(suggestions.size());
for (DSLMappingEntry text : suggestions) {
result.add(text.getMappingKey());
}
return result;
}
private void closeStream(InputStream stream) {
if (stream != null) try {
stream.close();
} catch ( IOException e ) {}
}
DSLAdapter() {
}
private static String findDSLConfigName(String content, IResource input) throws CoreException {
String dslConfigName = findDSLConfigName( content );
if (dslConfigName == null) {
// try searching the .package file
if (input != null && input.getParent() != null) {
MyResourceVisitor visitor = new MyResourceVisitor();
input.getParent().accept(visitor, IResource.DEPTH_ONE, IResource.NONE);
IResource packageDef = visitor.getPackageDef();
if (packageDef != null) {
if (packageDef instanceof IFile) {
IFile file = (IFile) packageDef;
try {
String pContent = new String(Util.getResourceContentsAsCharArray(file));
dslConfigName = findDSLConfigName( pContent );
} catch (CoreException e) {
DroolsEclipsePlugin.log(e);
}
}
}
}
}
return dslConfigName;
}
/** Sniffs out the expander/DSL config name as best it can. */
static String findDSLConfigName(String content) {
String name = null;
Matcher matches = EXPANDER_PATTERN.matcher( content );
if (matches.find()) {
name = matches.group(1) + ".dsl";
}
return name;
}
String getDSLConfigName() {
return dslConfigName;
}
public boolean isValid() {
return valid;
}
public boolean hasConditions() {
return conditionProposals.size() > 0;
}
public boolean hasConsequences() {
return consequenceProposals.size() > 0;
}
public List<String> listConditionItems() {
return conditionProposals;
}
public List<String> listConsequenceItems() {
return consequenceProposals;
}
public DSLTree getDSLTree() {
return dslTree;
}
private static class MyResourceVisitor implements IResourceVisitor {
private IResource packageDef;
public boolean visit(IResource resource) throws CoreException {
if ("package".equals(resource.getFileExtension())) {
packageDef = resource;
}
return true;
}
public IResource getPackageDef() {
return packageDef;
}
}
}