/*
* 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.editors.completion;
import static org.drools.compiler.compiler.DRLFactory.buildParser;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.regex.Pattern;
import org.drools.compiler.lang.DRLParser;
import org.drools.compiler.lang.DroolsEditorType;
import org.drools.compiler.lang.DroolsToken;
import org.drools.compiler.lang.Location;
import org.kie.internal.builder.conf.LanguageLevelOption;
/**
* A utility class that invokes the DRLParser on some partial drl text, and
* provides information back about the context of that parsed drl, such as a
* location type, a dialect, and so on.
*/
public class CompletionContext {
static final Pattern MVEL_DIALECT_PATTERN = Pattern.compile(
".*dialect\\s+\"mvel\".*", Pattern.DOTALL);
static final Pattern JAVA_DIALECT_PATTERN = Pattern.compile(
".*dialect\\s+\"java\".*", Pattern.DOTALL);
static final String MVEL_DIALECT = "mvel";
static final String JAVA_DIALECT = "java";
private LinkedList<Object> parserList;
private int location;
private int locationIndex;
private String dialect;
@SuppressWarnings("unchecked")
public CompletionContext(String backText) {
DRLParser parser = getParser(backText);
try {
parser.compilationUnit();
} catch (Exception ex) {
}
parserList = parser.getEditorInterface().get(0).getContent();
deriveLocation();
determineDialect(backText);
}
public boolean isJavaDialect() {
return JAVA_DIALECT.equalsIgnoreCase(dialect);
}
public boolean isMvelDialect() {
return MVEL_DIALECT.equalsIgnoreCase(dialect);
}
public boolean isDefaultDialect() {
return !isJavaDialect() && !isMvelDialect();
}
// note: this is a crude but reasonably fast way to determine the dialect,
// especially when parsing incomplete rules
private void determineDialect(String backText) {
dialect = null;
boolean mvel = MVEL_DIALECT_PATTERN.matcher(backText).matches();
boolean java = JAVA_DIALECT_PATTERN.matcher(backText).matches();
if (mvel) {
dialect = MVEL_DIALECT;
} else if (java) {
dialect = JAVA_DIALECT;
}
}
public Location getLocation() {
Location location = new Location(this.location);
switch (this.location) {
case Location.LOCATION_LHS_INSIDE_CONDITION_START:
int index = findToken("(", Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
if (index != -1) {
Object o = parserList.get(index - 1);
if (o instanceof DroolsToken) {
String className = ((DroolsToken) o).getText();
location.setProperty(Location.LOCATION_PROPERTY_CLASS_NAME, className);
}
}
String propertyName = null;
if (index + 1 < parserList.size()) {
propertyName = "";
}
int i = index + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
propertyName += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME, propertyName);
break;
case Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR:
index = findToken("(", Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
if (index != -1) {
Object o = parserList.get(index - 1);
if (o instanceof DroolsToken) {
String className = ((DroolsToken) o).getText();
location.setProperty(Location.LOCATION_PROPERTY_CLASS_NAME, className);
}
}
propertyName = null;
index = findTokenBack(Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
if (index != -1) {
if (index + 1 < locationIndex) {
propertyName = "";
}
i = index + 1;
while (i < locationIndex) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
String token = ((DroolsToken) o).getText();
if (":".equals(token)) {
propertyName = "";
} else {
propertyName += token;
}
} else {
break;
}
}
location.setProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME, propertyName);
}
break;
case Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT:
int index1 = findToken("(", Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
int index2 = findTokenBack(Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR, locationIndex);
int index3 = findTokenBack(Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
int index4 = findTokenBack(Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT, locationIndex);
if (index1 != -1 && index2 != -1) {
Object o = parserList.get(index1 - 1);
if (o instanceof DroolsToken) {
String className = ((DroolsToken) o).getText();
location.setProperty(Location.LOCATION_PROPERTY_CLASS_NAME, className);
}
}
String operator = "";
for (i = index2 + 1; i < locationIndex; i++) {
Object o = parserList.get(i);
if (o instanceof DroolsToken) {
operator += ((DroolsToken) o).getText();
}
if (index4 != -1 && i >= index4 ){
break;
}
if (i < locationIndex - 1) {
operator += " ";
}
}
location.setProperty(Location.LOCATION_PROPERTY_OPERATOR, operator.trim());
propertyName = null;
if (index1 != -1) {
if (index3 + 1 < locationIndex - 1) {
propertyName = "";
}
i = index3 + 1;
while (i < locationIndex - 1) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
String token = ((DroolsToken) o).getText();
if (":".equals(token)) {
propertyName = "";
} else {
propertyName += token;
}
} else {
break;
}
}
location.setProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME, propertyName);
}
break;
case Location.LOCATION_LHS_INSIDE_CONDITION_END:
index = findToken("(", Location.LOCATION_LHS_INSIDE_CONDITION_START, locationIndex);
if (index != -1) {
Object o = parserList.get(index - 1);
if (o instanceof DroolsToken) {
String className = ((DroolsToken) o).getText();
location.setProperty(Location.LOCATION_PROPERTY_CLASS_NAME, className);
}
}
break;
case Location.LOCATION_LHS_INSIDE_EVAL:
String eval = "";
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
eval += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_EVAL_CONTENT, eval.trim());
break;
case Location.LOCATION_LHS_FROM:
String from = null;
if (locationIndex + 1 < parserList.size()) {
from = "";
}
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_FROM_CONTENT, from);
break;
case Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE:
from = "";
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT, from);
break;
case Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE:
from = "";
index = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE, locationIndex);
index2 = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION, locationIndex);
if (index != -1 && index2 != -1) {
for (i = index + 1; i < index2 - 3; i++) {
Object o = parserList.get(i);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT, from.trim());
}
from = "";
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT, from);
break;
case Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE:
from = "";
index = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE, locationIndex);
index2 = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION, locationIndex);
index3 = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE, locationIndex);
index4 = findTokenBack(Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT, locationIndex);
if (index != -1 && index2 != -1) {
for (i = index + 1; i < index2 - 3; i++) {
Object o = parserList.get(i);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT, from.trim());
}
from = "";
if (index3 != -1 && index4 != -1) {
for (i = index3 + 1; i < index4 - 3; i++) {
Object o = parserList.get(i);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT, from);
}
from = "";
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
from += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_RESULT_CONTENT, from);
break;
case Location.LOCATION_RHS:
String rhs = "";
i = locationIndex + 1;
int endLocationOfLast = Integer.MAX_VALUE - 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
DroolsToken token = (DroolsToken) o;
if (endLocationOfLast + 1 < token.getStartIndex()) {
rhs += " ";
}
rhs += token.getText();
endLocationOfLast = token.getStopIndex();
}
}
location.setProperty(Location.LOCATION_RHS_CONTENT, rhs);
break;
case Location.LOCATION_RULE_HEADER:
String header = "";
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
header += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_HEADER_CONTENT, header);
break;
case Location.LOCATION_RULE_HEADER_KEYWORD:
header = "";
index = findTokenBack(Location.LOCATION_RULE_HEADER, locationIndex);
if (index != -1) {
for (i = index + 1; i < locationIndex; i++) {
Object o = parserList.get(i);
if (o instanceof DroolsToken) {
header += ((DroolsToken) o).getText();
}
}
}
i = locationIndex + 1;
while (i < parserList.size()) {
Object o = parserList.get(i++);
if (o instanceof DroolsToken) {
header += ((DroolsToken) o).getText();
}
}
location.setProperty(Location.LOCATION_HEADER_CONTENT, header.trim());
break;
}
return location;
}
public String getRuleName() {
if (parserList.size() >= 2) {
Object o = parserList.get(1);
if (o instanceof DroolsToken) {
String name = ((DroolsToken) o).getText();
if (name.startsWith("\"") && (name.endsWith("\""))) {
name = name.substring(1, name.length() - 1);
}
return name;
}
}
return null;
}
/**
* Returns the variables defined in the given rule (fragment).
* The key is the name of the variable.
* The value is a list of 2 String:
* - the first one is the class name of the variable
* - the second one is the property of the given class that defines the type of this variable,
* note that this property could be nested,
* if this property is null then the given class is the type of the variable
*/
public Map<String, String[]> getRuleParameters() {
Map<String, String[]> result = new HashMap<String, String[]>();
int i = 0;
int lastLocation = -1;
for (Object o: parserList) {
if (o instanceof DroolsToken) {
DroolsToken token = (DroolsToken) o;
if (DroolsEditorType.IDENTIFIER_VARIABLE.equals(token.getEditorType()) || DroolsEditorType.IDENTIFIER_PATTERN.equals(token.getEditorType())) {
String variableName = token.getText();
if (lastLocation == Location.LOCATION_LHS_BEGIN_OF_CONDITION) {
int j = i + 2;
String className = "";
while (j < parserList.size()) {
Object obj = parserList.get(j++);
if (obj instanceof DroolsToken) {
String s = ((DroolsToken) obj).getText();
if ("(".equals(s)) {
result.put(variableName, new String[] { className, null });
break;
} else {
className += s;
}
}
}
} else if (lastLocation == Location.LOCATION_LHS_INSIDE_CONDITION_START) {
int index = findTokenBack(Location.LOCATION_LHS_BEGIN_OF_CONDITION, i);
int j = index + 3;
String className = "";
while (j < i) {
Object obj = parserList.get(j++);
if (obj instanceof DroolsToken) {
String s = ((DroolsToken) obj).getText();
if ("(".equals(s)) {
break;
} else {
className += s;
}
}
}
j = i + 2;
String propertyName = "";
while (j < parserList.size()) {
Object obj = parserList.get(j++);
if (obj instanceof DroolsToken) {
String s = ((DroolsToken) obj).getText();
if (",".equals(s) || ")".equals(s)) {
result.put(variableName, new String[] { className, propertyName });
break;
} else {
propertyName += s;
}
} else {
result.put(variableName, new String[] { className, propertyName });
}
}
}
}
} else if (o instanceof Integer) {
lastLocation = (Integer) o;
}
i++;
}
return result;
}
private int findToken(String token, int integer, int location) {
int index = location - 1;
while (index >= 0) {
Object o = parserList.get(index);
if (o instanceof DroolsToken) {
if ("(".equals(((DroolsToken) o).getText())) {
if (getNextInteger(index) == integer){
return index;
}
// o = parserList.get(index + 1);
// if (o instanceof Integer) {
// if (integer == (Integer) o) {
// return index;
// }
// }
}
}
index--;
}
return -1;
}
private int findTokenBack(int token, int location) {
int index = location - 1;
while (index >= 0) {
Object o = parserList.get(index);
if (o instanceof Integer) {
if (token == (Integer) o) {
return index;
}
}
index--;
}
return -1;
}
private int getNextInteger(int location) {
int index = location;
while (index >= 0) {
Object o = parserList.get(index);
if (o instanceof Integer) {
return (Integer) o;
}
index++;
}
return -1;
}
private void deriveLocation() {
location = -1;
int i = 0;
for (Object object : parserList) {
if (object instanceof Integer) {
location = (Integer) object;
locationIndex = i;
}
i++;
}
}
private DRLParser getParser(final String text) {
// TODO: support different language levels
DRLParser parser = buildParser(text, LanguageLevelOption.DRL5);
parser.enableEditorInterface();
return parser;
}
}