001: /*
002: * Copyright 2006 JBoss Inc
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.drools.rule.builder;
018:
019: import java.util.ArrayList;
020: import java.util.Iterator;
021: import java.util.List;
022:
023: import org.drools.RuntimeDroolsException;
024: import org.drools.base.ClassObjectType;
025: import org.drools.base.FieldFactory;
026: import org.drools.base.ValueType;
027: import org.drools.base.evaluators.Operator;
028: import org.drools.compiler.Dialect;
029: import org.drools.compiler.RuleError;
030: import org.drools.facttemplates.FactTemplate;
031: import org.drools.facttemplates.FactTemplateFieldExtractor;
032: import org.drools.facttemplates.FactTemplateObjectType;
033: import org.drools.lang.MVELDumper;
034: import org.drools.lang.descr.AndDescr;
035: import org.drools.lang.descr.BaseDescr;
036: import org.drools.lang.descr.FieldBindingDescr;
037: import org.drools.lang.descr.FieldConstraintDescr;
038: import org.drools.lang.descr.LiteralRestrictionDescr;
039: import org.drools.lang.descr.OrDescr;
040: import org.drools.lang.descr.PatternDescr;
041: import org.drools.lang.descr.PredicateDescr;
042: import org.drools.lang.descr.QualifiedIdentifierRestrictionDescr;
043: import org.drools.lang.descr.RestrictionConnectiveDescr;
044: import org.drools.lang.descr.RestrictionDescr;
045: import org.drools.lang.descr.ReturnValueRestrictionDescr;
046: import org.drools.lang.descr.VariableRestrictionDescr;
047: import org.drools.rule.AbstractCompositeConstraint;
048: import org.drools.rule.AbstractCompositeRestriction;
049: import org.drools.rule.AndCompositeRestriction;
050: import org.drools.rule.AndConstraint;
051: import org.drools.rule.Declaration;
052: import org.drools.rule.LiteralConstraint;
053: import org.drools.rule.LiteralRestriction;
054: import org.drools.rule.MultiRestrictionFieldConstraint;
055: import org.drools.rule.OrCompositeRestriction;
056: import org.drools.rule.OrConstraint;
057: import org.drools.rule.Pattern;
058: import org.drools.rule.PatternSource;
059: import org.drools.rule.PredicateConstraint;
060: import org.drools.rule.ReturnValueConstraint;
061: import org.drools.rule.ReturnValueRestriction;
062: import org.drools.rule.RuleConditionElement;
063: import org.drools.rule.VariableConstraint;
064: import org.drools.rule.VariableRestriction;
065: import org.drools.rule.builder.dialect.mvel.MVELDialect;
066: import org.drools.spi.Constraint;
067: import org.drools.spi.Evaluator;
068: import org.drools.spi.FieldExtractor;
069: import org.drools.spi.FieldValue;
070: import org.drools.spi.ObjectType;
071: import org.drools.spi.Restriction;
072:
073: /**
074: * A builder for patterns
075: *
076: * @author etirelli
077: */
078: public class PatternBuilder implements RuleConditionBuilder {
079:
080: public PatternBuilder() {
081: }
082:
083: public RuleConditionElement build(RuleBuildContext context,
084: BaseDescr descr) {
085: return this .build(context, descr, null);
086: }
087:
088: /**
089: * Build a pattern for the given descriptor in the current
090: * context and using the given utils object
091: *
092: * @param context
093: * @param utils
094: * @param patternDescr
095: * @return
096: */
097: public RuleConditionElement build(RuleBuildContext context,
098: BaseDescr descr, Pattern prefixPattern) {
099:
100: final PatternDescr patternDescr = (PatternDescr) descr;
101:
102: if (patternDescr.getObjectType() == null
103: || patternDescr.getObjectType().equals("")) {
104: context.getErrors().add(
105: new RuleError(context.getRule(), patternDescr,
106: null, "ObjectType not correctly defined"));
107: return null;
108: }
109:
110: ObjectType objectType = null;
111:
112: final FactTemplate factTemplate = context.getPkg()
113: .getFactTemplate(patternDescr.getObjectType());
114:
115: if (factTemplate != null) {
116: objectType = new FactTemplateObjectType(factTemplate);
117: } else {
118: try {
119: final Class userProvidedClass = context.getDialect()
120: .getTypeResolver().resolveType(
121: patternDescr.getObjectType());
122: objectType = new ClassObjectType(userProvidedClass);
123: } catch (final ClassNotFoundException e) {
124: context.getErrors().add(
125: new RuleError(context.getRule(), patternDescr,
126: null, "Unable to resolve ObjectType '"
127: + patternDescr.getObjectType()
128: + "'"));
129: return null;
130: }
131: }
132:
133: Pattern pattern;
134: if (patternDescr.getIdentifier() != null
135: && !patternDescr.getIdentifier().equals("")) {
136:
137: if (context.getDeclarationResolver().isDuplicated(
138: patternDescr.getIdentifier())) {
139: // This declaration already exists, so throw an Exception
140: context.getErrors().add(
141: new RuleError(context.getRule(), patternDescr,
142: null,
143: "Duplicate declaration for variable '"
144: + patternDescr.getIdentifier()
145: + "' in the rule '"
146: + context.getRule().getName()
147: + "'"));
148: }
149:
150: pattern = new Pattern(context.getNextPatternId(),
151: 0, // offset is 0 by default
152: objectType, patternDescr.getIdentifier(),
153: patternDescr.isInternalFact());
154: } else {
155: pattern = new Pattern(context.getNextPatternId(), 0, // offset is 0 by default
156: objectType, null);
157: }
158: // adding the newly created pattern to the build stack
159: // this is necessary in case of local declaration usage
160: context.getBuildStack().push(pattern);
161:
162: for (final Iterator it = patternDescr.getDescrs().iterator(); it
163: .hasNext();) {
164: final Object object = it.next();
165: buildConstraint(context, pattern, object, null);
166: }
167:
168: if (patternDescr.getSource() != null) {
169: // we have a pattern source, so build it
170: RuleConditionBuilder builder = context.getDialect()
171: .getBuilder(patternDescr.getSource().getClass());
172:
173: PatternSource source = (PatternSource) builder.build(
174: context, patternDescr.getSource());
175:
176: pattern.setSource(source);
177: }
178:
179: // poping the pattern
180: context.getBuildStack().pop();
181: return pattern;
182: }
183:
184: private void buildConstraint(final RuleBuildContext context,
185: final Pattern pattern, final Object constraint,
186: final AbstractCompositeConstraint container) {
187: if (constraint instanceof FieldBindingDescr) {
188: build(context, pattern, (FieldBindingDescr) constraint);
189: } else if (constraint instanceof FieldConstraintDescr) {
190: build(context, pattern, (FieldConstraintDescr) constraint,
191: container);
192: } else if (constraint instanceof PredicateDescr) {
193: build(context, pattern, (PredicateDescr) constraint,
194: container);
195: } else if (constraint instanceof AndDescr) {
196: AndConstraint and = new AndConstraint();
197: for (Iterator it = ((AndDescr) constraint).getDescrs()
198: .iterator(); it.hasNext();) {
199: this .buildConstraint(context, pattern, it.next(), and);
200: }
201: if (container == null) {
202: pattern.addConstraint(and);
203: } else {
204: container.addConstraint(and);
205: }
206: } else if (constraint instanceof OrDescr) {
207: OrConstraint or = new OrConstraint();
208: for (Iterator it = ((OrDescr) constraint).getDescrs()
209: .iterator(); it.hasNext();) {
210: this .buildConstraint(context, pattern, it.next(), or);
211: }
212: if (container == null) {
213: pattern.addConstraint(or);
214: } else {
215: container.addConstraint(or);
216: }
217: } else {
218: context.getErrors()
219: .add(
220: new RuleError(context.getRule(),
221: (BaseDescr) constraint, null,
222: "This is a bug: unable to build constraint descriptor: '"
223: + constraint
224: + "' in rule '"
225: + context.getRule()
226: .getName() + "'"));
227: }
228: }
229:
230: private void build(final RuleBuildContext context,
231: final Pattern pattern,
232: final FieldConstraintDescr fieldConstraintDescr,
233: final AbstractCompositeConstraint container) {
234: String fieldName = fieldConstraintDescr.getFieldName();
235:
236: if (fieldName.indexOf('[') > -1) {
237: rewriteToEval(context, pattern, fieldConstraintDescr,
238: container);
239:
240: // after building the predicate, we are done, so return
241: return;
242: }
243:
244: if (fieldName.indexOf('.') > -1) {
245: // we have a composite field name
246: String[] identifiers = fieldName.split("\\.");
247: if (identifiers.length == 2
248: && ((pattern.getDeclaration() != null && identifiers[0]
249: .equals(pattern.getDeclaration()
250: .getIdentifier())) || ("this"
251: .equals(identifiers[0])))) {
252: // we have a self reference, so, it is fine to do direct access
253: fieldName = identifiers[1];
254: } else {
255: rewriteToEval(context, pattern, fieldConstraintDescr,
256: container);
257:
258: // after building the predicate, we are done, so return
259: return;
260: }
261: }
262:
263: // if it is not a complex expression, just build a simple field constraint
264: final FieldExtractor extractor = getFieldExtractor(context,
265: fieldConstraintDescr, pattern.getObjectType(),
266: fieldName, false);
267: if (extractor == null) {
268: if (fieldConstraintDescr.getFieldName().startsWith("this.")) {
269: // it may still be MVEL special syntax, like map key syntax, so try eval
270: rewriteToEval(context, pattern, fieldConstraintDescr,
271: container);
272:
273: // after building the predicate, we are done, so return
274: return;
275: } else {
276: context.getErrors().add(
277: new RuleError(context.getRule(),
278: fieldConstraintDescr, null,
279: "Unable to create Field Extractor for '"
280: + fieldName
281: + "' of '"
282: + pattern.getObjectType()
283: .toString()
284: + "' in rule '"
285: + context.getRule().getName()
286: + "'"));
287: return;
288: }
289: }
290:
291: Restriction restriction = createRestriction(context, pattern,
292: fieldConstraintDescr, fieldConstraintDescr
293: .getRestriction(), extractor);
294:
295: if (restriction == null) {
296: // error was already logged during restriction creation failure
297: return;
298: }
299:
300: Constraint constraint = null;
301: if (restriction instanceof AbstractCompositeRestriction) {
302: constraint = new MultiRestrictionFieldConstraint(extractor,
303: restriction);
304: } else if (restriction instanceof LiteralRestriction) {
305: constraint = new LiteralConstraint(extractor,
306: (LiteralRestriction) restriction);
307: } else if (restriction instanceof VariableRestriction) {
308: constraint = new VariableConstraint(extractor,
309: (VariableRestriction) restriction);
310: } else if (restriction instanceof ReturnValueRestriction) {
311: constraint = new ReturnValueConstraint(extractor,
312: (ReturnValueRestriction) restriction);
313: } else {
314: context.getErrors()
315: .add(
316: new RuleError(context.getRule(),
317: fieldConstraintDescr, null,
318: "This is a bug: Unkown restriction type '"
319: + restriction.getClass()
320: + "' for pattern '"
321: + pattern.getObjectType()
322: .toString()
323: + "' in rule '"
324: + context.getRule()
325: .getName() + "'"));
326: }
327:
328: if (container == null) {
329: pattern.addConstraint(constraint);
330: } else {
331: container.addConstraint(constraint);
332: }
333: }
334:
335: private void rewriteToEval(final RuleBuildContext context,
336: final Pattern pattern,
337: final FieldConstraintDescr fieldConstraintDescr,
338: final AbstractCompositeConstraint container) {
339: // it is a complex expression, so we need to turn it into an MVEL predicate
340: Dialect dialect = context.getDialect();
341: // switch to MVEL dialect
342: MVELDialect mvelDialect = (MVELDialect) context
343: .getDialect("mvel");
344: boolean strictMode = mvelDialect.isStrictMode();
345: mvelDialect.setStrictMode(false);
346:
347: context.setDialect(mvelDialect);
348:
349: PredicateDescr predicateDescr = new PredicateDescr();
350: MVELDumper dumper = new MVELDumper();
351: predicateDescr.setContent(dumper.dump(fieldConstraintDescr));
352:
353: build(context, pattern, predicateDescr, container);
354:
355: mvelDialect.setStrictMode(strictMode);
356: // fall back to original dialect
357: context.setDialect(dialect);
358: }
359:
360: private Restriction createRestriction(
361: final RuleBuildContext context, final Pattern pattern,
362: final FieldConstraintDescr fieldConstraintDescr,
363: final RestrictionConnectiveDescr top,
364: final FieldExtractor extractor) {
365: Restriction[] restrictions = new Restriction[top
366: .getRestrictions().size()];
367: int index = 0;
368:
369: for (Iterator it = top.getRestrictions().iterator(); it
370: .hasNext();) {
371: RestrictionDescr restrictionDescr = (RestrictionDescr) it
372: .next();
373:
374: if (restrictionDescr instanceof RestrictionConnectiveDescr) {
375: restrictions[index++] = this .createRestriction(context,
376: pattern, fieldConstraintDescr,
377: (RestrictionConnectiveDescr) restrictionDescr,
378: extractor);
379:
380: } else {
381: restrictions[index] = buildRestriction(context,
382: pattern, extractor, fieldConstraintDescr,
383: restrictionDescr);
384: if (restrictions[index] == null) {
385: context.getErrors().add(
386: new RuleError(context.getRule(),
387: fieldConstraintDescr, null,
388: "Unable to create restriction '"
389: + restrictionDescr
390: .toString()
391: + "' for field '"
392: + fieldConstraintDescr
393: .getFieldName()
394: + "' in the rule '"
395: + context.getRule()
396: .getName() + "'"));
397: }
398: index++;
399: }
400: }
401:
402: if (restrictions.length > 1) {
403: AbstractCompositeRestriction composite = null;
404: if (top.getConnective() == RestrictionConnectiveDescr.AND) {
405: composite = new AndCompositeRestriction(restrictions);
406: } else if (top.getConnective() == RestrictionConnectiveDescr.OR) {
407: composite = new OrCompositeRestriction(restrictions);
408: } else {
409: context.getErrors().add(
410: new RuleError(context.getRule(),
411: fieldConstraintDescr, null,
412: "This is a bug: Impossible to create a composite restriction for connective: "
413: + top.getConnective()
414: + "' for field '"
415: + fieldConstraintDescr
416: .getFieldName()
417: + "' in the rule '"
418: + context.getRule().getName()
419: + "'"));
420: }
421:
422: return composite;
423: } else if (restrictions.length == 1) {
424: return restrictions[0];
425: }
426: context
427: .getErrors()
428: .add(
429: new RuleError(
430: context.getRule(),
431: fieldConstraintDescr,
432: null,
433: "This is a bug: trying to create a restriction for an empty restriction list for field '"
434: + fieldConstraintDescr
435: .getFieldName()
436: + "' in the rule '"
437: + context.getRule().getName()
438: + "'"));
439: return null;
440: }
441:
442: private void build(final RuleBuildContext context,
443: final Pattern pattern,
444: final FieldBindingDescr fieldBindingDescr) {
445:
446: if (context.getDeclarationResolver().isDuplicated(
447: fieldBindingDescr.getIdentifier())) {
448: // This declaration already exists, so throw an Exception
449: context.getErrors()
450: .add(
451: new RuleError(context.getRule(),
452: fieldBindingDescr, null,
453: "Duplicate declaration for variable '"
454: + fieldBindingDescr
455: .getIdentifier()
456: + "' in the rule '"
457: + context.getRule()
458: .getName() + "'"));
459: return;
460: }
461:
462: final FieldExtractor extractor = getFieldExtractor(context,
463: fieldBindingDescr, pattern.getObjectType(),
464: fieldBindingDescr.getFieldName(), true);
465: if (extractor == null) {
466: return;
467: }
468:
469: pattern.addDeclaration(fieldBindingDescr.getIdentifier(),
470: extractor);
471: }
472:
473: private void build(final RuleBuildContext context,
474: final Pattern pattern, final PredicateDescr predicateDescr,
475: final AbstractCompositeConstraint container) {
476:
477: final Dialect.AnalysisResult analysis = context.getDialect()
478: .analyzeExpression(context, predicateDescr,
479: predicateDescr.getContent());
480:
481: if (analysis == null) {
482: // something bad happened
483: return;
484: }
485:
486: // this will return an array with 2 lists
487: // where first list is from rule local variables
488: // second list is from global variables
489: final List[] usedIdentifiers = analysis.getBoundIdentifiers();
490:
491: final List tupleDeclarations = new ArrayList();
492: final List factDeclarations = new ArrayList();
493: for (int i = 0, size = usedIdentifiers[0].size(); i < size; i++) {
494: final Declaration decl = context.getDeclarationResolver()
495: .getDeclaration((String) usedIdentifiers[0].get(i));
496: if (decl.getPattern() == pattern) {
497: factDeclarations.add(decl);
498: } else {
499: tupleDeclarations.add(decl);
500: }
501: }
502: this .createImplicitBindings(context, pattern, analysis
503: .getNotBoundedIdentifiers(), factDeclarations);
504:
505: final Declaration[] previousDeclarations = (Declaration[]) tupleDeclarations
506: .toArray(new Declaration[tupleDeclarations.size()]);
507: final Declaration[] localDeclarations = (Declaration[]) factDeclarations
508: .toArray(new Declaration[factDeclarations.size()]);
509: final String[] requiredGlobals = (String[]) usedIdentifiers[1]
510: .toArray(new String[usedIdentifiers[1].size()]);
511:
512: final PredicateConstraint predicateConstraint = new PredicateConstraint(
513: null, previousDeclarations, localDeclarations,
514: requiredGlobals);
515:
516: if (container == null) {
517: pattern.addConstraint(predicateConstraint);
518: } else {
519: container.addConstraint(predicateConstraint);
520: }
521:
522: final PredicateBuilder builder = context.getDialect()
523: .getPredicateBuilder();
524:
525: builder.build(context, usedIdentifiers, previousDeclarations,
526: localDeclarations, predicateConstraint, predicateDescr);
527:
528: }
529:
530: /**
531: * @param context
532: * @param utils
533: * @param pattern
534: * @param usedIdentifiers
535: * @param NOT_BOUND_INDEX
536: * @param factDeclarations
537: */
538: private void createImplicitBindings(final RuleBuildContext context,
539: final Pattern pattern, final List unboundIdentifiers,
540: final List factDeclarations) {
541: // the following will create the implicit bindings
542: for (int i = 0, size = unboundIdentifiers.size(); i < size; i++) {
543: final String identifier = (String) unboundIdentifiers
544: .get(i);
545:
546: Declaration declaration = createDeclarationObject(context,
547: identifier, pattern);
548:
549: if (declaration != null) {
550: factDeclarations.add(declaration);
551: }
552: }
553: }
554:
555: /**
556: * Creates a declaration object for the field identified by the given identifier
557: * on the give pattern object
558: *
559: * @param context
560: * @param identifier
561: * @param pattern
562: * @return
563: */
564: private Declaration createDeclarationObject(
565: final RuleBuildContext context, final String identifier,
566: final Pattern pattern) {
567: final FieldBindingDescr implicitBinding = new FieldBindingDescr(
568: identifier, identifier);
569:
570: final FieldExtractor extractor = getFieldExtractor(context,
571: implicitBinding, pattern.getObjectType(),
572: implicitBinding.getFieldName(), false);
573: if (extractor == null) {
574: return null;
575: }
576:
577: final Declaration declaration = new Declaration(identifier,
578: extractor, pattern);
579: return declaration;
580: }
581:
582: private Restriction buildRestriction(
583: final RuleBuildContext context, final Pattern pattern,
584: final FieldExtractor extractor,
585: final FieldConstraintDescr fieldConstraintDescr,
586: final RestrictionDescr restrictionDescr) {
587: Restriction restriction = null;
588: if (restrictionDescr instanceof LiteralRestrictionDescr) {
589: restriction = buildRestriction(context, extractor,
590: fieldConstraintDescr,
591: (LiteralRestrictionDescr) restrictionDescr);
592: } else if (restrictionDescr instanceof QualifiedIdentifierRestrictionDescr) {
593: restriction = buildRestriction(
594: context,
595: extractor,
596: fieldConstraintDescr,
597: (QualifiedIdentifierRestrictionDescr) restrictionDescr);
598: } else if (restrictionDescr instanceof VariableRestrictionDescr) {
599: restriction = buildRestriction(context, extractor,
600: fieldConstraintDescr,
601: (VariableRestrictionDescr) restrictionDescr);
602: } else if (restrictionDescr instanceof ReturnValueRestrictionDescr) {
603: restriction = buildRestriction(context, pattern, extractor,
604: fieldConstraintDescr,
605: (ReturnValueRestrictionDescr) restrictionDescr);
606:
607: }
608:
609: return restriction;
610: }
611:
612: private VariableRestriction buildRestriction(
613: final RuleBuildContext context,
614: final FieldExtractor extractor,
615: final FieldConstraintDescr fieldConstraintDescr,
616: final VariableRestrictionDescr variableRestrictionDescr) {
617: if (variableRestrictionDescr.getIdentifier() == null
618: || variableRestrictionDescr.getIdentifier().equals("")) {
619: context.getErrors().add(
620: new RuleError(context.getRule(),
621: variableRestrictionDescr, null,
622: "Identifier not defined for binding field '"
623: + fieldConstraintDescr
624: .getFieldName() + "'"));
625: return null;
626: }
627:
628: Declaration declaration = context.getDeclarationResolver()
629: .getDeclaration(
630: variableRestrictionDescr.getIdentifier());
631:
632: if (declaration == null) {
633: // trying to create implicit declaration
634: final Pattern this Pattern = (Pattern) context
635: .getBuildStack().peek();
636: final Declaration implicit = this .createDeclarationObject(
637: context, variableRestrictionDescr.getIdentifier(),
638: this Pattern);
639: if (implicit != null) {
640: declaration = implicit;
641: } else {
642: context
643: .getErrors()
644: .add(
645: new RuleError(
646: context.getRule(),
647: variableRestrictionDescr,
648: null,
649: "Unable to return Declaration for identifier '"
650: + variableRestrictionDescr
651: .getIdentifier()
652: + "'"));
653: return null;
654: }
655: }
656:
657: final Evaluator evaluator = getEvaluator(context,
658: variableRestrictionDescr, extractor.getValueType(),
659: variableRestrictionDescr.getEvaluator());
660: if (evaluator == null) {
661: return null;
662: }
663:
664: return new VariableRestriction(extractor, declaration,
665: evaluator);
666: }
667:
668: private LiteralRestriction buildRestriction(
669: final RuleBuildContext context,
670: final FieldExtractor extractor,
671: final FieldConstraintDescr fieldConstraintDescr,
672: final LiteralRestrictionDescr literalRestrictionDescr) {
673: FieldValue field = null;
674: try {
675: field = FieldFactory.getFieldValue(literalRestrictionDescr
676: .getText(), extractor.getValueType());
677: } catch (final Exception e) {
678: context.getErrors().add(
679: new RuleError(context.getRule(),
680: literalRestrictionDescr, e,
681: "Unable to create a Field value of type '"
682: + extractor.getValueType()
683: + "' and value '"
684: + literalRestrictionDescr.getText()
685: + "'"));
686: }
687:
688: if (field == null) {
689: return null;
690: }
691:
692: final Evaluator evaluator = getEvaluator(context,
693: literalRestrictionDescr, extractor.getValueType(),
694: literalRestrictionDescr.getEvaluator());
695: if (evaluator == null) {
696: return null;
697: }
698:
699: return new LiteralRestriction(field, evaluator, extractor);
700: }
701:
702: private Restriction buildRestriction(
703: final RuleBuildContext context,
704: final FieldExtractor extractor,
705: final FieldConstraintDescr fieldConstraintDescr,
706: final QualifiedIdentifierRestrictionDescr qiRestrictionDescr) {
707: FieldValue field = null;
708: final String[] parts = qiRestrictionDescr.getText()
709: .split("\\.");
710:
711: // if only 2 parts, it may be a composed direct property access
712: if (parts.length == 2) {
713: Declaration implicit = null;
714: if ("this".equals(parts[0])) {
715: implicit = this .createDeclarationObject(context,
716: parts[1], (Pattern) context.getBuildStack()
717: .peek());
718: } else {
719: final Declaration decl = context
720: .getDeclarationResolver().getDeclaration(
721: parts[0]);
722: // if a declaration exists, then it may be a variable direct property access, not an enum
723: if (decl != null) {
724: if (decl.isPatternDeclaration()) {
725: implicit = this .createDeclarationObject(
726: context, parts[1], decl.getPattern());
727:
728: } else {
729: context
730: .getErrors()
731: .add(
732: new RuleError(
733: context.getRule(),
734: qiRestrictionDescr,
735: "",
736: "Not possible to directly access the property '"
737: + parts[1]
738: + "' of declaration '"
739: + parts[0]
740: + "' since it is not a pattern"));
741: return null;
742: }
743: }
744: }
745:
746: if (implicit != null) {
747: final Evaluator evaluator = getEvaluator(context,
748: qiRestrictionDescr, extractor.getValueType(),
749: qiRestrictionDescr.getEvaluator());
750: if (evaluator == null) {
751: return null;
752: }
753:
754: return new VariableRestriction(extractor, implicit,
755: evaluator);
756: }
757: }
758:
759: final int lastDot = qiRestrictionDescr.getText().lastIndexOf(
760: '.');
761: final String className = qiRestrictionDescr.getText()
762: .substring(0, lastDot);
763: final String fieldName = qiRestrictionDescr.getText()
764: .substring(lastDot + 1);
765: try {
766: final Class staticClass = context.getDialect()
767: .getTypeResolver().resolveType(className);
768: field = FieldFactory.getFieldValue(staticClass.getField(
769: fieldName).get(null), extractor.getValueType());
770: } catch (final ClassNotFoundException e) {
771: // nothing to do, as it is not a class name with static field
772: } catch (final Exception e) {
773: context.getErrors().add(
774: new RuleError(context.getRule(),
775: qiRestrictionDescr, e,
776: "Unable to create a Field value of type '"
777: + extractor.getValueType()
778: + "' and value '"
779: + qiRestrictionDescr.getText()
780: + "'"));
781: }
782:
783: if (field == null) {
784: return null;
785: }
786:
787: final Evaluator evaluator = getEvaluator(context,
788: qiRestrictionDescr, extractor.getValueType(),
789: qiRestrictionDescr.getEvaluator());
790: if (evaluator == null) {
791: return null;
792: }
793:
794: return new LiteralRestriction(field, evaluator, extractor);
795: }
796:
797: private ReturnValueRestriction buildRestriction(
798: final RuleBuildContext context,
799: final Pattern pattern,
800: final FieldExtractor extractor,
801: final FieldConstraintDescr fieldConstraintDescr,
802: final ReturnValueRestrictionDescr returnValueRestrictionDescr) {
803: Dialect.AnalysisResult analysis = context.getDialect()
804: .analyzeExpression(context,
805: returnValueRestrictionDescr,
806: returnValueRestrictionDescr.getContent());
807: final List[] usedIdentifiers = analysis.getBoundIdentifiers();
808:
809: final List tupleDeclarations = new ArrayList();
810: final List factDeclarations = new ArrayList();
811: for (int i = 0, size = usedIdentifiers[0].size(); i < size; i++) {
812: final Declaration declaration = context
813: .getDeclarationResolver().getDeclaration(
814: (String) usedIdentifiers[0].get(i));
815: if (declaration.getPattern() == pattern) {
816: factDeclarations.add(declaration);
817: } else {
818: tupleDeclarations.add(declaration);
819: }
820: }
821:
822: createImplicitBindings(context, pattern, analysis
823: .getNotBoundedIdentifiers(), factDeclarations);
824:
825: final Evaluator evaluator = getEvaluator(context,
826: returnValueRestrictionDescr, extractor.getValueType(),
827: returnValueRestrictionDescr.getEvaluator());
828: if (evaluator == null) {
829: return null;
830: }
831:
832: final Declaration[] previousDeclarations = (Declaration[]) tupleDeclarations
833: .toArray(new Declaration[tupleDeclarations.size()]);
834: final Declaration[] localDeclarations = (Declaration[]) factDeclarations
835: .toArray(new Declaration[factDeclarations.size()]);
836: final String[] requiredGlobals = (String[]) usedIdentifiers[1]
837: .toArray(new String[usedIdentifiers[1].size()]);
838: final ReturnValueRestriction returnValueRestriction = new ReturnValueRestriction(
839: extractor, previousDeclarations, localDeclarations,
840: requiredGlobals, evaluator);
841:
842: final ReturnValueBuilder builder = context.getDialect()
843: .getReturnValueBuilder();
844:
845: builder.build(context, usedIdentifiers, previousDeclarations,
846: localDeclarations, returnValueRestriction,
847: returnValueRestrictionDescr);
848:
849: return returnValueRestriction;
850: }
851:
852: private FieldExtractor getFieldExtractor(
853: final RuleBuildContext context, final BaseDescr descr,
854: final ObjectType objectType, final String fieldName,
855: final boolean reportError) {
856: FieldExtractor extractor = null;
857:
858: if (objectType.getValueType() == ValueType.FACTTEMPLATE_TYPE) {
859: //@todo use extractor cache
860: final FactTemplate factTemplate = ((FactTemplateObjectType) objectType)
861: .getFactTemplate();
862: extractor = new FactTemplateFieldExtractor(factTemplate,
863: factTemplate.getFieldTemplateIndex(fieldName));
864: } else {
865: try {
866: ClassLoader classloader = context.getPkg()
867: .getPackageCompilationData().getClassLoader();
868: extractor = context.getDialect()
869: .getClassFieldExtractorCache().getExtractor(
870: ((ClassObjectType) objectType)
871: .getClassType(), fieldName,
872: classloader);
873: } catch (final RuntimeDroolsException e) {
874: if (reportError) {
875: context.getErrors().add(
876: new RuleError(context.getRule(), descr, e,
877: "Unable to create Field Extractor for '"
878: + fieldName + "'"));
879: }
880: }
881: }
882:
883: return extractor;
884: }
885:
886: private Evaluator getEvaluator(final RuleBuildContext context,
887: final BaseDescr descr, final ValueType valueType,
888: final String evaluatorString) {
889:
890: final Evaluator evaluator = valueType.getEvaluator(Operator
891: .determineOperator(evaluatorString));
892:
893: if (evaluator == null) {
894: context.getErrors().add(
895: new RuleError(context.getRule(), descr, null,
896: "Unable to determine the Evaluator for '"
897: + valueType + "' and '"
898: + evaluatorString + "'"));
899: }
900:
901: return evaluator;
902: }
903:
904: }
|