/*
* 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 java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.drools.compiler.lang.dsl.DSLMapping;
import org.drools.compiler.lang.dsl.DSLMappingEntry;
import org.drools.compiler.lang.dsl.DSLMappingEntry.Section;
public class DSLTree {
public static final String separator = "=";
public static final String tab = " ";
private Node current = null;
private Node last = null;
private Node rootCond = null;
private Node rootConseq = null;
private boolean empty = true;
private ArrayList<String> suggestions = new ArrayList<String>();
private HashMap<String, String> objToNL = new HashMap<String, String>();
public DSLTree() {
this.rootCond = new Node("root");
this.rootConseq = new Node("root");
}
/**
* the method will take the dsl file and build a DSLTree using
* the Node class.
* @param dslFile
*/
public void buildTree(String dslFile) {
buildTree(openDSLFile(dslFile));
}
/**
* the method uses the DSLAdapter to get the contents of the
* DSL mapping file.
* @param dslcontents
*/
public void buildTree(Reader dslcontents) {
buildTree(createBufferedReader(dslcontents));
}
private void buildTree(BufferedReader breader) {
this.rootCond.clearChildren();
this.rootConseq.clearChildren();
parseFile(breader);
try {
breader.close();
} catch (IOException e) {
e.printStackTrace();
}
this.empty = false;
}
/**
* method will create a BufferedReader to read the file.
* @param filename
* @return
*/
protected BufferedReader openDSLFile(String filename) {
try {
FileReader reader = new FileReader(filename);
BufferedReader breader = new BufferedReader(reader);
return breader;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Create a buffered reader for the reader created by the DSLAdapater
* @param reader
* @return
*/
protected BufferedReader createBufferedReader(Reader reader) {
return new BufferedReader(reader);
}
/**
* if the DSL mapping hasn't been loaded, the method will return
* true. If the DSL mapping has been loaded, the method returns
* false.
* @return
*/
public boolean isEmpty() {
return this.empty;
}
/**
* method will use the BufferedReader to read the contents of the file.
* It calls other methods to parse the line and build the tree.
* @param reader
*/
protected void parseFile(BufferedReader reader) {
String line = null;
try {
while ( (line = reader.readLine()) != null) {
Section section = getSection(line);
String nl = stripHeadingAndCode(line);
String objname = this.getObjMetadata(nl);
nl = this.stripObjMetadata(nl);
addEntry(section, nl, objname);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void buildTree(DSLMapping mapping) {
for (DSLMappingEntry entry : mapping.getEntries()) {
Section section = entry.getSection();
String nl = entry.getMappingKey();
String objname = entry.getMetaData().getMetaData();
addEntry(section, nl, objname);
}
}
private void addEntry(Section section, String nl, String objname) {
if (!nl.startsWith("-")) {
if (objname != null && !"".equals(objname)) {
this.addObjToNLMap(objname, nl);
}
String[] tokenz = nl.split("\\s");
if (section == DSLMappingEntry.CONDITION || section == DSLMappingEntry.ANY) {
addTokens(tokenz, rootCond);
}
if (section == DSLMappingEntry.CONSEQUENCE || section == DSLMappingEntry.ANY) {
addTokens(tokenz, rootConseq);
}
} else {
String res = this.objToNL.get(objname);
StringTokenizer tokenz = new StringTokenizer(nl);
addTokens(res,tokenz);
}
}
public void addObjToNLMap(String objname, String nl) {
if (!objname.startsWith("-")) {
this.objToNL.put(objname, nl);
}
}
protected Section getSection(String text) {
if (text.startsWith(DSLMappingEntry.CONDITION.getSymbol())) {
return DSLMappingEntry.CONDITION;
} else if (text.startsWith(DSLMappingEntry.CONSEQUENCE.getSymbol())) {
return DSLMappingEntry.CONSEQUENCE;
} else if (text.startsWith(DSLMappingEntry.ANY.getSymbol())) {
return DSLMappingEntry.ANY;
} else if (text.startsWith(DSLMappingEntry.KEYWORD.getSymbol())) {
return DSLMappingEntry.KEYWORD;
}
return null;
}
/**
* method will strip out the when, then, * at the beginning of each
* line and the mapped drl expression
* @param text
* @return
*/
protected String stripHeadingAndCode(String text) {
if (text.startsWith(DSLMappingEntry.CONDITION.getSymbol())) {
return text.substring(DSLMappingEntry.CONDITION.getSymbol().length() + 2,text.indexOf("="));
} else if (text.startsWith(DSLMappingEntry.CONSEQUENCE.getSymbol())) {
return text.substring(DSLMappingEntry.CONSEQUENCE.getSymbol().length() + 2,text.indexOf("="));
} else if (text.startsWith(DSLMappingEntry.ANY.getSymbol())) {
return text.substring(DSLMappingEntry.ANY.getSymbol().length() + 2,text.indexOf("="));
} else if (text.startsWith("#")) {
return "";
} else {
return text;
}
}
/**
* Method will return just the object metadata
* @param text
* @return
*/
protected String getObjMetadata(String text) {
if (text.startsWith("[")) {
return text.substring(1,text.lastIndexOf("]"));
} else {
return "";
}
}
/**
* method will strip the metadata from the text string
* @param text
* @return
*/
protected String stripObjMetadata(String text) {
if (text.startsWith("[")) {
return text.substring(text.lastIndexOf("]") + 1);
} else {
return text;
}
}
/**
* The method is different than addTokens(StringTokenizer). this method
* expects additional metadata. It expects to get an object name or "*"
* meaning all. If the metadata is a wildcard all, it will add the
* tokens to all the top level nodes that are immediate child of root.
* @param metadata
* @param tokens
*/
public void addTokens(String metadata, StringTokenizer tokens) {
Node mnode = this.rootCond.addToken(metadata);
Node thenode = mnode;
while (tokens.hasMoreTokens()) {
Node newnode = thenode.addToken(tokens.nextToken());
thenode = newnode;
}
}
/**
* method adds the token to root
* @param tokens
*/
public void addTokens(String[] tokens, Node rootNode) {
Node thenode = rootNode;
for (int i = 0; i < tokens.length; i++) {
Node newnode = thenode.addToken(tokens[i]);
thenode = newnode;
}
}
/**
* the method will tokenize the text and try to find
* the node that matches and return the children. the method
* will traverse down the network as far as it can and return
* the children at that level.
* @param text
* @return
*/
public Node[] getConditionChildren(String text) {
Node thenode = this.rootCond;
if (text.length() > 0) {
StringTokenizer tokenz = new StringTokenizer(text);
this.last = this.current;
while (tokenz.hasMoreTokens()) {
String strtk = tokenz.nextToken();
Node ch = thenode.getChild(strtk);
// if a child is found, we set thenode to the child Node
if (ch != null) {
thenode = ch;
} else {
break;
}
}
if (thenode != this.rootCond) {
this.current = thenode;
}
}
Collection<Node> children = thenode.getChildren();
Node[] nchild = new Node[children.size()];
return children.toArray(nchild);
}
/**
* the method will tokenize the text and try to find
* the node that matches and return the children. the method
* will traverse down the network as far as it can and return
* the children at that level.
* @param text
* @return
*/
public Node[] getConsequenceChildren(String text) {
Node thenode = this.rootConseq;
if (text.length() >= 0) {
StringTokenizer tokenz = new StringTokenizer(text);
this.last = this.current;
while (tokenz.hasMoreTokens()) {
String strtk = tokenz.nextToken();
Node ch = thenode.getChild(strtk);
// if a child is found, we set thenode to the child Node
if (ch != null) {
thenode = ch;
} else {
break;
}
}
if (thenode != this.rootConseq) {
this.current = thenode;
}
}
Collection<Node> children = thenode.getChildren();
Node[] nchild = new Node[children.size()];
return children.toArray(nchild);
}
/**
* the method expects the caller to pass the object
* @param obj
* @param text
* @return
*/
public Node[] getChildren(String obj, String text) {
Node thenode = this.rootCond.getChild(obj);
if (thenode == null) {
for (Node child: this.rootCond.getChildren()) {
String tokenText = child.getToken();
if (tokenText != null) {
int index = tokenText.indexOf("{");
if (index != -1) {
String substring = tokenText.substring(0, index);
if (obj != null && obj.startsWith(substring)) {
thenode = child;
}
}
}
}
}
if (thenode != null && text.length() > 0) {
StringTokenizer tokenz = new StringTokenizer(text);
this.last = this.current;
while (tokenz.hasMoreTokens()) {
String strtk = tokenz.nextToken();
Node ch = thenode.getChild(strtk);
// if a child is found, we set thenode to the child Node
if (ch != null) {
thenode = ch;
} else {
break;
}
}
if (thenode != this.rootCond) {
this.current = thenode;
}
}
if (thenode == null) {
return null;
// thenode = this.rootCond;
}
Collection<Node> children = thenode.getChildren();
Node[] nchild = new Node[children.size()];
return children.toArray(nchild);
}
/**
* for convienance, the method will return a list of strings
* that are children of the last node found. If the editor
* wants to generate the children strings, call the method
* with true
* @param text
* @return
*/
public ArrayList<String> getConditionChildrenList(String text, boolean addChildren) {
Node[] c = getConditionChildren(text);
this.suggestions.clear();
for (int idx=0; idx < c.length; idx++) {
this.suggestions.add(c[idx].getToken());
if (addChildren) {
this.addChildToList(c[idx], c[idx].getToken(), this.suggestions);
}
}
return this.suggestions;
}
/**
* for convienance, the method will return a list of strings
* that are children of the last node found. If the editor
* wants to generate the children strings, call the method
* with true
* @param text
* @return
*/
public ArrayList<String> getConsequenceChildrenList(String text, boolean addChildren) {
Node[] c = getConsequenceChildren(text);
this.suggestions.clear();
for (int idx=0; idx < c.length; idx++) {
if (addChildren) {
this.addChildToList(c[idx], c[idx].getToken(), this.suggestions);
} else {
this.suggestions.add(c[idx].getToken());
}
}
return this.suggestions;
}
/**
*
* @param obj
* @param text
* @param addChildren
* @return
*/
public ArrayList<String> getChildrenList(String obj, String text, boolean addChildren, boolean firstLine) {
Node[] c = getChildren(obj,text);
this.suggestions.clear();
if (c != null) {
for (int idx=0; idx < c.length; idx++) {
if (addChildren) {
this.addChildToList(c[idx], c[idx].getToken(), this.suggestions);
} else {
this.suggestions.add(c[idx].getToken());
}
}
}
if (text.trim().length() == 0 || this.suggestions.isEmpty()) {
// in the event the list is empty, we also add
// the top level nodes
for (Node t : rootCond.getChildren()) {
if ((!firstLine || t.getToken() != null) && !this.suggestions.contains(t.getToken())) {
if (addChildren) {
this.addChildToList(t, t.getToken(), this.suggestions);
} else {
this.suggestions.add(t.getToken());
}
}
}
}
return this.suggestions;
}
/**
* method will prepend the parent text to the child and generate
* the possible combinations in text format.
* @param n
* @param prefix
* @param list
*/
public void addChildToList(Node n, String prefix, ArrayList<String> list) {
if (n.getChildren().size() > 0) {
for (Node child : n.getChildren()) {
if (prefix != null && "-".equals(child.getToken())) {
if (!list.contains(prefix)) {
list.add(prefix);
}
return;
}
String text = (prefix == null ? "" : prefix + " ") + child.getToken();
// list.add(text);
addChildToList(child,text,list);
}
} else {
if (!list.contains(prefix)) {
list.add(prefix);
}
}
}
public Node getCurrent() {
return current;
}
public void setCurrent(Node current) {
this.current = current;
}
public Node getLast() {
return last;
}
public void setLast(Node last) {
this.last = last;
}
/**
* The method will print the DSLTree to System.out in text format.
*/
public void printTree() {
System.out.println("ROOT");
for (Node n : rootCond.getChildren()) {
printNode(n);
}
}
/**
* method will print the node and then iterate over the children
* @param n
*/
protected void printNode(Node n) {
printTabs(n.getDepth());
System.out.println("- \"" + n.getToken() + "\"");
for (Node c : n.getChildren()) {
printNode(c);
}
}
/**
* Method will print n number of tabs
* @param count
*/
protected void printTabs(int count) {
for (int idx=0; idx < count; idx++) {
System.out.print(tab);
}
}
}