001: /*
002: * Copyright (C) 1999-2004 <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</a>
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018: package org.mandarax.kernel.meta;
019:
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Modifier;
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.Vector;
025:
026: import org.apache.commons.collections.iterators.SingletonIterator;
027: import org.mandarax.kernel.AbstractPropertiesSupport;
028: import org.mandarax.kernel.CachedClauseSet;
029: import org.mandarax.kernel.Clause;
030: import org.mandarax.kernel.ClauseSetChangeEvent;
031: import org.mandarax.kernel.ClauseSetChangeListener;
032: import org.mandarax.kernel.ConstantTerm;
033: import org.mandarax.kernel.Fact;
034: import org.mandarax.kernel.LogicFactory;
035:
036: /**
037: * Abstract clause set based on the reflect mechanism. The idea is to consider java methods with a boolean
038: * return type as predicates and to analyse objects in order to build facts for tupels of objects
039: * where the invokation of the method with these obects as parameter yields true.
040: * Note that there is a boolean property to enable a cache mechanism. If the cache is switched off
041: * (the default), any call to <code>clauses()</code> builds a new clause iterator and therefore may
042: * perform a new fetch operation. Otherwise the result
043: * is stored and the clause set will not note further changes in the object model.
044: * There is also a flag to negate the clause set. If this flag is false (the default), a fact
045: * is built from the objects where invokation of the method (the JPredicate) returns <code>true</code>.
046: * If the set is negated, then a fact is built from objects where the invokation returns <code>false</code>;
047: * @author <A href="http://www-ist.massey.ac.nz/JBDietrich" target="_top">Jens Dietrich</A>
048: * @version 3.4 <7 March 05>
049: * @since 1.0
050: * @todo replace clauses(Clauses,Object) ! parameters are only passed with the constructor in the subclass!!
051: * @version 3.4 <7 March 05>
052: */
053: abstract class AbstractClauseSet extends AbstractPropertiesSupport
054: implements CachedClauseSet {
055:
056: protected boolean cacheEnabled = false;
057: protected Method method = null;
058: protected JPredicate predicate = null;
059: protected Collection cache = null;
060: protected boolean negated = false;
061: protected transient Vector clauseSetChangeListener = new Vector();
062:
063: /**
064: * Constructor.
065: */
066: protected AbstractClauseSet() {
067: super ();
068: }
069:
070: /**
071: * Constructor.
072: * @param isNegated true if the set should be negated, false otherwise
073: */
074: protected AbstractClauseSet(boolean isNegated) {
075: this ();
076:
077: negated = isNegated;
078: }
079:
080: /**
081: * Add a clause set listener.
082: * @param l a listener
083: */
084: public void addClauseSetChangeListener(ClauseSetChangeListener l) {
085: clauseSetChangeListener.add(l);
086: }
087:
088: /**
089: * Build a fact.
090: * @return a fact
091: * @param target the object receiving the metod call
092: * @param parameters the parameters of the method call
093: */
094: protected Clause buildFact(Object target, Object[] parameters) {
095: try {
096: LogicFactory logicFactory = LogicFactory
097: .getDefaultFactory();
098: Object result = method.invoke(target, parameters);
099:
100: if (((Boolean) result).booleanValue() != negated) {
101:
102: // build terms
103: ConstantTerm[] terms = new ConstantTerm[parameters.length + 1];
104:
105: terms[0] = logicFactory.createConstantTerm(target);
106:
107: for (int i = 0; i < parameters.length; i++) {
108: terms[i + 1] = logicFactory
109: .createConstantTerm(parameters[i]);
110: }
111:
112: // build and return the fact
113: Fact aFact = logicFactory.createFact(predicate, terms);
114:
115: return aFact;
116: }
117: } catch (Throwable t) {
118: System.err
119: .println("Error building fact: " + t.getMessage());
120: }
121:
122: return null;
123: }
124:
125: /**
126: * Build the collection of facts.
127: * @return a collection of facts
128: */
129: protected abstract Collection buildFacts();
130:
131: /**
132: * Check the method.
133: * @param aMethod a method
134: * @throws java.lang.IllegalAccessException Thrown if the return type of the method is not boolean or if the number of parameters does not equal 1.
135: * @throws java.lang.IllegalArgumentException Thrown if the method is not public.
136: */
137: protected void checkMethod(Method aMethod)
138: throws NullPointerException, IllegalAccessException,
139: IllegalArgumentException {
140: if (aMethod.getParameterTypes().length != 1) {
141: throw new IllegalArgumentException(
142: "The number of parameter sets does not fit");
143: }
144:
145: if (!Modifier.isPublic(aMethod.getModifiers())) {
146: throw new IllegalAccessException("Public method expected");
147: }
148:
149: if (aMethod.getReturnType() != Boolean.TYPE) {
150: throw new IllegalArgumentException(
151: "Method returning a boolean expected");
152: }
153: }
154:
155: /**
156: * Get an iterator for clauses.
157: * @return a clause iterator
158: */
159: public org.mandarax.util.ClauseIterator clauses() {
160: Collection coll = null;
161:
162: if (isCacheEnabled()) {
163: if (cache == null) {
164: cache = buildFacts();
165: }
166:
167: coll = cache;
168: } else {
169: coll = buildFacts();
170: }
171:
172: return new org.mandarax.util.SingleClauseSetIterator(coll);
173: }
174:
175: /**
176: * Get an iterator for clauses. The parameters are ignored and <code>clauses()</code> is called!
177: * @return a clause iterator
178: * @param query the query clause
179: * @param additionalParameter an additional parameter
180: */
181: public org.mandarax.util.ClauseIterator clauses(Clause query,
182: Object additionalParameter) {
183: return clauses();
184: }
185:
186: /**
187: * Fire a clause set change event
188: * @param e an event
189: */
190: protected void fireClauseSetChangeEvent(ClauseSetChangeEvent e) {
191: ClauseSetChangeListener l;
192:
193: for (Iterator it = clauseSetChangeListener.iterator(); it
194: .hasNext();) {
195: l = (ClauseSetChangeListener) it.next();
196:
197: l.clauseSetChanged(e);
198: }
199: }
200:
201: /**
202: * Get a key for indexing. Here this is the predicate shared by all clauses in the set.
203: * @return the key object
204: */
205: public Object getKey() {
206: return predicate;
207: }
208:
209: /**
210: * Validate the parameters provided to set up the set.
211: * @param aMethod the method to be evaluated
212: * @throws java.lang.IllegalAccessException Thrown if the method is not public.
213: * @throws java.lang.IllegalArgumentException Thrown if the return type of the method is not boolean or if the number of argument sets does not match.
214: * @throws java.lang.NullPointerException Thrown if one of the arguments is null.
215: */
216: protected void initialize(Method aMethod)
217: throws NullPointerException, IllegalAccessException,
218: IllegalArgumentException {
219: checkMethod(aMethod);
220:
221: method = aMethod;
222: predicate = new JPredicate(aMethod, negated);
223: }
224:
225: /**
226: * Validate the parameters provided to set up the set.
227: * @param aPredicate the JPredicate
228: * @throws java.lang.IllegalAccessException Thrown if the underlying method is not public.
229: * @throws java.lang.IllegalArgumentException Thrown if the return type of the underlying method is not boolean or if the number of argument sets does not match.
230: * @throws java.lang.NullPointerException Thrown if one of the arguments is null.
231: */
232: protected void initialize(JPredicate aPredicate)
233: throws NullPointerException, IllegalAccessException,
234: IllegalArgumentException {
235: checkMethod(aPredicate.getMethod());
236:
237: method = aPredicate.getMethod();
238: predicate = aPredicate;
239: }
240:
241: /**
242: * Indicates whether the cache is enabled.
243: * @return true if the cache is enabled, false otherwise
244: */
245: public boolean isCacheEnabled() {
246: return cacheEnabled;
247: }
248:
249: /**
250: * Remove a clause set listener.
251: * @param l a listener
252: */
253: public void removeClauseSetChangeListener(ClauseSetChangeListener l) {
254: clauseSetChangeListener.remove(l);
255: }
256:
257: /**
258: * Reset the set. In particular, this includes cleaning the cache.
259: */
260: public void reset() {
261: cache = null;
262: }
263:
264: /**
265: * Enable / disable the cache property.
266: * @param flag true if the cache should be enabled and false if the cache should be disabled
267: */
268: public void setCacheEnabled(boolean flag) {
269: cacheEnabled = flag;
270: }
271:
272: /**
273: * Convert the object to a string.
274: * @return the string representation of this object
275: */
276: public String toString() {
277: return predicate.toString() + " - facts";
278: }
279:
280: /**
281: * Get an iterator iterating over the predicates contained in this clause set.
282: * @return an iterator
283: */
284: public Iterator predicates() {
285: return new SingletonIterator(predicate);
286: }
287: }
|