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.ArrayList;
021: import java.util.Collections;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026:
027: import org.drools.RuntimeDroolsException;
028:
029: public class GroupElement extends ConditionalElement {
030:
031: private static final long serialVersionUID = 400L;
032:
033: public static final Type AND = new AndType();
034: public static final Type OR = new OrType();
035: public static final Type EXISTS = new ExistsType();
036: public static final Type NOT = new NotType();
037:
038: private Type type = null;
039: private final List children = new ArrayList();
040:
041: public GroupElement() {
042: this (AND);
043: }
044:
045: public GroupElement(final Type type) {
046: this .type = type;
047: }
048:
049: /**
050: * Adds a child to the current GroupElement.
051: *
052: * Restrictions are:
053: * NOT/EXISTS: can have only one child, either a single Pattern or another CE
054: *
055: * @param child
056: */
057: public void addChild(final RuleConditionElement child) {
058: if ((this .isNot() || this .isExists())
059: && (this .children.size() > 0)) {
060: throw new RuntimeDroolsException(
061: this .type.toString()
062: + " can have only a single child element. Either a single Pattern or another CE.");
063: }
064: this .children.add(child);
065: }
066:
067: /**
068: * Adds the given child as the (index)th child of the this GroupElement
069: * @param index
070: * @param rce
071: */
072: public void addChild(final int index, final RuleConditionElement rce) {
073: this .children.add(index, rce);
074: }
075:
076: public List getChildren() {
077: return this .children;
078: }
079:
080: /**
081: * @inheritDoc
082: */
083: public Map getInnerDeclarations() {
084: return this .type.getInnerDeclarations(this .children);
085: }
086:
087: /**
088: * @inheritDoc
089: */
090: public Map getOuterDeclarations() {
091: return this .type.getOuterDeclarations(this .children);
092: }
093:
094: /**
095: * @inheritDoc
096: */
097: public Declaration resolveDeclaration(final String identifier) {
098: return (Declaration) this .type.getInnerDeclarations(
099: this .children).get(identifier);
100: }
101:
102: /**
103: * Optimize the group element subtree by removing redundancies
104: * like an AND inside another AND, OR inside OR, single branches
105: * AND/OR, etc.
106: *
107: * LogicTransformer does further, more complicated, transformations
108: */
109: public void pack() {
110: // we must clone, since we want to iterate only over the original list
111: final Object[] clone = this .children.toArray();
112: for (int i = 0; i < clone.length; i++) {
113: // if child is also a group element, there may be
114: // some possible clean up / optimizations to be done
115: if (clone[i] instanceof GroupElement) {
116: final GroupElement childGroup = (GroupElement) clone[i];
117: childGroup.pack(this );
118: }
119: }
120:
121: // if after packing, this is an AND or OR GE with a single
122: // child GE, then clone child into current node eliminating child
123: if ((this .isAnd() || this .isOr())
124: && (this .children.size() == 1)) {
125: final Object child = this .getChildren().get(0);
126: if (child instanceof GroupElement) {
127: final GroupElement group = (GroupElement) child;
128: this .type = group.getType();
129: this .children.clear();
130: this .children.addAll(group.getChildren());
131: }
132: }
133: }
134:
135: /**
136: * @param parent
137: */
138: private void pack(final GroupElement parent) {
139: if (this .children.size() == 0) {
140: // if there is no child, just remove this node
141: parent.children.remove(this );
142: return;
143: }
144:
145: // If this is an AND or OR or EXISTS, there are some possible merges
146: if (this .isAnd() || this .isOr() || this .isExists()) {
147:
148: // if parent is of the same type as current node,
149: // then merge this childs with parent childs
150: if (parent.getType() == this .getType()) {
151:
152: // we must keep the order so, save index
153: int index = parent.getChildren().indexOf(this );
154: parent.getChildren().remove(this );
155: // for each child, pack it and add it to parent
156: for (final Iterator childIt = this .children.iterator(); childIt
157: .hasNext();) {
158: final Object child = childIt.next();
159: // we must keep the order, so add in the same place were parent was before
160: parent.getChildren().add(index++, child);
161: if (child instanceof GroupElement) {
162: final int previousSize = parent.getChildren()
163: .size();
164: ((GroupElement) child).pack(parent);
165: // in case the child also added elements to the parent,
166: // we need to compensate
167: index += (parent.getChildren().size() - previousSize);
168: }
169: }
170:
171: // if current node has a single child, then move it to parent and pack it
172: } else if ((!this .isExists())
173: && (this .children.size() == 1)) {
174: // we must keep the order so, save index
175: final int index = parent.getChildren().indexOf(this );
176: parent.getChildren().remove(this );
177:
178: final Object child = this .children.get(0);
179: parent.getChildren().add(index, child);
180:
181: if (child instanceof GroupElement) {
182: ((GroupElement) child).pack(parent);
183: }
184:
185: // otherwise pack itself
186: } else {
187: this .pack();
188: }
189:
190: // also pack itself if it is a NOT
191: } else {
192: this .pack();
193: }
194: }
195:
196: /**
197: * Traverses two trees and checks that they are structurally equal at all
198: * levels
199: *
200: * @param e1
201: * @param e2
202: * @return
203: */
204: public boolean equals(final Object object) {
205: // Return false if its null or not an instance of ConditionalElement
206: if (object == null || !(object instanceof GroupElement)) {
207: return false;
208: }
209:
210: // Return true if they are the same reference
211: if (this == object) {
212: return true;
213: }
214:
215: // Now try a recurse manual check
216: final GroupElement e2 = (GroupElement) object;
217: if (!this .type.equals(e2.type)) {
218: return false;
219: }
220:
221: final List e1Children = this .getChildren();
222: final List e2Children = e2.getChildren();
223: if (e1Children.size() != e2Children.size()) {
224: return false;
225: }
226:
227: for (int i = 0; i < e1Children.size(); i++) {
228: final Object e1Object1 = e1Children.get(i);
229: final Object e2Object1 = e2Children.get(i);
230: if (!e1Object1.equals(e2Object1)) {
231: return false;
232: }
233: }
234:
235: return true;
236: }
237:
238: public int hashCode() {
239: return this .type.hashCode() + this .children.hashCode();
240: }
241:
242: /**
243: * Clones all Conditional Elements but references the non ConditionalElement
244: * children
245: *
246: * @param e1
247: * @param e2
248: * @return
249: */
250: public Object clone() {
251: GroupElement cloned = null;
252:
253: try {
254: cloned = (GroupElement) this .getClass().newInstance();
255: } catch (final InstantiationException e) {
256: throw new RuntimeException("Could not clone '"
257: + this .getClass().getName() + "'");
258: } catch (final IllegalAccessException e) {
259: throw new RuntimeException("Could not clone '"
260: + this .getClass().getName() + "'");
261: }
262:
263: cloned.setType(this .getType());
264:
265: for (final Iterator it = this .children.iterator(); it.hasNext();) {
266: RuleConditionElement re = (RuleConditionElement) it.next();
267: if (re instanceof GroupElement) {
268: re = (RuleConditionElement) ((GroupElement) re).clone();
269: }
270: cloned.addChild(re);
271: }
272:
273: return cloned;
274: }
275:
276: public Type getType() {
277: return this .type;
278: }
279:
280: public void setType(final Type type) {
281: this .type = type;
282: }
283:
284: public boolean isAnd() {
285: return this .type.isAnd();
286: }
287:
288: public boolean isOr() {
289: return this .type.isOr();
290: }
291:
292: public boolean isNot() {
293: return this .type.isNot();
294: }
295:
296: public boolean isExists() {
297: return this .type.isExists();
298: }
299:
300: public String toString() {
301: return this .type.toString() + this .children.toString();
302: }
303:
304: /**
305: * A public interface for CE types
306: */
307: public static interface Type extends Serializable {
308:
309: /**
310: * Returns true if this CE type is an AND
311: */
312: public boolean isAnd();
313:
314: /**
315: * Returns true if this CE type is an OR
316: */
317: public boolean isOr();
318:
319: /**
320: * Returns true if this CE type is an NOT
321: */
322: public boolean isNot();
323:
324: /**
325: * Returns true if this CE type is an EXISTS
326: */
327: public boolean isExists();
328:
329: /**
330: * Returns a map of declarations that are
331: * visible inside of an element of this type
332: */
333: public Map getInnerDeclarations(List children);
334:
335: /**
336: * Returns a map of declarations that are
337: * visible outside of an element of this type
338: */
339: public Map getOuterDeclarations(List children);
340: }
341:
342: private static abstract class AbstractType implements Type {
343:
344: private static final long serialVersionUID = 400L;
345:
346: /**
347: * @inheritDoc
348: */
349: public Map getInnerDeclarations(final List children) {
350: Map declarations = null;
351:
352: if (children.isEmpty()) {
353: declarations = Collections.EMPTY_MAP;
354: } else if (children.size() == 1) {
355: final RuleConditionElement re = (RuleConditionElement) children
356: .get(0);
357: declarations = re.getOuterDeclarations();
358: } else {
359: declarations = new HashMap();
360: for (final Iterator it = children.iterator(); it
361: .hasNext();) {
362: declarations.putAll(((RuleConditionElement) it
363: .next()).getOuterDeclarations());
364: }
365: }
366: return declarations;
367: }
368:
369: /**
370: * @inheritDoc
371: */
372: public Map getOuterDeclarations(final List children) {
373: Map declarations = null;
374:
375: if (children.isEmpty()) {
376: declarations = Collections.EMPTY_MAP;
377: } else if (children.size() == 1) {
378: final RuleConditionElement re = (RuleConditionElement) children
379: .get(0);
380: declarations = re.getOuterDeclarations();
381: } else {
382: declarations = new HashMap();
383: for (final Iterator it = children.iterator(); it
384: .hasNext();) {
385: declarations.putAll(((RuleConditionElement) it
386: .next()).getOuterDeclarations());
387: }
388: }
389: return declarations;
390: }
391: }
392:
393: /**
394: * An AND CE type
395: */
396: private static class AndType extends AbstractType {
397:
398: private static final long serialVersionUID = 400L;
399:
400: AndType() {
401: }
402:
403: public boolean isAnd() {
404: return true;
405: }
406:
407: public boolean isExists() {
408: return false;
409: }
410:
411: public boolean isNot() {
412: return false;
413: }
414:
415: public boolean isOr() {
416: return false;
417: }
418:
419: public boolean equals(final Object obj) {
420: if (!(obj instanceof AndType)) {
421: return false;
422: }
423: return true;
424: }
425:
426: public int hashCode() {
427: return 11;
428: }
429:
430: public String toString() {
431: return "AND";
432: }
433:
434: }
435:
436: /**
437: * An OR CE type
438: */
439: private static class OrType extends AbstractType {
440:
441: private static final long serialVersionUID = 400L;
442:
443: OrType() {
444: }
445:
446: public boolean isAnd() {
447: return false;
448: }
449:
450: public boolean isExists() {
451: return false;
452: }
453:
454: public boolean isNot() {
455: return false;
456: }
457:
458: public boolean isOr() {
459: return true;
460: }
461:
462: public boolean equals(final Object obj) {
463: if (!(obj instanceof OrType)) {
464: return false;
465: }
466: return true;
467: }
468:
469: public int hashCode() {
470: return 17;
471: }
472:
473: public String toString() {
474: return "OR";
475: }
476: }
477:
478: /**
479: * A NOT CE type
480: */
481: private static class NotType extends AbstractType {
482:
483: private static final long serialVersionUID = 400L;
484:
485: NotType() {
486: }
487:
488: public boolean isAnd() {
489: return false;
490: }
491:
492: public boolean isExists() {
493: return false;
494: }
495:
496: public boolean isNot() {
497: return true;
498: }
499:
500: public boolean isOr() {
501: return false;
502: }
503:
504: /**
505: * @inheritDoc
506: */
507: public Map getOuterDeclarations(final List children) {
508: return Collections.EMPTY_MAP;
509: }
510:
511: public boolean equals(final Object obj) {
512: if (!(obj instanceof NotType)) {
513: return false;
514: }
515: return true;
516: }
517:
518: public int hashCode() {
519: return 23;
520: }
521:
522: public String toString() {
523: return "NOT";
524: }
525: }
526:
527: /**
528: * An EXISTS CE type
529: */
530: private static class ExistsType extends AbstractType {
531:
532: private static final long serialVersionUID = 400L;
533:
534: ExistsType() {
535: }
536:
537: public boolean isAnd() {
538: return false;
539: }
540:
541: public boolean isExists() {
542: return true;
543: }
544:
545: public boolean isNot() {
546: return false;
547: }
548:
549: public boolean isOr() {
550: return false;
551: }
552:
553: /**
554: * @inheritDoc
555: */
556: public Map getOuterDeclarations(final List children) {
557: return Collections.EMPTY_MAP;
558: }
559:
560: public boolean equals(final Object obj) {
561: if (!(obj instanceof ExistsType)) {
562: return false;
563: }
564: return true;
565: }
566:
567: public int hashCode() {
568: return 31;
569: }
570:
571: public String toString() {
572: return "EXISTS";
573: }
574: }
575:
576: }
|