0001: package org.drools.eclipse.editors.completion;
0002:
0003: import java.io.IOException;
0004: import java.util.ArrayList;
0005: import java.util.Collection;
0006: import java.util.HashMap;
0007: import java.util.HashSet;
0008: import java.util.Iterator;
0009: import java.util.List;
0010: import java.util.Map;
0011: import java.util.Set;
0012:
0013: import org.drools.base.ClassTypeResolver;
0014: import org.drools.compiler.PackageBuilderConfiguration;
0015: import org.drools.eclipse.DRLInfo;
0016: import org.drools.eclipse.DroolsEclipsePlugin;
0017: import org.drools.eclipse.DroolsPluginImages;
0018: import org.drools.eclipse.DRLInfo.RuleInfo;
0019: import org.drools.eclipse.editors.AbstractRuleEditor;
0020: import org.drools.eclipse.editors.DRLRuleEditor;
0021: import org.drools.eclipse.util.ProjectClassLoader;
0022: import org.drools.lang.Location;
0023: import org.drools.lang.descr.AndDescr;
0024: import org.drools.lang.descr.BaseDescr;
0025: import org.drools.lang.descr.ExistsDescr;
0026: import org.drools.lang.descr.FactTemplateDescr;
0027: import org.drools.lang.descr.FieldBindingDescr;
0028: import org.drools.lang.descr.FieldTemplateDescr;
0029: import org.drools.lang.descr.GlobalDescr;
0030: import org.drools.lang.descr.NotDescr;
0031: import org.drools.lang.descr.OrDescr;
0032: import org.drools.lang.descr.PatternDescr;
0033: import org.drools.rule.builder.dialect.mvel.MVELConsequenceBuilder;
0034: import org.drools.rule.builder.dialect.mvel.MVELDialect;
0035: import org.drools.spi.KnowledgeHelper;
0036: import org.drools.util.asm.ClassFieldInspector;
0037: import org.eclipse.jface.text.IDocument;
0038: import org.eclipse.jface.text.ITextViewer;
0039: import org.eclipse.jface.text.contentassist.ICompletionProposal;
0040: import org.eclipse.swt.graphics.Image;
0041: import org.mvel.CompiledExpression;
0042: import org.mvel.ExpressionCompiler;
0043: import org.mvel.ParserContext;
0044: import org.mvel.PropertyVerifier;
0045:
0046: /**
0047: * For handling within rules.
0048: *
0049: * @author Michael Neale, Kris Verlanen
0050: */
0051: public class RuleCompletionProcessor extends DefaultCompletionProcessor {
0052:
0053: private static final String DIALECT = "dialect";
0054:
0055: private static final Image DROOLS_ICON = DroolsPluginImages
0056: .getImage(DroolsPluginImages.DROOLS);
0057:
0058: private static final Image CLASS_ICON = DroolsPluginImages
0059: .getImage(DroolsPluginImages.CLASS);
0060:
0061: /**
0062: * A CompletionContext contains the DRL backtext parsing results, to avoid
0063: * multilpe parser invocations
0064: */
0065: private CompletionContext context;
0066:
0067: public RuleCompletionProcessor(AbstractRuleEditor editor) {
0068: super (editor);
0069: }
0070:
0071: protected List getCompletionProposals(ITextViewer viewer,
0072: int documentOffset) {
0073: try {
0074: final List list = new ArrayList();
0075: IDocument doc = viewer.getDocument();
0076:
0077: String backText = readBackwards(documentOffset, doc);
0078: final String prefix = CompletionUtil
0079: .stripLastWord(backText);
0080:
0081: // if inside the keyword "rule ", no code completion
0082: if (backText.length() < 5) {
0083: return list;
0084: }
0085:
0086: this .context = new CompletionContext(backText);
0087: Location location = context.getLocation();
0088:
0089: if (location.getType() == Location.LOCATION_RULE_HEADER) {
0090: addRuleHeaderProposals(list, documentOffset, prefix,
0091: backText);
0092: } else if (location.getType() == Location.LOCATION_RHS) {
0093: addRHSCompletionProposals(
0094: list,
0095: documentOffset,
0096: prefix,
0097: backText,
0098: (String) location
0099: .getProperty(Location.LOCATION_LHS_CONTENT),
0100: (String) location
0101: .getProperty(Location.LOCATION_RHS_CONTENT));
0102: } else {
0103: addLHSCompletionProposals(list, documentOffset,
0104: location, prefix, backText);
0105: }
0106:
0107: filterProposalsOnPrefix(prefix, list);
0108: return list;
0109: } catch (Throwable t) {
0110: DroolsEclipsePlugin.log(t);
0111: }
0112: return null;
0113: }
0114:
0115: protected void addRHSCompletionProposals(List list,
0116: int documentOffset, String prefix, String backText,
0117: String conditions, String consequence) {
0118: // only add functions and keywords if at the beginning of a
0119: // new statement
0120: if (consequence == null
0121: || consequence.length() < prefix.length()) {
0122: // possible if doing code completion directly after "then"
0123: return;
0124: }
0125: String consequenceWithoutPrefix = consequence.substring(0,
0126: consequence.length() - prefix.length());
0127:
0128: if (context == null) {
0129: context = new CompletionContext(backText);
0130: }
0131:
0132: boolean startOfDialectExpression = isStartOfDialectExpression(consequenceWithoutPrefix);
0133: if (startOfDialectExpression) {
0134: addRHSKeywordCompletionProposals(list, documentOffset,
0135: prefix);
0136: addRHSFunctionCompletionProposals(list, documentOffset,
0137: prefix);
0138: }
0139:
0140: //if we have 1st a dialect defined locally, or 2nd a global dialect
0141: //the locally defined dialect will override the package default
0142: if (isJavaDialect()) {
0143: addRHSJavaCompletionProposals(list, documentOffset, prefix,
0144: backText, consequence);
0145: } else if (isMvelDialect()) {
0146: addRHSMvelCompletionProposals(list, documentOffset, prefix,
0147: backText, consequence, startOfDialectExpression);
0148: }
0149: }
0150:
0151: private boolean isJavaDialect() {
0152: // java is the default dialect, so no package dialect means java
0153: // conditions are ordered from the more specific to the more general
0154: if (context.isJavaDialect()) {
0155: return true;
0156: } else if (context.isDefaultDialect()
0157: && (!(getAttributes().containsKey(DIALECT)) || hasPackageDialect("java"))) {
0158: return true;
0159: }
0160:
0161: return false;
0162: }
0163:
0164: private boolean isMvelDialect() {
0165: if (context.isMvelDialect()) {
0166: return true;
0167: } else if (context.isDefaultDialect()
0168: && hasPackageDialect("mvel")) {
0169: return true;
0170: }
0171: return false;
0172: }
0173:
0174: private boolean hasPackageDialect(String dialect) {
0175: String globalDialect = (String) getAttributes().get(DIALECT);
0176: if (globalDialect != null
0177: && dialect.equalsIgnoreCase(globalDialect)) {
0178: return true;
0179: }
0180: return false;
0181: }
0182:
0183: protected void addLHSCompletionProposals(List list,
0184: int documentOffset, Location location, String prefix,
0185: String backText) {
0186: switch (location.getType()) {
0187: case Location.LOCATION_LHS_BEGIN_OF_CONDITION:
0188: // if we are at the beginning of a new condition
0189: // add drools keywords
0190: list.add(new RuleCompletionProposal(documentOffset
0191: - prefix.length(), prefix.length(), "and", "and ",
0192: DROOLS_ICON));
0193: list.add(new RuleCompletionProposal(documentOffset
0194: - prefix.length(), prefix.length(), "or", "or ",
0195: DROOLS_ICON));
0196: list.add(new RuleCompletionProposal(documentOffset
0197: - prefix.length(), prefix.length(), "from",
0198: "from ", DROOLS_ICON));
0199: list.add(new RuleCompletionProposal(documentOffset
0200: - prefix.length(), prefix.length(), "forall",
0201: "forall( )", 8, DROOLS_ICON));
0202: RuleCompletionProposal prop = new RuleCompletionProposal(
0203: documentOffset - prefix.length(), prefix.length(),
0204: "eval", "eval( )", 6);
0205: prop.setImage(DROOLS_ICON);
0206: list.add(prop);
0207: prop = new RuleCompletionProposal(documentOffset
0208: - prefix.length(), prefix.length(), "then", "then"
0209: + System.getProperty("line.separator") + "\t");
0210: prop.setImage(DROOLS_ICON);
0211: list.add(prop);
0212: // we do not break but also add all elements that are needed for
0213: // and/or
0214: case Location.LOCATION_LHS_BEGIN_OF_CONDITION_AND_OR:
0215: list.add(new RuleCompletionProposal(documentOffset
0216: - prefix.length(), prefix.length(), "not", "not ",
0217: DROOLS_ICON));
0218: // we do not break but also add all elements that are needed for
0219: // not
0220: case Location.LOCATION_LHS_BEGIN_OF_CONDITION_NOT:
0221: list.add(new RuleCompletionProposal(documentOffset
0222: - prefix.length(), prefix.length(), "exists",
0223: "exists ", DROOLS_ICON));
0224: // we do not break but also add all elements that are needed for
0225: // exists
0226: case Location.LOCATION_LHS_FROM_ACCUMULATE:
0227: case Location.LOCATION_LHS_FROM_COLLECT:
0228: case Location.LOCATION_LHS_BEGIN_OF_CONDITION_EXISTS:
0229: // and add imported classes
0230: Iterator iterator = getImports().iterator();
0231: while (iterator.hasNext()) {
0232: String name = (String) iterator.next();
0233: int index = name.lastIndexOf(".");
0234: if (index != -1) {
0235: String className = name.substring(index + 1);
0236: RuleCompletionProposal p = new RuleCompletionProposal(
0237: documentOffset - prefix.length(), prefix
0238: .length(), className, className
0239: + "( )", className.length() + 2);
0240: p.setPriority(-1);
0241: p.setImage(CLASS_ICON);
0242: list.add(p);
0243: }
0244: }
0245: iterator = getClassesInPackage().iterator();
0246: while (iterator.hasNext()) {
0247: String name = (String) iterator.next();
0248: int index = name.lastIndexOf(".");
0249: if (index != -1) {
0250: String className = name.substring(index + 1);
0251: RuleCompletionProposal p = new RuleCompletionProposal(
0252: documentOffset - prefix.length(), prefix
0253: .length(), className, className
0254: + "( )", className.length() + 2);
0255: p.setPriority(-1);
0256: p.setImage(CLASS_ICON);
0257: list.add(p);
0258: }
0259: }
0260: iterator = getTemplates().iterator();
0261: while (iterator.hasNext()) {
0262: String name = (String) iterator.next();
0263: RuleCompletionProposal p = new RuleCompletionProposal(
0264: documentOffset - prefix.length(), prefix
0265: .length(), name, name + "( )", name
0266: .length() + 2);
0267: p.setPriority(-1);
0268: p.setImage(CLASS_ICON);
0269: list.add(p);
0270: }
0271: break;
0272: case Location.LOCATION_LHS_INSIDE_CONDITION_START:
0273: String className = (String) location
0274: .getProperty(Location.LOCATION_PROPERTY_CLASS_NAME);
0275: String propertyName = (String) location
0276: .getProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME);
0277: if (className != null) {
0278: boolean isTemplate = addFactTemplatePropertyProposals(
0279: documentOffset, prefix, className, list);
0280: if (!isTemplate) {
0281: ClassTypeResolver resolver = new ClassTypeResolver(
0282: getUniqueImports(), ProjectClassLoader
0283: .getProjectClassLoader(getEditor()));
0284: try {
0285: String currentClass = className;
0286: if (propertyName != null) {
0287: String[] nestedProperties = propertyName
0288: .split("\\.");
0289: int nbSuperProperties = nestedProperties.length - 1;
0290: if (propertyName.endsWith(".")) {
0291: nbSuperProperties++;
0292: }
0293: for (int i = 0; i < nbSuperProperties; i++) {
0294: String simplePropertyName = nestedProperties[i];
0295: currentClass = getSimplePropertyClass(
0296: currentClass,
0297: simplePropertyName);
0298: currentClass = convertToNonPrimitiveClass(currentClass);
0299: }
0300: }
0301: RuleCompletionProposal p = new RuleCompletionProposal(
0302: documentOffset - prefix.length(),
0303: prefix.length(), "this");
0304: p.setImage(METHOD_ICON);
0305: list.add(p);
0306: Class clazz = resolver
0307: .resolveType(currentClass);
0308: if (clazz != null) {
0309: if (Map.class.isAssignableFrom(clazz)) {
0310: p = new RuleCompletionProposal(
0311: documentOffset
0312: - prefix.length(),
0313: prefix.length(), "this['']",
0314: "this['']", 6);
0315: p.setImage(METHOD_ICON);
0316: list.add(p);
0317: }
0318: ClassFieldInspector inspector = new ClassFieldInspector(
0319: clazz);
0320: Map types = inspector.getFieldTypes();
0321: Iterator iterator2 = inspector
0322: .getFieldNames().keySet()
0323: .iterator();
0324: while (iterator2.hasNext()) {
0325: String name = (String) iterator2.next();
0326: p = new RuleCompletionProposal(
0327: documentOffset
0328: - prefix.length(),
0329: prefix.length(), name, name
0330: + " ");
0331: p.setImage(METHOD_ICON);
0332: list.add(p);
0333: Class type = (Class) types.get(name);
0334: if (type != null
0335: && Map.class
0336: .isAssignableFrom(type)) {
0337: name += "['']";
0338: p = new RuleCompletionProposal(
0339: documentOffset
0340: - prefix.length(),
0341: prefix.length(), name,
0342: name, name.length() - 2);
0343: p.setImage(METHOD_ICON);
0344: list.add(p);
0345: }
0346: }
0347: }
0348: } catch (IOException exc) {
0349: // Do nothing
0350: } catch (ClassNotFoundException exc) {
0351: // Do nothing
0352: }
0353: }
0354: }
0355: break;
0356: case Location.LOCATION_LHS_INSIDE_CONDITION_OPERATOR:
0357: className = (String) location
0358: .getProperty(Location.LOCATION_PROPERTY_CLASS_NAME);
0359: String property = (String) location
0360: .getProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME);
0361: String type = getPropertyClass(className, property);
0362:
0363: list.add(new RuleCompletionProposal(documentOffset
0364: - prefix.length(), prefix.length(), "==", "== ",
0365: DROOLS_ICON));
0366: list.add(new RuleCompletionProposal(documentOffset
0367: - prefix.length(), prefix.length(), "!=", "!= ",
0368: DROOLS_ICON));
0369: list.add(new RuleCompletionProposal(documentOffset
0370: - prefix.length(), prefix.length(), ":", ": ",
0371: DROOLS_ICON));
0372: list.add(new RuleCompletionProposal(documentOffset
0373: - prefix.length(), prefix.length(), "->",
0374: "-> ( )", 5, DROOLS_ICON));
0375: list.add(new RuleCompletionProposal(documentOffset
0376: - prefix.length(), prefix.length(), "memberOf",
0377: "memberOf ", DROOLS_ICON));
0378: list.add(new RuleCompletionProposal(documentOffset
0379: - prefix.length(), prefix.length(), "not memberOf",
0380: "not memberOf ", DROOLS_ICON));
0381: list.add(new RuleCompletionProposal(documentOffset
0382: - prefix.length(), prefix.length(), "in",
0383: "in ( )", 5, DROOLS_ICON));
0384: list.add(new RuleCompletionProposal(documentOffset
0385: - prefix.length(), prefix.length(), "not in",
0386: "not in ( )", 9, DROOLS_ICON));
0387:
0388: if (isComparable(type)) {
0389: list.add(new RuleCompletionProposal(documentOffset
0390: - prefix.length(), prefix.length(), "<", "< ",
0391: DROOLS_ICON));
0392: list.add(new RuleCompletionProposal(documentOffset
0393: - prefix.length(), prefix.length(), "<=",
0394: "<= ", DROOLS_ICON));
0395: list.add(new RuleCompletionProposal(documentOffset
0396: - prefix.length(), prefix.length(), ">", "> ",
0397: DROOLS_ICON));
0398: list.add(new RuleCompletionProposal(documentOffset
0399: - prefix.length(), prefix.length(), ">=",
0400: ">= ", DROOLS_ICON));
0401: }
0402: if (type.equals("java.lang.String")) {
0403: list.add(new RuleCompletionProposal(documentOffset
0404: - prefix.length(), prefix.length(), "matches",
0405: "matches \"\"", 9, DROOLS_ICON));
0406: list.add(new RuleCompletionProposal(documentOffset
0407: - prefix.length(), prefix.length(),
0408: "not matches", "not matches \"\"", 13,
0409: DROOLS_ICON));
0410: }
0411: if (isSubtypeOf(type, "java.util.Collection")) {
0412: list.add(new RuleCompletionProposal(documentOffset
0413: - prefix.length(), prefix.length(), "contains",
0414: "contains ", DROOLS_ICON));
0415: list.add(new RuleCompletionProposal(documentOffset
0416: - prefix.length(), prefix.length(),
0417: "not contains", "not contains ", DROOLS_ICON));
0418: }
0419: break;
0420: case Location.LOCATION_LHS_INSIDE_CONDITION_ARGUMENT:
0421: // determine type
0422: className = (String) location
0423: .getProperty(Location.LOCATION_PROPERTY_CLASS_NAME);
0424: property = (String) location
0425: .getProperty(Location.LOCATION_PROPERTY_PROPERTY_NAME);
0426: String operator = (String) location
0427: .getProperty(Location.LOCATION_PROPERTY_OPERATOR);
0428: type = getPropertyClass(className, property);
0429:
0430: if ("in".equals(operator)) {
0431: list.add(new RuleCompletionProposal(documentOffset
0432: - prefix.length(), prefix.length(), "()",
0433: "( )", 2, DROOLS_ICON));
0434: break;
0435: }
0436:
0437: if ("contains".equals(operator)
0438: || "excludes".equals(operator)) {
0439: type = "java.lang.Object";
0440: }
0441:
0442: if ("memberOf".equals(operator)) {
0443: type = "java.util.Collection";
0444: }
0445:
0446: boolean isObject = false;
0447: if ("java.lang.Object".equals(type)) {
0448: isObject = true;
0449: }
0450:
0451: list.add(new RuleCompletionProposal(documentOffset
0452: - prefix.length(), prefix.length(), "null",
0453: "null ", DROOLS_ICON));
0454: if ("boolean".equals(type)) {
0455: list.add(new RuleCompletionProposal(documentOffset
0456: - prefix.length(), prefix.length(), "true",
0457: "true ", DROOLS_ICON));
0458: list.add(new RuleCompletionProposal(documentOffset
0459: - prefix.length(), prefix.length(), "false",
0460: "false ", DROOLS_ICON));
0461: }
0462: if (isObject || "java.lang.String".equals(type)) {
0463: list.add(new RuleCompletionProposal(documentOffset
0464: - prefix.length(), prefix.length(), "\"\"",
0465: "\"\"", 1, DROOLS_ICON));
0466: }
0467: if (isObject || "java.util.Date".equals(type)) {
0468: list.add(new RuleCompletionProposal(documentOffset
0469: - prefix.length(), prefix.length(),
0470: "\"dd-mmm-yyyy\"", "\"dd-mmm-yyyy\"", 1,
0471: DROOLS_ICON));
0472: }
0473: list.add(new RuleCompletionProposal(documentOffset
0474: - prefix.length(), prefix.length(), "()", "( )",
0475: 2, DROOLS_ICON));
0476: // add parameters with possibly matching type
0477: if (context.getRule() != null) {
0478: Map result = new HashMap();
0479: addRuleParameters(result, context.getRule().getLhs()
0480: .getDescrs());
0481: Iterator iterator2 = result.entrySet().iterator();
0482: while (iterator2.hasNext()) {
0483: Map.Entry entry = (Map.Entry) iterator2.next();
0484: String paramName = (String) entry.getKey();
0485: String paramType = (String) entry.getValue();
0486: if (isSubtypeOf(paramType, type)) {
0487: RuleCompletionProposal proposal = new RuleCompletionProposal(
0488: documentOffset - prefix.length(),
0489: prefix.length(), paramName);
0490: proposal.setPriority(-1);
0491: proposal.setImage(VARIABLE_ICON);
0492: list.add(proposal);
0493: }
0494: }
0495: }
0496: // add globals with possibly matching type
0497: List globals = getGlobals();
0498: if (globals != null) {
0499: for (iterator = globals.iterator(); iterator.hasNext();) {
0500: GlobalDescr global = (GlobalDescr) iterator.next();
0501: if (isSubtypeOf(global.getType(), type)) {
0502: RuleCompletionProposal proposal = new RuleCompletionProposal(
0503: documentOffset - prefix.length(),
0504: prefix.length(), global.getIdentifier());
0505: proposal.setPriority(-1);
0506: proposal.setImage(VARIABLE_ICON);
0507: list.add(proposal);
0508: }
0509: }
0510: }
0511: break;
0512: case Location.LOCATION_LHS_INSIDE_EVAL:
0513: String content = (String) location
0514: .getProperty(Location.LOCATION_EVAL_CONTENT);
0515: list.addAll(getJavaCompletionProposals(documentOffset,
0516: content, prefix, getRuleParameters(backText)));
0517: break;
0518: case Location.LOCATION_LHS_INSIDE_CONDITION_END:
0519: list.add(new RuleCompletionProposal(documentOffset
0520: - prefix.length(), prefix.length(), "&&", "&& ",
0521: DROOLS_ICON));
0522: list.add(new RuleCompletionProposal(documentOffset
0523: - prefix.length(), prefix.length(), "||", "|| ",
0524: DROOLS_ICON));
0525: list.add(new RuleCompletionProposal(documentOffset
0526: - prefix.length(), prefix.length(), ",", ", ",
0527: DROOLS_ICON));
0528: break;
0529: case Location.LOCATION_LHS_FROM:
0530: String fromText = (String) location
0531: .getProperty(Location.LOCATION_FROM_CONTENT);
0532: int index = fromText.indexOf('.');
0533: if (index == -1) {
0534: // add accumulate and collect keyword
0535: list
0536: .add(new RuleCompletionProposal(
0537: documentOffset - prefix.length(),
0538: prefix.length(),
0539: "accumulate",
0540: "accumulate ( , init ( ), action ( ), result ( ) )",
0541: 13, DROOLS_ICON));
0542: PackageBuilderConfiguration config = new PackageBuilderConfiguration(
0543: ProjectClassLoader
0544: .getProjectClassLoader(getEditor()),
0545: null);
0546: Map accumulateFunctions = config
0547: .getAccumulateFunctionsMap();
0548: for (Iterator iterator2 = accumulateFunctions.keySet()
0549: .iterator(); iterator2.hasNext();) {
0550: String accumulateFunction = (String) iterator2
0551: .next();
0552: list.add(new RuleCompletionProposal(documentOffset
0553: - prefix.length(), prefix.length(),
0554: "accumulate " + accumulateFunction,
0555: "accumulate ( , " + accumulateFunction
0556: + "( ) )", 13, DROOLS_ICON));
0557: }
0558: list.add(new RuleCompletionProposal(documentOffset
0559: - prefix.length(), prefix.length(), "collect",
0560: "collect ( )", 10, DROOLS_ICON));
0561: // add all functions
0562: if ("".equals(fromText)) {
0563: List functions = getFunctions();
0564: iterator = functions.iterator();
0565: while (iterator.hasNext()) {
0566: String name = (String) iterator.next() + "()";
0567: prop = new RuleCompletionProposal(
0568: documentOffset - prefix.length(),
0569: prefix.length(), name, name, name
0570: .length() - 1);
0571: prop.setPriority(-1);
0572: prop.setImage(METHOD_ICON);
0573: list.add(prop);
0574: }
0575: }
0576: list.addAll(getJavaCompletionProposals(documentOffset,
0577: fromText, prefix, getRuleParameters(backText)));
0578: }
0579: break;
0580: case Location.LOCATION_LHS_FROM_ACCUMULATE_INIT_INSIDE:
0581: content = (String) location
0582: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT);
0583: list.addAll(getJavaCompletionProposals(documentOffset,
0584: content, prefix, getRuleParameters(backText)));
0585: break;
0586: case Location.LOCATION_LHS_FROM_ACCUMULATE_ACTION_INSIDE:
0587: content = (String) location
0588: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT);
0589: content += (String) location
0590: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT);
0591: list.addAll(getJavaCompletionProposals(documentOffset,
0592: content, prefix, getRuleParameters(backText)));
0593: break;
0594: case Location.LOCATION_LHS_FROM_ACCUMULATE_RESULT_INSIDE:
0595: content = (String) location
0596: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_INIT_CONTENT);
0597: content += (String) location
0598: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_ACTION_CONTENT);
0599: content += (String) location
0600: .getProperty(Location.LOCATION_PROPERTY_FROM_ACCUMULATE_RESULT_CONTENT);
0601: list.addAll(getJavaCompletionProposals(documentOffset,
0602: content, prefix, getRuleParameters(backText)));
0603: break;
0604: }
0605: }
0606:
0607: private String getPropertyClass(String className,
0608: String propertyName) {
0609: if (className != null && propertyName != null) {
0610: FactTemplateDescr template = getTemplate(className);
0611: if (template != null) {
0612: Iterator iterator = template.getFields().iterator();
0613: while (iterator.hasNext()) {
0614: FieldTemplateDescr field = (FieldTemplateDescr) iterator
0615: .next();
0616: if (propertyName.equals(field.getName())) {
0617: String type = field.getClassType();
0618: if (isPrimitiveType(type)) {
0619: return type;
0620: }
0621: ClassTypeResolver resolver = new ClassTypeResolver(
0622: getUniqueImports(),
0623: ProjectClassLoader
0624: .getProjectClassLoader(getEditor()));
0625: try {
0626: Class clazz = resolver.resolveType(type);
0627: if (clazz != null) {
0628: return clazz.getName();
0629: }
0630: } catch (ClassNotFoundException exc) {
0631: DroolsEclipsePlugin.log(exc);
0632: }
0633: }
0634: }
0635: // if not found, return null
0636: } else {
0637: String[] nestedProperties = propertyName.split("\\.");
0638: String currentClass = className;
0639: for (int i = 0; i < nestedProperties.length; i++) {
0640: String simplePropertyName = nestedProperties[i];
0641: currentClass = getSimplePropertyClass(currentClass,
0642: simplePropertyName);
0643: }
0644: return currentClass;
0645: }
0646: }
0647: return null;
0648: }
0649:
0650: private String getSimplePropertyClass(String className,
0651: String propertyName) {
0652: if ("this".equals(propertyName)) {
0653: return className;
0654: }
0655: if (propertyName.endsWith("]")) {
0656: // TODO can we take advantage of generics here?
0657: return "java.lang.Object";
0658: }
0659: ClassTypeResolver resolver = new ClassTypeResolver(
0660: getUniqueImports(), ProjectClassLoader
0661: .getProjectClassLoader(getEditor()));
0662: try {
0663: Class clazz = resolver.resolveType(className);
0664: if (clazz != null) {
0665: Class clazzz = (Class) new ClassFieldInspector(clazz)
0666: .getFieldTypes().get(propertyName);
0667: if (clazzz != null) {
0668: return clazzz.getName();
0669: }
0670: }
0671: } catch (IOException exc) {
0672: // Do nothing
0673: } catch (ClassNotFoundException exc) {
0674: // Do nothing
0675: }
0676: return "java.lang.Object";
0677: }
0678:
0679: private Map getRuleParameters(String backText) {
0680: Map result = new HashMap();
0681: // add globals
0682: List globals = getGlobals();
0683: if (globals != null) {
0684: for (Iterator iterator = globals.iterator(); iterator
0685: .hasNext();) {
0686: GlobalDescr global = (GlobalDescr) iterator.next();
0687: result.put(global.getIdentifier(), global.getType());
0688: }
0689: }
0690:
0691: if (context == null) {
0692: context = new CompletionContext(backText);
0693: }
0694: if (context.getRule() == null) {
0695: return result;
0696: }
0697: // add parameters defined in conditions
0698: addRuleParameters(result, context.getRule().getLhs()
0699: .getDescrs());
0700: return result;
0701: }
0702:
0703: private boolean isComparable(String type) {
0704: if (type == null) {
0705: return false;
0706: }
0707: if (isPrimitiveNumericType(type)) {
0708: return true;
0709: }
0710: if (isObjectNumericType(type)) {
0711: return true;
0712: }
0713: if (isSubtypeOf(type, "java.lang.Comparable")) {
0714: return true;
0715: }
0716: return false;
0717: }
0718:
0719: private boolean isPrimitiveType(String type) {
0720: return isPrimitiveNumericType(type) || type.equals("boolean");
0721: }
0722:
0723: private boolean isPrimitiveNumericType(String type) {
0724: return type.equals("byte") || type.equals("short")
0725: || type.equals("int") || type.equals("long")
0726: || type.equals("float") || type.equals("double")
0727: || type.equals("char");
0728: }
0729:
0730: private boolean isObjectNumericType(String type) {
0731: return type.equals("java.lang.Byte")
0732: || type.equals("java.lang.Short")
0733: || type.equals("java.lang.Integer")
0734: || type.equals("java.lang.Long")
0735: || type.equals("java.lang.Float")
0736: || type.equals("java.lang.Double")
0737: || type.equals("java.lang.Char");
0738: }
0739:
0740: /**
0741: * Returns true if the first class is the same or a subtype of the second
0742: * class.
0743: *
0744: * @param class1
0745: * @param class2
0746: * @return
0747: */
0748: private boolean isSubtypeOf(String class1, String class2) {
0749: if (class1 == null || class2 == null) {
0750: return false;
0751: }
0752: class1 = convertToNonPrimitiveClass(class1);
0753: class2 = convertToNonPrimitiveClass(class2);
0754: // TODO add code to take primitive types into account
0755: ClassTypeResolver resolver = new ClassTypeResolver(
0756: getUniqueImports(), ProjectClassLoader
0757: .getProjectClassLoader(getEditor()));
0758: try {
0759: Class clazz1 = resolver.resolveType(class1);
0760: Class clazz2 = resolver.resolveType(class2);
0761: if (clazz1 == null || clazz2 == null) {
0762: return false;
0763: }
0764: return clazz2.isAssignableFrom(clazz1);
0765: } catch (ClassNotFoundException exc) {
0766: return false;
0767: }
0768: }
0769:
0770: private String convertToNonPrimitiveClass(String clazz) {
0771: if (!isPrimitiveType(clazz)) {
0772: return clazz;
0773: }
0774: if ("byte".equals(clazz)) {
0775: return "java.lang.Byte";
0776: } else if ("short".equals(clazz)) {
0777: return "java.lang.Short";
0778: } else if ("int".equals(clazz)) {
0779: return "java.lang.Integer";
0780: } else if ("long".equals(clazz)) {
0781: return "java.lang.Long";
0782: } else if ("float".equals(clazz)) {
0783: return "java.lang.Float";
0784: } else if ("double".equals(clazz)) {
0785: return "java.lang.Double";
0786: } else if ("char".equals(clazz)) {
0787: return "java.lang.Char";
0788: } else if ("boolean".equals(clazz)) {
0789: return "java.lang.Boolean";
0790: }
0791: // should never occur
0792: return null;
0793: }
0794:
0795: private void addRHSFunctionCompletionProposals(List list,
0796: int documentOffset, String prefix) {
0797: Iterator iterator;
0798: RuleCompletionProposal prop;
0799: List functions = getFunctions();
0800: iterator = functions.iterator();
0801: while (iterator.hasNext()) {
0802: String name = (String) iterator.next() + "()";
0803: prop = new RuleCompletionProposal(documentOffset
0804: - prefix.length(), prefix.length(), name, name
0805: + ";", name.length() - 1);
0806: prop.setPriority(-1);
0807: prop.setImage(METHOD_ICON);
0808: list.add(prop);
0809: }
0810: }
0811:
0812: private void addRHSKeywordCompletionProposals(List list,
0813: int documentOffset, String prefix) {
0814: RuleCompletionProposal prop = new RuleCompletionProposal(
0815: documentOffset - prefix.length(), prefix.length(),
0816: "update", "update();", 7);
0817: prop.setImage(DROOLS_ICON);
0818: list.add(prop);
0819: prop = new RuleCompletionProposal(documentOffset
0820: - prefix.length(), prefix.length(), "retract",
0821: "retract();", 8);
0822: prop.setImage(DROOLS_ICON);
0823: list.add(prop);
0824: prop = new RuleCompletionProposal(documentOffset
0825: - prefix.length(), prefix.length(), "insert",
0826: "insert();", 7);
0827: prop.setImage(DROOLS_ICON);
0828: list.add(prop);
0829: prop = new RuleCompletionProposal(documentOffset
0830: - prefix.length(), prefix.length(), "insertLogical",
0831: "insertLogical();", 14);
0832: prop.setImage(DROOLS_ICON);
0833: list.add(prop);
0834: }
0835:
0836: private void addRHSJavaCompletionProposals(List list,
0837: int documentOffset, String prefix, String backText,
0838: String consequence) {
0839: list.addAll(getJavaCompletionProposals(documentOffset,
0840: consequence, prefix, getRuleParameters(backText)));
0841: }
0842:
0843: private void addRHSMvelCompletionProposals(List list,
0844: final int documentOffset, String prefix, String backText,
0845: String consequence, boolean expressionStart) {
0846:
0847: Collection mvelCompletionProposals = getMvelCompletionProposals(
0848: consequence, documentOffset, prefix,
0849: getRuleParameters(backText), backText, expressionStart);
0850: list.addAll(mvelCompletionProposals);
0851: }
0852:
0853: private Collection getMvelCompletionProposals(
0854: final String consequence, final int documentOffset,
0855: final String prefix, Map params, String backText,
0856: boolean startOfExpression) {
0857:
0858: final Set proposals = new HashSet();
0859:
0860: if (!(getEditor() instanceof DRLRuleEditor)) {
0861: return proposals;
0862: }
0863:
0864: String mvelTextWithoutPrefix = getTextWithoutPrefix(
0865: consequence, prefix);
0866:
0867: String compilableConsequence = CompletionUtil
0868: .getCompilableText(mvelTextWithoutPrefix);
0869: MVELConsequenceBuilder builder = new MVELConsequenceBuilder();
0870: compilableConsequence = builder
0871: .processMacros(compilableConsequence);
0872:
0873: // attempt to compile and analyze
0874: try {
0875: DRLInfo drlInfo = DroolsEclipsePlugin.getDefault()
0876: .parseResource((DRLRuleEditor) getEditor(), true,
0877: true);
0878:
0879: ParserContext compilationContext = createMvelAnalysisContext(
0880: params, drlInfo, compilableConsequence);
0881:
0882: if (startOfExpression) {
0883: Collection jdtProps = getJavaCompletionProposals(
0884: documentOffset, prefix, prefix, params);
0885: proposals.addAll(jdtProps);
0886:
0887: addMvelCompletions(proposals, documentOffset, prefix,
0888: compilationContext.getVariables());
0889:
0890: addMvelCompletions(proposals, documentOffset, prefix,
0891: compilationContext.getInputs());
0892:
0893: } else {
0894: // we are completing the methods for an existing type or
0895: // variable, we need find the last type in the expression to complete against
0896:
0897: if (!"".equals(compilableConsequence.trim())) {
0898: Class lastType = context.getMvelReturnedType();
0899: if (lastType == null) {
0900: lastType = Object.class;
0901: }
0902:
0903: //FIXME: there is a small chance of var name collision using this arbitrary mvdrlofc as a variable name.
0904: //ideally the varibale name should be inferred from the last memeber of the expression
0905: String syntheticVarName = "mvdrlofc";
0906: String javaText = "\n"
0907: + lastType.getName().replace('$', '.')
0908: + " " + syntheticVarName + ";\n"
0909: + syntheticVarName + ".";
0910: Collection jdtProps = getJavaMvelCompletionProposals(
0911: documentOffset, javaText, prefix, params);
0912: proposals.addAll(jdtProps);
0913: }
0914: }
0915:
0916: } catch (Throwable e) {
0917: DroolsEclipsePlugin.log(e);
0918: }
0919:
0920: return proposals;
0921: }
0922:
0923: private Map getResolvedMvelInputs(Map params) {
0924: ClassTypeResolver resolver = new ClassTypeResolver(
0925: getUniqueImports(), ProjectClassLoader
0926: .getProjectClassLoader(getEditor()));
0927:
0928: Map resolved = new HashMap();
0929: for (Iterator iter = params.entrySet().iterator(); iter
0930: .hasNext();) {
0931: Map.Entry entry = (Map.Entry) iter.next();
0932: String inputType = (String) entry.getValue();
0933: try {
0934: Class type = resolver.resolveType(inputType);
0935: resolved.put(entry.getKey(), type);
0936: } catch (ClassNotFoundException e) {
0937: DroolsEclipsePlugin.log(e);
0938: }
0939: }
0940: return resolved;
0941: }
0942:
0943: private ParserContext createMvelAnalysisContext(Map params,
0944: DRLInfo drlInfo, String compilableConsequence) {
0945: String currentRulename = context.getRule().getName();
0946: RuleInfo[] ruleInfos = drlInfo.getRuleInfos();
0947: RuleInfo currentRule = null;
0948: for (int i = 0; i < ruleInfos.length; i++) {
0949: if (currentRulename.equals(ruleInfos[i].getRuleName())) {
0950: currentRule = ruleInfos[i];
0951: break;
0952: }
0953: }
0954: MVELDialect dialect = (MVELDialect) currentRule.getDialect();
0955:
0956: final ParserContext initialContext = new ParserContext(dialect
0957: .getImports(), null, drlInfo.getPackageName() + "."
0958: + currentRule.getRuleName());
0959:
0960: for (Iterator it = dialect.getPackgeImports().values()
0961: .iterator(); it.hasNext();) {
0962: String packageImport = (String) it.next();
0963: initialContext.addPackageImport(packageImport);
0964: }
0965:
0966: try {
0967:
0968: initialContext.setStrictTypeEnforcement(dialect
0969: .isStrictMode());
0970:
0971: initialContext.setInterceptors(dialect.getInterceptors());
0972: initialContext.setInputs(getResolvedMvelInputs(params));
0973: initialContext.addInput("drools", KnowledgeHelper.class);
0974: initialContext.setCompiled(true);
0975:
0976: //compile the expression
0977: ExpressionCompiler compiler = new ExpressionCompiler(
0978: compilableConsequence);
0979: CompiledExpression expression = compiler
0980: .compile(initialContext);
0981: ParserContext compilationContext = compiler
0982: .getParserContextState();
0983:
0984: Class lastType = expression.getKnownEgressType();
0985: if (lastType == null) {
0986: lastType = Object.class;
0987: }
0988: context.setMvelReturnedType(lastType);
0989:
0990: if ("java.lang.Object".equals(lastType.getName())) {
0991: // attempt to use the property verifier to get
0992: // a better type resolution (a recommend by cbrock, though egress gives consistent results
0993: String lastExpression = CompletionUtil
0994: .getLastLine(compilableConsequence);
0995: lastType = new PropertyVerifier(lastExpression,
0996: compilationContext).analyze();
0997: }
0998:
0999: return compilationContext;
1000: } catch (Exception e) {
1001: return initialContext;
1002: }
1003: }
1004:
1005: private void addMvelCompletions(final Collection proposals,
1006: int documentOffset, String prefix, Map inputs) {
1007: for (Iterator iter = inputs.entrySet().iterator(); iter
1008: .hasNext();) {
1009: Map.Entry entry = (Map.Entry) iter.next();
1010: String prop = (String) entry.getKey();
1011:
1012: //JBRULES-1134 do not add completions if they already exist
1013: if (containsProposal(proposals, prop)) {
1014: continue;
1015: }
1016:
1017: Class type = (Class) entry.getValue();
1018: String display = prop + " - "
1019: + type.getName().replace('$', '.');
1020:
1021: RuleCompletionProposal rcp = new RuleCompletionProposal(
1022: documentOffset - prefix.length(), prefix.length(),
1023: display, prop);
1024: rcp.setImage(DefaultCompletionProcessor.VARIABLE_ICON);
1025: proposals.add(rcp);
1026: }
1027: }
1028:
1029: /**
1030: * Attempt to compare proposals of different types based on the tokenized display string
1031: * @param proposals
1032: * @param newProposal
1033: * @return true if the collection contains a prposal which matches the new Proposal.
1034: * The match is based on the first token based on a space split
1035: */
1036: boolean containsProposal(final Collection proposals,
1037: String newProposal) {
1038: for (Iterator iter = proposals.iterator(); iter.hasNext();) {
1039: ICompletionProposal prop = (ICompletionProposal) iter
1040: .next();
1041: String displayString = prop.getDisplayString();
1042: String[] existings = displayString.split(" ");
1043: if (existings.length == 0) {
1044: continue;
1045: }
1046:
1047: String[] newProposals = newProposal.split(" ");
1048: if (newProposals.length == 0) {
1049: continue;
1050: }
1051:
1052: if (existings[0].equals(newProposals[0])) {
1053: return true;
1054: }
1055: }
1056: return false;
1057: }
1058:
1059: private void addRuleParameters(Map result, List descrs) {
1060: if (descrs == null) {
1061: return;
1062: }
1063: Iterator iterator = descrs.iterator();
1064: while (iterator.hasNext()) {
1065: BaseDescr descr = (BaseDescr) iterator.next();
1066: addRuleParameters(result, descr);
1067: }
1068: }
1069:
1070: private void addRuleParameters(Map result, BaseDescr descr) {
1071: if (descr == null) {
1072: return;
1073: }
1074: if (descr instanceof PatternDescr) {
1075: String name = ((PatternDescr) descr).getIdentifier();
1076: if (name != null) {
1077: result
1078: .put(name, ((PatternDescr) descr)
1079: .getObjectType());
1080: }
1081: addRuleSubParameters(result, ((PatternDescr) descr)
1082: .getDescrs(), ((PatternDescr) descr)
1083: .getObjectType());
1084: } else if (descr instanceof AndDescr) {
1085: addRuleParameters(result, ((AndDescr) descr).getDescrs());
1086: } else if (descr instanceof OrDescr) {
1087: addRuleParameters(result, ((OrDescr) descr).getDescrs());
1088: } else if (descr instanceof ExistsDescr) {
1089: addRuleParameters(result, ((ExistsDescr) descr).getDescrs());
1090: } else if (descr instanceof NotDescr) {
1091: addRuleParameters(result, ((NotDescr) descr).getDescrs());
1092: }
1093: }
1094:
1095: private void addRuleSubParameters(Map result, List descrs,
1096: String clazz) {
1097: if (descrs == null) {
1098: return;
1099: }
1100: Iterator iterator = descrs.iterator();
1101: while (iterator.hasNext()) {
1102: BaseDescr descr = (BaseDescr) iterator.next();
1103: if (descr instanceof FieldBindingDescr) {
1104: FieldBindingDescr fieldDescr = (FieldBindingDescr) descr;
1105: String name = fieldDescr.getIdentifier();
1106: String field = fieldDescr.getFieldName();
1107: String type = getPropertyClass(clazz, field);
1108: if (name != null) {
1109: result.put(name, type);
1110: }
1111: }
1112: }
1113: }
1114:
1115: private void addRuleHeaderProposals(List list, int documentOffset,
1116: String prefix, String backText) {
1117: list.add(new RuleCompletionProposal(documentOffset
1118: - prefix.length(), prefix.length(), "salience",
1119: "salience ", DROOLS_ICON));
1120: list.add(new RuleCompletionProposal(documentOffset
1121: - prefix.length(), prefix.length(), "no-loop",
1122: "no-loop ", DROOLS_ICON));
1123: list.add(new RuleCompletionProposal(documentOffset
1124: - prefix.length(), prefix.length(), "agenda-group",
1125: "agenda-group ", DROOLS_ICON));
1126: list.add(new RuleCompletionProposal(documentOffset
1127: - prefix.length(), prefix.length(), "duration",
1128: "duration ", DROOLS_ICON));
1129: list.add(new RuleCompletionProposal(documentOffset
1130: - prefix.length(), prefix.length(), "auto-focus",
1131: "auto-focus ", DROOLS_ICON));
1132: list.add(new RuleCompletionProposal(documentOffset
1133: - prefix.length(), prefix.length(), "when", "when"
1134: + System.getProperty("line.separator") + "\t ",
1135: DROOLS_ICON));
1136: list.add(new RuleCompletionProposal(documentOffset
1137: - prefix.length(), prefix.length(), "activation-group",
1138: "activation-group ", DROOLS_ICON));
1139: list.add(new RuleCompletionProposal(documentOffset
1140: - prefix.length(), prefix.length(), "date-effective",
1141: "date-effective \"dd-MMM-yyyy\"", 16, DROOLS_ICON));
1142: list.add(new RuleCompletionProposal(documentOffset
1143: - prefix.length(), prefix.length(), "date-expires",
1144: "date-expires \"dd-MMM-yyyy\"", 14, DROOLS_ICON));
1145: list.add(new RuleCompletionProposal(documentOffset
1146: - prefix.length(), prefix.length(), "enabled",
1147: "enabled false", DROOLS_ICON));
1148: list.add(new RuleCompletionProposal(documentOffset
1149: - prefix.length(), prefix.length(), "ruleflow-group",
1150: "ruleflow-group \"\"", 16, DROOLS_ICON));
1151: list.add(new RuleCompletionProposal(documentOffset
1152: - prefix.length(), prefix.length(), "lock-on-active",
1153: "lock-on-active ", DROOLS_ICON));
1154: list.add(new RuleCompletionProposal(documentOffset
1155: - prefix.length(), prefix.length(), "dialect \"java\"",
1156: "dialect \"java\" ", DROOLS_ICON));
1157: list.add(new RuleCompletionProposal(documentOffset
1158: - prefix.length(), prefix.length(), "dialect \"mvel\"",
1159: "dialect \"mvel\" ", DROOLS_ICON));
1160: }
1161:
1162: private boolean addFactTemplatePropertyProposals(
1163: int documentOffset, String prefix, String templateName,
1164: List list) {
1165: FactTemplateDescr descr = getTemplate(templateName);
1166: if (descr == null) {
1167: return false;
1168: }
1169: Iterator iterator = descr.getFields().iterator();
1170: while (iterator.hasNext()) {
1171: FieldTemplateDescr field = (FieldTemplateDescr) iterator
1172: .next();
1173: String fieldName = field.getName();
1174: RuleCompletionProposal p = new RuleCompletionProposal(
1175: documentOffset - prefix.length(), prefix.length(),
1176: fieldName, fieldName + " ");
1177: p.setImage(METHOD_ICON);
1178: list.add(p);
1179: }
1180: return true;
1181: }
1182:
1183: }
|