/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* 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.outline;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.core.DroolsElement;
import org.drools.eclipse.core.DroolsModelBuilder;
import org.drools.eclipse.core.Package;
import org.drools.eclipse.core.RuleSet;
import org.drools.eclipse.core.ui.DroolsContentProvider;
import org.drools.eclipse.core.ui.DroolsGroupByRuleGroupContentProvider;
import org.drools.eclipse.core.ui.DroolsLabelProvider;
import org.drools.eclipse.core.ui.DroolsTreeSorter;
import org.drools.eclipse.core.ui.FilterActionGroup;
import org.drools.eclipse.editors.AbstractRuleEditor;
import org.drools.compiler.lang.descr.AttributeDescr;
import org.drools.compiler.lang.descr.PackageDescr;
import org.drools.compiler.lang.descr.RuleDescr;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
/**
* Simple outline view of a DRL file. At present this is not wired in with the Parser, so it is fault
* tolerant of incorrect syntax.
* Should provide navigation assistance in large rule files.
*/
public class RuleContentOutlinePage extends ContentOutlinePage {
private AbstractRuleEditor editor;
private RuleSet ruleSet = DroolsModelBuilder.createRuleSet();
private Map<String, RuleDescr> rules;
private boolean groupByRulegroup = false;
private TreeViewer viewer = null;
///////////////////////////////////
// Patterns that the parser uses
// TODO: this should just reuse the existing parser to avoid inconsistencies
// with for example comments
///////////////////////////////////
private static final Pattern RULE_PATTERN1 = Pattern.compile(
"\\n\\s*rule\\s+\"([^\"]+)\"", Pattern.DOTALL);
private static final Pattern RULE_PATTERN2 = Pattern.compile(
"\\n\\s*rule\\s+([^\\s;#\"]+)", Pattern.DOTALL);
private static final Pattern PACKAGE_PATTERN = Pattern.compile(
"\\s*package\\s+([^\\s;#]+);?", Pattern.DOTALL);
private static final Pattern FUNCTION_PATTERN = Pattern.compile(
"\\n\\s*function\\s+(\\S+)\\s+(\\S+)\\(.*?\\)", Pattern.DOTALL);
private static final Pattern TEMPLATE_PATTERN = Pattern.compile(
"\\n\\s*template\\s+([^\\s;#\"]+)", Pattern.DOTALL);
private static final Pattern IMPORT_PATTERN = Pattern.compile(
"\\n\\s*import\\s+([^\\s;#]+);?", Pattern.DOTALL);
private static final Pattern EXPANDER_PATTERN = Pattern.compile(
"\\n\\s*expander\\s+([^\\s;#]+);?", Pattern.DOTALL);
private static final Pattern GLOBAL_PATTERN = Pattern.compile(
"\\n\\s*global\\s+(\\S+)\\s+([^\\s;#]+);?", Pattern.DOTALL);
private static final Pattern QUERY_PATTERN1 = Pattern.compile(
"\\n\\s*query\\s+\"([^\"]+)\"", Pattern.DOTALL);
private static final Pattern QUERY_PATTERN2 = Pattern.compile(
"\\n\\s*query\\s+([^\\s;#\"]+)", Pattern.DOTALL);
public RuleContentOutlinePage(AbstractRuleEditor editor) {
this.editor = editor;
}
DroolsContentProvider contentProvider = null;
DroolsGroupByRuleGroupContentProvider groupByRuleGroupContentProvider = null;
private void setContentProvider() {
IPreferenceStore preferenceStore= DroolsEclipsePlugin.getDefault().getPreferenceStore();
groupByRulegroup = preferenceStore.getBoolean("GroupByRuleGroupAction.isChecked");
contentProvider = new DroolsContentProvider();
groupByRuleGroupContentProvider = new DroolsGroupByRuleGroupContentProvider();
if (groupByRulegroup) {
viewer.setContentProvider(groupByRuleGroupContentProvider);
} else {
viewer.setContentProvider(contentProvider);
}
}
public void createControl(Composite parent) {
super.createControl(parent);
viewer = getTreeViewer();
setContentProvider();
viewer.setLabelProvider(new DroolsLabelProvider());
viewer.setSorter(new DroolsTreeSorter());
viewer.setInput(ruleSet);
FilterActionGroup filterActionGroup = new FilterActionGroup(
viewer, "org.drools.eclipse.editors.outline.RuleContentOutlinePage");
filterActionGroup.fillActionBars(getSite().getActionBars());
update();
// add the listener for navigation of the rule document.
super.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
Object selectionObj = event.getSelection();
if (selectionObj != null && selectionObj instanceof StructuredSelection) {
StructuredSelection sel = (StructuredSelection) selectionObj;
DroolsElement element = (DroolsElement) sel.getFirstElement();
if (element != null) {
editor.selectAndReveal(element.getOffset(),
element.getLength());
}
}
}
});
}
/**
* Updates the outline page.
*/
public void update() {
TreeViewer viewer = getTreeViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control != null && !control.isDisposed()) {
initRules();
populatePackageTreeNode();
viewer.refresh();
control.setRedraw(false);
viewer.expandToLevel(2);
control.setRedraw(true);
}
}
}
/**
* populates the PackageTreeNode with all of its child elements
*
* @param packageTreeNode the node to populate
*/
public void populatePackageTreeNode() {
String ruleFileContents = editor.getContent();
populatePackageTreeNode(ruleFileContents);
}
void populatePackageTreeNode(String ruleFileContents) {
DroolsModelBuilder.clearRuleSet(ruleSet);
Matcher matcher = PACKAGE_PATTERN.matcher(ruleFileContents);
String packageName = null;
int startChar = 0;
int endChar = 0;
if (matcher.find()) {
packageName = matcher.group(1);
startChar = matcher.start(1);
endChar = matcher.end(1);
}
Package pkg = DroolsModelBuilder.addPackage(ruleSet, packageName,
startChar, endChar - startChar);
matcher = RULE_PATTERN1.matcher(ruleFileContents);
while (matcher.find()) {
String ruleName = matcher.group(1);
RuleDescr descr = (RuleDescr) rules.get(ruleName);
if (descr != null) {
DroolsModelBuilder.addRule(pkg, ruleName, null,
matcher.start(1), matcher.end(1) - matcher.start(1),
extractAttributes(descr));
}
}
matcher = RULE_PATTERN2.matcher(ruleFileContents);
while (matcher.find()) {
String ruleName = matcher.group(1);
RuleDescr descr = (RuleDescr) rules.get(ruleName);
if (descr != null) {
DroolsModelBuilder.addRule(pkg, ruleName, null,
matcher.start(1), matcher.end(1) - matcher.start(1),
extractAttributes(descr));
}
}
matcher = FUNCTION_PATTERN.matcher(ruleFileContents);
while (matcher.find()) {
String functionName = matcher.group(2);
DroolsModelBuilder.addFunction(pkg, functionName + "()", null,
matcher.start(2), matcher.end(2) - matcher.start(2));
}
matcher = EXPANDER_PATTERN.matcher(ruleFileContents);
if (matcher.find()) {
String expanderName = matcher.group(1);
DroolsModelBuilder.addExpander(pkg, expanderName, null,
matcher.start(1), matcher.end(1) - matcher.start(1));
}
matcher = IMPORT_PATTERN.matcher(ruleFileContents);
while (matcher.find()) {
String importName = matcher.group(1);
DroolsModelBuilder.addImport(pkg, importName, null,
matcher.start(1), matcher.end(1) - matcher.start(1));
}
matcher = GLOBAL_PATTERN.matcher(ruleFileContents);
while (matcher.find()) {
String globalType = matcher.group(1);
String globalName = matcher.group(2);
String name = globalName + " : " + globalType;
DroolsModelBuilder.addGlobal(pkg, name, null,
matcher.start(2), matcher.end(2) - matcher.start(2));
}
matcher = QUERY_PATTERN1.matcher(ruleFileContents);
while (matcher.find()) {
String queryName = matcher.group(1);
DroolsModelBuilder.addQuery(pkg, queryName, null,
matcher.start(1), matcher.end(1) - matcher.start(1));
}
matcher = QUERY_PATTERN2.matcher(ruleFileContents);
while (matcher.find()) {
String queryName = matcher.group(1);
DroolsModelBuilder.addQuery(pkg, queryName, null,
matcher.start(1), matcher.end(1) - matcher.start(1));
}
matcher = TEMPLATE_PATTERN.matcher(ruleFileContents);
while (matcher.find()) {
String templateName = matcher.group(1);
DroolsModelBuilder.addTemplate(pkg, templateName, null,
matcher.start(1), matcher.end(1) - matcher.start(1));
}
}
RuleSet getRuleSet() {
return ruleSet;
}
private Map<String, String> extractAttributes(RuleDescr ruleDescr) {
Map<String, String> attributes = null;
if (ruleDescr != null) {
attributes = new HashMap<String, String>();
for (AttributeDescr attribute: ruleDescr.getAttributes().values()) {
if (attribute != null && attribute.getName() != null) {
attributes.put(attribute.getName(), attribute.getValue());
}
}
}
return attributes;
}
public void initRules() {
rules = new HashMap<String, RuleDescr>();
try {
DRLInfo drlInfo = DroolsEclipsePlugin.getDefault().parseResource(editor, true, false);
if (drlInfo != null) {
PackageDescr packageDescr = drlInfo.getPackageDescr();
if (packageDescr != null) {
for (RuleDescr ruleDescr: packageDescr.getRules()) {
if (ruleDescr != null && ruleDescr.getName() != null) {
rules.put(ruleDescr.getName(), ruleDescr);
}
}
}
}
} catch (DroolsParserException e) {
DroolsEclipsePlugin.log(e);
}
}
class GroupByRuleGroupAction extends Action {
public GroupByRuleGroupAction() {
super();
setText("Group by Rule Group");
setToolTipText("Group by Rule Group");
setDescription("Group by agenda-group, activation-group or ruleflow-group");
setChecked(groupByRulegroup);
}
@Override
public void run() {
setGroupByRuleGroup(!groupByRulegroup);
}
private void setGroupByRuleGroup(boolean groupBy) {
groupByRulegroup = groupBy;
setChecked(groupBy);
IPreferenceStore preferenceStore= DroolsEclipsePlugin.getDefault().getPreferenceStore();
preferenceStore.setValue("GroupByRuleGroupAction.isChecked", groupBy);
setContentProvider();
viewer.refresh(true);
}
}
@Override
public void makeContributions(IMenuManager menuManager,
IToolBarManager toolBarManager, IStatusLineManager statusLineManager) {
// TODO Auto-generated method stub
GroupByRuleGroupAction groupByAction = new GroupByRuleGroupAction ();
menuManager.add(groupByAction);
super.makeContributions(menuManager, toolBarManager, statusLineManager);
}
}