001: package org.drools.eclipse.editors.completion;
002:
003: import java.util.List;
004: import java.util.regex.Matcher;
005: import java.util.regex.Pattern;
006:
007: import org.drools.compiler.DrlParser;
008: import org.drools.compiler.DroolsParserException;
009: import org.drools.lang.Location;
010: import org.drools.lang.descr.BaseDescr;
011: import org.drools.lang.descr.PackageDescr;
012: import org.drools.lang.descr.RuleDescr;
013:
014: /**
015: * A utility class that invokes the DRLParser on some partial drl text, and provides
016: * information back about the context of that parserd drl,
017: * such as a location type, a dialect, and so on.
018: *
019: */
020: public class CompletionContext {
021:
022: static final Pattern PATTERN_PATTERN_OPERATOR = Pattern
023: .compile(
024: ".*[(,](\\s*(\\S*)\\s*:)?\\s*([^\\s<>!=:\\(\\),]+)(\\s*([<>=!]+)\\s*[^\\s<>!=:]*\\s*(&&|\\|\\|))*\\s+",
025: Pattern.DOTALL);
026:
027: static final Pattern PATTERN_PATTERN_COMPARATOR_ARGUMENT = Pattern
028: .compile(
029: ".*[(,](\\s*(\\S*)\\s*:)?\\s*([^\\s<>!=:\\(\\)]+)\\s*(([<>=!]+)\\s*[^\\s<>!=:]+\\s*(&&|\\|\\|)\\s*)*([<>=!]+)\\s*[^\\s<>!=:]*",
030: Pattern.DOTALL);
031:
032: static final Pattern EVAL_PATTERN = Pattern.compile(
033: ".*\\s+eval\\s*\\(\\s*([(^\\))(\\([^\\)]*\\)?)]*)",
034: Pattern.DOTALL);
035:
036: static final Pattern ACCUMULATE_PATTERN_INIT = Pattern.compile(
037: ".*,?\\s*init\\s*\\(\\s*(.*)", Pattern.DOTALL);
038:
039: static final Pattern ACCUMULATE_PATTERN_ACTION = Pattern
040: .compile(
041: ".*,?\\s*init\\s*\\(\\s*(.*)\\)\\s*,?\\s*action\\s*\\(\\s*(.*)",
042: Pattern.DOTALL);
043:
044: static final Pattern ACCUMULATE_PATTERN_REVERSE = Pattern
045: .compile(
046: ".*,?\\s*init\\s*\\(\\s*(.*)\\)\\s*,?\\s*action\\s*\\(\\s*(.*)\\)\\s*,?\\s*reverse\\s*\\(\\s*(.*)",
047: Pattern.DOTALL);
048:
049: static final Pattern ACCUMULATE_PATTERN_RESULT = Pattern
050: .compile(
051: ".*,?\\s*init\\s*\\(\\s*(.*)\\)\\s*,?\\s*action\\s*\\(\\s*(.*)\\)\\s*,?(\\s*reverse\\s*\\(\\s*(.*)\\)\\s*,?)?\\s*result\\s*\\(\\s*(.*)",
052: Pattern.DOTALL);
053:
054: static final Pattern THEN_PATTERN = Pattern.compile(
055: ".*\n\\s*when\\s*(.*)\n\\s*then\\s*(.*)", Pattern.DOTALL);
056:
057: static final Pattern ENDS_WITH_SPACES = Pattern.compile(".*\\s+",
058: Pattern.DOTALL);
059:
060: static final Pattern ENDS_WITH_COLON = Pattern.compile(".*:\\s*",
061: Pattern.DOTALL);
062:
063: static final Pattern ENDS_WITH_BRACKET = Pattern.compile(
064: ".*\\)\\s*", Pattern.DOTALL);
065:
066: static final Pattern MVEL_DIALECT_PATTERN = Pattern.compile(
067: ".*dialect\\s+\"mvel\".*", Pattern.DOTALL);
068:
069: static final Pattern JAVA_DIALECT_PATTERN = Pattern.compile(
070: ".*dialect\\s+\"java\".*", Pattern.DOTALL);
071:
072: static final String MVEL_DIALECT = "mvel";
073: static final String JAVA_DIALECT = "java";
074:
075: private String backText;
076: private DrlParser parser;
077: private RuleDescr rule;
078: private PackageDescr packageDescr;
079: private String dialect;
080: private Class mvelReturnedType;
081:
082: public CompletionContext(String backText) {
083: this .backText = backText;
084: this .parser = new DrlParser();
085:
086: try {
087: packageDescr = parser.parse(backText);
088: List rules = packageDescr.getRules();
089: if (rules != null && rules.size() == 1) {
090: this .rule = (RuleDescr) rules.get(0);
091: }
092:
093: } catch (DroolsParserException exc) {
094: // do nothing
095: }
096:
097: //FIXME: the whole story of dialect determination for completion needs beefing up
098: determineDialect(backText);
099: }
100:
101: public boolean isJavaDialect() {
102: return JAVA_DIALECT.equalsIgnoreCase(dialect);
103: }
104:
105: public boolean isMvelDialect() {
106: return MVEL_DIALECT.equalsIgnoreCase(dialect);
107: }
108:
109: public boolean isDefaultDialect() {
110: return !isJavaDialect() && !isMvelDialect();
111: }
112:
113: public PackageDescr getPackageDescr() {
114: return packageDescr;
115: }
116:
117: //note: this is a crude but reasonably fast way to determine the dialect,
118: //especially when parsing imcomplete rules
119: private void determineDialect(String backText) {
120: dialect = null;
121: boolean mvel = MVEL_DIALECT_PATTERN.matcher(backText).matches();
122: boolean java = JAVA_DIALECT_PATTERN.matcher(backText).matches();
123: //which dialect may be defined for this rule?
124: if (mvel) {
125: dialect = MVEL_DIALECT;
126: }
127: if (java) {
128: dialect = JAVA_DIALECT;
129: }
130: }
131:
132: public Location getLocation() {
133: if (backText == null || rule == null) {
134: return new Location(Location.LOCATION_UNKNOWN);
135: }
136: return determineLocationForDescr(rule, parser.getLocation(),
137: backText);
138: }
139:
140: public RuleDescr getRule() {
141: return rule;
142: }
143:
144: private static Location determineLocationForDescr(BaseDescr descr,
145: Location location, String backText) {
146: if (location.getType() == Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR) {
147: if (!ENDS_WITH_SPACES.matcher(backText).matches()
148: || ENDS_WITH_COLON.matcher(backText).matches()) {
149: location
150: .setType(Location.LOCATION_LHS_INSIDE_CONDITION_START);
151: }
152: } else if (location.getType() == Location.LOCATION_LHS_INSIDE_CONDITION_END) {
153: if (!backText.endsWith(" ")) {
154: location
155: .setType(Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT);
156: }
157: } else if (location.getType() == Location.LOCATION_LHS_INSIDE_EVAL) {
158: Matcher matcher = EVAL_PATTERN.matcher(backText);
159: if (matcher.matches()) {
160: String content = matcher.group(1);
161: location.setProperty(Location.LOCATION_EVAL_CONTENT,
162: content);
163: }
164: } else if (location.getType() == Location.LOCATION_LHS_INSIDE_CONDITION_START) {
165: Matcher matcher = PATTERN_PATTERN_COMPARATOR_ARGUMENT
166: .matcher(backText);
167: if (matcher.matches()) {
168: location
169: .setType(Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT);
170: location.setProperty(
171: Location.LOCATION_PROPERTY_OPERATOR, matcher
172: .group(7));
173: return location;
174: }
175:
176: matcher = PATTERN_PATTERN_OPERATOR.matcher(backText);
177: if (matcher.matches()) {
178: location
179: .setType(Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR);
180: return location;
181: }
182: } else if (location.getType() == Location.LOCATION_LHS_FROM) {
183: if (location.getProperty(Location.LOCATION_FROM_CONTENT) == null) {
184: location
185: .setProperty(Location.LOCATION_FROM_CONTENT, "");
186: } else if (((String) location
187: .getProperty(Location.LOCATION_FROM_CONTENT))
188: .length() > 0
189: && (ENDS_WITH_SPACES.matcher(backText).matches() || ENDS_WITH_BRACKET
190: .matcher(backText).matches())) {
191: location
192: .setType(Location.LOCATION_LHS_BEGIN_OF_CONDITION);
193: }
194: } else if (location.getType() == Location.LOCATION_LHS_FROM_ACCUMULATE_INIT) {
195: Matcher matcher = ACCUMULATE_PATTERN_INIT.matcher(backText);
196: if (matcher.matches()) {
197: location
198: .setType(Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE);
199: location
200: .setProperty(
201: Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT,
202: matcher.group(1));
203: }
204: } else if (location.getType() == Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION) {
205: Matcher matcher = ACCUMULATE_PATTERN_ACTION
206: .matcher(backText);
207: if (matcher.matches()) {
208: location
209: .setType(Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE);
210: location
211: .setProperty(
212: Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT,
213: matcher.group(2));
214: }
215: } else if (location.getType() == Location.LOCATION_LHS_FROM_ACCUMULATE_REVERSE) {
216: Matcher matcher = ACCUMULATE_PATTERN_REVERSE
217: .matcher(backText);
218: if (matcher.matches()) {
219: location
220: .setType(Location.LOCATION_LHS_FROM_ACCUMULATE_REVERSE_INSIDE);
221: location
222: .setProperty(
223: Location.LOCATION_PROPERTY_FROM_ACCUMULATE_REVERSE_CONTENT,
224: matcher.group(3));
225: }
226: matcher = ACCUMULATE_PATTERN_RESULT.matcher(backText);
227: if (matcher.matches()) {
228: location
229: .setType(Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE);
230: location
231: .setProperty(
232: Location.LOCATION_PROPERTY_FROM_ACCUMULATE_RESULT_CONTENT,
233: matcher.group(5));
234: }
235: } else if (location.getType() == Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT) {
236: Matcher matcher = ACCUMULATE_PATTERN_RESULT
237: .matcher(backText);
238: if (matcher.matches()) {
239: location
240: .setType(Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE);
241: location
242: .setProperty(
243: Location.LOCATION_PROPERTY_FROM_ACCUMULATE_RESULT_CONTENT,
244: matcher.group(5));
245: }
246: } else if (location.getType() == Location.LOCATION_RHS) {
247: Matcher matcher = THEN_PATTERN.matcher(backText);
248: if (matcher.matches()) {
249: location.setProperty(Location.LOCATION_LHS_CONTENT,
250: matcher.group(1));
251: location.setProperty(Location.LOCATION_RHS_CONTENT,
252: matcher.group(2));
253: return location;
254: }
255: }
256:
257: return location;
258: }
259:
260: public Class getMvelReturnedType() {
261: return mvelReturnedType;
262: }
263:
264: public void setMvelReturnedType(Class getMvelReturnedType) {
265: this.mvelReturnedType = getMvelReturnedType;
266: }
267: }
|