001: package org.drools.rule;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.io.Serializable;
020: import java.util.Calendar;
021: import java.util.Iterator;
022: import java.util.Map;
023:
024: import org.drools.base.SalienceInteger;
025: import org.drools.spi.AgendaGroup;
026: import org.drools.spi.Consequence;
027: import org.drools.spi.Duration;
028: import org.drools.spi.Salience;
029:
030: /**
031: * A <code>Rule</code> contains a set of <code>Test</code>s and a
032: * <code>Consequence</code>.
033: * <p>
034: * The <code>Test</code>s describe the circumstances that representrepresent
035: * a match for this rule. The <code>Consequence</code> gets fired when the
036: * Conditions match.
037: *
038: * @see Eval
039: * @see Consequence
040: * @author <a href="mailto:bob@eng.werken.com"> bob mcwhirter </a>
041: * @author <a href="mailto:simon@redhillconsulting.com.au"> Simon Harris </a>
042: * @author <a href="mailto:mproctor@codehaus.org"> mark proctor </a>
043: */
044: public class Rule implements Serializable {
045: /**
046: *
047: */
048: private static final long serialVersionUID = 400L;
049:
050: /** */
051: // ------------------------------------------------------------
052: // Instance members
053: // ------------------------------------------------------------
054: /** The parent pkg */
055: private String pkg;
056:
057: /** Name of the rule. */
058: private final String name;
059:
060: /** Salience value. */
061: private Salience salience;
062:
063: /** The Rule is dirty after patterns have been added */
064: private boolean dirty;
065: private Map declarations;
066: private Declaration[] declarationArray;
067:
068: private GroupElement lhsRoot;
069:
070: private String dialect;
071:
072: private String agendaGroup;
073:
074: /** Consequence. */
075: private Consequence consequence;
076:
077: /** Truthness duration. */
078: private Duration duration;
079:
080: /** Load order in Package */
081: private long loadOrder;
082:
083: /** Is recursion of this rule allowed */
084: private boolean noLoop;
085:
086: /** makes the rule's much the current focus */
087: private boolean autoFocus;
088:
089: private String activationGroup;
090:
091: private String ruleFlowGroup;
092:
093: private boolean lockOnActive;
094:
095: private boolean hasLogicalDependency;
096:
097: /** indicates that the rule is semantically correct. */
098: private boolean semanticallyValid;
099:
100: private Calendar dateEffective;
101:
102: private Calendar dateExpires;
103:
104: private boolean enabled;
105:
106: // ------------------------------------------------------------
107: // Constructors
108: // ------------------------------------------------------------
109:
110: /**
111: * Construct a
112: * <code>Rule<code> with the given name for the specified pkg parent
113: *
114: * @param name
115: * The name of this rule.
116: */
117: public Rule(final String name, final String pkg,
118: final String agendaGroup) {
119: this .name = name;
120: this .pkg = pkg;
121: this .agendaGroup = agendaGroup;
122: this .lhsRoot = GroupElementFactory.newAndInstance();
123: this .semanticallyValid = true;
124: this .enabled = true;
125: this .salience = SalienceInteger.DEFAULT_SALIENCE;
126: }
127:
128: /**
129: * Construct a
130: * <code>Rule<code> with the given name for the specified pkg parent
131: *
132: * @param name
133: * The name of this rule.
134: */
135: public Rule(final String name, final String agendaGroup) {
136: this (name, null, agendaGroup);
137: }
138:
139: public Rule(final String name) {
140: this (name, null, AgendaGroup.MAIN);
141: }
142:
143: public String getDialect() {
144: return dialect;
145: }
146:
147: public void setDialect(String dialect) {
148: this .dialect = dialect;
149: }
150:
151: /**
152: * Set the truthness duration. This causes a delay before the firing of the
153: * <code>Consequence</code> if the rule is still true at the end of the
154: * duration.
155: *
156: * <p>
157: * This is merely a convenience method for calling
158: * {@link #setDuration(Duration)}with a <code>FixedDuration</code>.
159: * </p>
160: *
161: * @see #setDuration(Duration)
162: * @see FixedDuration
163: *
164: * @param seconds -
165: * The number of seconds the rule must hold true in order to
166: * fire.
167: */
168: public void setDuration(final long ms) {
169: this .duration = new FixedDuration(ms);
170: }
171:
172: /**
173: * Set the truthness duration object. This causes a delay before the firing
174: * of the <code>Consequence</code> if the rule is still true at the end of
175: * the duration.
176: *
177: * @param duration
178: * The truth duration object.
179: */
180: public void setDuration(final Duration duration) {
181: this .duration = duration;
182: }
183:
184: /**
185: * Retrieve the truthness duration object.
186: *
187: * @return The truthness duration object.
188: */
189: public Duration getDuration() {
190: return this .duration;
191: }
192:
193: /**
194: * Determine if this rule is internally consistent and valid.
195: * This will include checks to make sure the rules semantic components (actions and predicates)
196: * are valid.
197: *
198: * No exception is thrown.
199: * <p>
200: * A <code>Rule</code> must include at least one parameter declaration and
201: * one condition.
202: * </p>
203: *
204: * @return <code>true</code> if this rule is valid, else
205: * <code>false</code>.
206: */
207: public boolean isValid() {
208: //if ( this.patterns.size() == 0 ) {
209: // return false;
210: //}
211:
212: if (this .consequence == null || !isSemanticallyValid()) {
213: return false;
214: }
215:
216: return true;
217: }
218:
219: public String getPackage() {
220: return this .pkg;
221: }
222:
223: /**
224: * Retrieve the name of this rule.
225: *
226: * @return The name of this rule.
227: */
228: public String getName() {
229: return this .name;
230: }
231:
232: /**
233: * Retrieve the <code>Rule</code> salience.
234: *
235: * @return The salience.
236: */
237: public Salience getSalience() {
238: return this .salience;
239: }
240:
241: /**
242: * Set the <code>Rule<code> salience.
243: *
244: * @param salience The salience.
245: */
246: public void setSalience(final Salience salience) {
247: this .salience = salience;
248: }
249:
250: public String getAgendaGroup() {
251: return this .agendaGroup;
252: }
253:
254: public void setAgendaGroup(final String agendaGroup) {
255: this .agendaGroup = agendaGroup;
256: }
257:
258: public boolean isNoLoop() {
259: return this .noLoop;
260: }
261:
262: /**
263: * This returns true is the rule is effective.
264: * If the rule is not effective, it cannot activate.
265: *
266: * This uses the dateEffective, dateExpires and enabled flag to decide this.
267: */
268: public boolean isEffective() {
269: if (!this .enabled) {
270: return false;
271: }
272: if (this .dateEffective == null && this .dateExpires == null) {
273: return true;
274: } else {
275: final Calendar now = Calendar.getInstance();
276: if (this .dateEffective != null && this .dateExpires != null) {
277: return (now.after(this .dateEffective) && now
278: .before(this .dateExpires));
279: } else if (this .dateEffective != null) {
280: return (now.after(this .dateEffective));
281: } else {
282: return (now.before(this .dateExpires));
283: }
284:
285: }
286: }
287:
288: public void setNoLoop(final boolean noLoop) {
289: this .noLoop = noLoop;
290: }
291:
292: public boolean getAutoFocus() {
293: return this .autoFocus;
294: }
295:
296: public void setAutoFocus(final boolean autoFocus) {
297: this .autoFocus = autoFocus;
298: }
299:
300: public String getActivationGroup() {
301: return this .activationGroup;
302: }
303:
304: public void setActivationGroup(final String activationGroup) {
305: this .activationGroup = activationGroup;
306: }
307:
308: public String getRuleFlowGroup() {
309: return this .ruleFlowGroup;
310: }
311:
312: public void setRuleFlowGroup(final String ruleFlowGroup) {
313: this .ruleFlowGroup = ruleFlowGroup;
314: }
315:
316: /**
317: * Retrieve a parameter <code>Declaration</code> by identifier.
318: *
319: * @param identifier
320: * The identifier.
321: *
322: * @return The declaration or <code>null</code> if no declaration matches
323: * the <code>identifier</code>.
324: */
325: public Declaration getDeclaration(final String identifier) {
326: if (this .dirty || (this .declarations == null)) {
327: this .declarations = this .lhsRoot.getOuterDeclarations();
328: this .declarationArray = (Declaration[]) this .declarations
329: .values().toArray(
330: new Declaration[this .declarations.values()
331: .size()]);
332: this .dirty = false;
333: }
334: return (Declaration) this .declarations.get(identifier);
335: }
336:
337: /**
338: * This field is updated at runtime, when the first logical assertion is done. I'm currently not too happy about having this determine at runtime
339: * but its currently easier than trying to do this at compile time, although eventually this should be changed
340: * @return
341: */
342: public boolean hasLogicalDependency() {
343: return this .hasLogicalDependency;
344: }
345:
346: public void setHasLogicalDependency(boolean hasLogicalDependency) {
347: this .hasLogicalDependency = hasLogicalDependency;
348: }
349:
350: public boolean isLockOnActive() {
351: return this .lockOnActive;
352: }
353:
354: public void setLockOnActive(final boolean lockOnActive) {
355: this .lockOnActive = lockOnActive;
356: }
357:
358: /**
359: * Retrieve the set of all <i>root fact object </i> parameter
360: * <code>Declarations</code>.
361: *
362: * @return The Set of <code>Declarations</code> in order which specify the
363: * <i>root fact objects</i>.
364: */
365: public Declaration[] getDeclarations() {
366: if (this .dirty || (this .declarationArray == null)) {
367: this .declarations = this .lhsRoot.getOuterDeclarations();
368: this .declarationArray = (Declaration[]) this .declarations
369: .values().toArray(
370: new Declaration[this .declarations.values()
371: .size()]);
372: this .dirty = false;
373: }
374: return this .declarationArray;
375: }
376:
377: /**
378: * Add a pattern to the rule. All patterns are searched for bindings which are then added to the rule
379: * as declarations
380: *
381: * @param condition
382: * The <code>Test</code> to add.
383: * @throws InvalidRuleException
384: */
385: public void addPattern(final RuleConditionElement element) {
386: this .dirty = true;
387: this .lhsRoot.addChild(element);
388: }
389:
390: /**
391: * Retrieve the <code>List</code> of <code>Conditions</code> for this
392: * rule.
393: *
394: * @return The <code>List</code> of <code>Conditions</code>.
395: */
396: public GroupElement getLhs() {
397: return this .lhsRoot;
398: }
399:
400: public void setLhs(final GroupElement lhsRoot) {
401: this .dirty = true;
402: this .lhsRoot = lhsRoot;
403: }
404:
405: /**
406: * Uses the LogicTransformer to process the Rule patters - if no ORs are
407: * used this will return an array of a single AND element. If there are Ors
408: * it will return an And element for each possible logic branch. The
409: * processing uses as a clone of the Rule's patterns, so they are not
410: * changed.
411: *
412: * @return
413: * @throws InvalidPatternException
414: */
415: public GroupElement[] getTransformedLhs()
416: throws InvalidPatternException {
417: return LogicTransformer.getInstance().transform(this .lhsRoot);
418: }
419:
420: public int getSpecifity() {
421: return getSpecifity(this .lhsRoot);
422: }
423:
424: private int getSpecifity(final GroupElement ce) {
425: int specificity = 0;
426: for (final Iterator it = ce.getChildren().iterator(); it
427: .hasNext();) {
428: final Object object = it.next();
429: if (object instanceof Pattern) {
430: specificity += getSpecifity((Pattern) object);
431: } else if (object instanceof GroupElement) {
432: specificity += getSpecifity((GroupElement) object);
433: }
434: }
435: return specificity;
436: }
437:
438: private int getSpecifity(final Pattern pattern) {
439: int specificity = 0;
440: for (final Iterator it = pattern.getConstraints().iterator(); it
441: .hasNext();) {
442: if (!(it.next() instanceof Declaration)) {
443: specificity++;
444: }
445: }
446:
447: return specificity;
448: }
449:
450: /**
451: * Set the <code>Consequence</code> that is associated with the successful
452: * match of this rule.
453: *
454: * @param consequence
455: * The <code>Consequence</code> to attach to this
456: * <code>Rule</code>.
457: */
458: public void setConsequence(final Consequence consequence) {
459: this .consequence = consequence;
460: }
461:
462: /**
463: * Retrieve the <code>Consequence</code> associated with this
464: * <code>Rule</code>.
465: *
466: * @return The <code>Consequence</code>.
467: */
468: public Consequence getConsequence() {
469: return this .consequence;
470: }
471:
472: public long getLoadOrder() {
473: return this .loadOrder;
474: }
475:
476: public void setLoadOrder(final long loadOrder) {
477: this .loadOrder = loadOrder;
478: }
479:
480: public String toString() {
481: return "[Rule name=" + this .name + ", agendaGroup="
482: + this .agendaGroup + ", salience=" + this .salience
483: + ", no-loop=" + this .noLoop + "]";
484: }
485:
486: public int hashCode() {
487: final int PRIME = 31;
488: int result = 1;
489: result = PRIME * result
490: + ((name == null) ? 0 : name.hashCode());
491: result = PRIME * result + ((pkg == null) ? 0 : pkg.hashCode());
492: return result;
493: }
494:
495: public boolean equals(Object obj) {
496: if (this == obj)
497: return true;
498: if (!super .equals(obj))
499: return false;
500: if (getClass() != obj.getClass())
501: return false;
502: final Rule other = (Rule) obj;
503: if (name == null) {
504: if (other.name != null)
505: return false;
506: } else if (!name.equals(other.name))
507: return false;
508: if (pkg == null) {
509: if (other.pkg != null)
510: return false;
511: } else if (!pkg.equals(other.pkg))
512: return false;
513: return true;
514: }
515:
516: public void setSemanticallyValid(final boolean valid) {
517: this .semanticallyValid = valid;
518: }
519:
520: /**
521: * This will return if the semantic actions or predicates in the rules
522: * are valid.
523: * This is provided so that lists of rules can be provided even if their semantic actions
524: * do not "compile" etc.
525: */
526: public boolean isSemanticallyValid() {
527: return this .semanticallyValid;
528: }
529:
530: /**
531: * Sets the date from which this rule takes effect (can include time to the millisecond).
532: * @param effectiveDate
533: */
534: public void setDateEffective(final Calendar effectiveDate) {
535: this .dateEffective = effectiveDate;
536: }
537:
538: /**
539: * Sets the date after which the rule will no longer apply (can include time to the millisecond).
540: * @param expiresDate
541: */
542: public void setDateExpires(final Calendar expiresDate) {
543: this .dateExpires = expiresDate;
544: }
545:
546: public Calendar getDateEffective() {
547: return this .dateEffective;
548: }
549:
550: public Calendar getDateExpires() {
551: return this .dateExpires;
552: }
553:
554: /**
555: * A rule is enabled by default. This can explicitly disable it in which case it will never activate.
556: */
557: public void setEnabled(final boolean b) {
558: this .enabled = b;
559: }
560:
561: public boolean isEnabled() {
562: return this.enabled;
563: }
564: }
|