001: /**
002: * Copyright 2008 Jens Dietrich Licensed under the Apache License, Version 2.0 (the "License");
003: * you may not use this file except in compliance with the License.
004: * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
005: * Unless required by applicable law or agreed to in writing, software distributed under the
006: * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
007: * either express or implied. See the License for the specific language governing permissions
008: * and limitations under the License.
009: */package nz.org.take.nscript;
010:
011: /**
012: * Script parser. Stateful, instances should not be shared.
013: * @author <a href="http://www-ist.massey.ac.nz/JBDietrich/">Jens Dietrich</a>
014: */
015:
016: import java.beans.BeanInfo;
017: import java.beans.Introspector;
018: import java.beans.PropertyDescriptor;
019: import java.io.IOException;
020: import java.io.LineNumberReader;
021: import java.io.Reader;
022: import java.lang.reflect.Method;
023: import java.lang.reflect.Modifier;
024: import java.util.ArrayList;
025: import java.util.BitSet;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.List;
030: import java.util.Map;
031: import java.util.StringTokenizer;
032: import java.util.Map.Entry;
033: import java.util.regex.Pattern;
034: import nz.org.take.AbstractPredicate;
035: import nz.org.take.AggregationFunction;
036: import nz.org.take.Annotatable;
037: import nz.org.take.AnnotationKeys;
038: import nz.org.take.ComplexTerm;
039: import nz.org.take.Constant;
040: import nz.org.take.DefaultKnowledgeBase;
041: import nz.org.take.DerivationRule;
042: import nz.org.take.ExternalFactStore;
043: import nz.org.take.Fact;
044: import nz.org.take.JPredicate;
045: import nz.org.take.KnowledgeBase;
046: import nz.org.take.Predicate;
047: import nz.org.take.Prerequisite;
048: import nz.org.take.PropertyPredicate;
049: import nz.org.take.Query;
050: import nz.org.take.SimplePredicate;
051: import nz.org.take.Term;
052: import nz.org.take.Variable;
053:
054: public class Parser extends ParserSupport {
055: public static final String _NAME1 = "[a-zA-Z][a-zA-Z0-9_]*"; // names for types
056: public static final String _NAME2 = "[a-zA-Z_][a-zA-Z0-9_]*"; // names for var and ref declarations
057: public static final Pattern TYPE_NAME = Pattern.compile(_NAME1
058: + "(\\." + _NAME1 + ")*");
059: public static final Pattern NAME = Pattern.compile(_NAME2);
060: public static final Pattern LIST_OF_NAMES = Pattern.compile(_NAME2
061: + "([\\s]*\\,[\\s]*" + _NAME2 + ")*");
062: public static final Pattern IMPORT = Pattern.compile("import[\\s]+"
063: + _NAME2 + "(\\." + _NAME2 + ")*(\\.\\*)*[\\s]*[;]?[\\s]*");
064: public static final Pattern COMMENT = Pattern.compile("//(.)*");
065: public static final Pattern GLOBAL_ANNOTATION = Pattern
066: .compile("@@(.)+=(.)+");
067: public static final Pattern LOCAL_ANNOTATION = Pattern
068: .compile("@[^@](.)*=(.)+");
069: public static final Pattern EXTERNAL = Pattern
070: .compile("external[\\s]+[a-zA-Z_][a-zA-Z0-9_]*:[\\s]+[a-zA-Z_][a-zA-Z0-9_]*\\[(.)*\\]");
071: public static final Pattern AGGREGATION = Pattern
072: .compile("aggregation[\\s]+(.)*\\[(.)*\\]"); // not very precise, needs improvement
073: public static final Pattern QUERY = Pattern
074: .compile("(not[\\s]+)?"
075: + _NAME2
076: + "[\\s]*\\[[\\s]*(in|out)([\\s]*,[\\s]*(in|out))*[\\s]*\\]");
077: public static final Pattern RULE = Pattern.compile(_NAME1
078: + ":[\\s]+if[\\s]+(.)*");
079: public static final Pattern FACT = Pattern
080: .compile(_NAME1 + ":(.)*");
081: public static final Pattern CONDITION1 = Pattern.compile(_NAME2
082: + "\\[(.)*\\]");
083: public static final Pattern STRING_LITERAL = Pattern
084: .compile("\'(.)*\'"); // TODO handle escaped quotes
085: private ClassLoader classLoader = Parser.class.getClassLoader();
086: private KnowledgeBase kb = null;
087: private Map<String, Variable> variables = new HashMap<String, Variable>();
088: private Map<String, Constant> constants = new HashMap<String, Constant>();
089: private Map<String, AggregationFunction> aggregationFunctions = new HashMap<String, AggregationFunction>();
090: private Map<String, String> localAnnotations = new HashMap<String, String>();
091: private List<QuerySpec> querySpecs = new ArrayList<QuerySpec>();
092: private Map<String, Predicate> predicatesByName = new HashMap<String, Predicate>();
093: private Map<SimplePredicate, SimplePredicate> predicates = new HashMap<SimplePredicate, SimplePredicate>(); // use map for simple lookup with get
094: private List<String> importedClasses = new ArrayList<String>();
095: private List<String> importedPackages = new ArrayList<String>();
096: private List<ScriptException> issues = null;
097: private Collection<String> ids = new HashSet<String>();
098: boolean verificationMode = false;
099: private JSPELParser elParser = new JSPELParser(variables,
100: constants, aggregationFunctions);
101: private AnnotationPropagationPolicy annotationPolicy = AnnotationPropagationPolicy.ALL;
102:
103: public Parser() {
104: super ();
105: this .importedPackages.add("java.lang");
106: }
107:
108: public List<ScriptException> check(Reader reader)
109: throws ScriptException {
110: verificationMode = true;
111: this .issues = new ArrayList<ScriptException>();
112: parse(reader);
113: return this .issues;
114: }
115:
116: public KnowledgeBase parse(Reader reader) throws ScriptException {
117: kb = new DefaultKnowledgeBase();
118: LineNumberReader bufReader = new LineNumberReader(reader);
119: String line = null;
120: try {
121: while ((line = bufReader.readLine()) != null) {
122: int no = bufReader.getLineNumber();
123: line = line.trim();
124: if (line.length() > 0) {
125: try {
126: parseLine(line, no);
127: } catch (RuntimeException x) {
128: this .error(no, x, "Exception parsing line");
129: }
130: }
131: }
132:
133: // build queries
134: buildQueries();
135:
136: } catch (IOException e) {
137: throw new ScriptException(e);
138: }
139: return kb;
140: }
141:
142: private void parseLine(String line, int no) throws ScriptException {
143:
144: if (line.startsWith("//")) {
145: // comment, don't parse
146: } else if (line.startsWith("@@")) {
147: debug("parse line ", " as global annotation: ", line);
148: parseGlobalAnnotation(line, no);
149: } else if (line.startsWith("@")) {
150: debug("parse line ", " as local annotation: ", line);
151: parseLocalAnnotation(line, no);
152: } else if (line.startsWith("import")) {
153: debug("parse line ", " as import: ", line);
154: parseImport(line, no);
155: } else if (line.startsWith("var ")) {
156: debug("parse line ", " as var declaration: ", line);
157: parseVarDeclaration(line, no);
158: } else if (line.startsWith("ref ")) {
159: debug("parse line ", " as ref declaration: ", line);
160: parseRefDeclaration(line, no);
161: } else if (line.startsWith("query ")) {
162: debug("parse line ", " as query: ", line);
163: parseQuery(line, no);
164: } else if (line.startsWith("external ")) {
165: debug("parse line ", " as external fact store: ", line);
166: parseExternalFactStore(line, no);
167: } else if (line.startsWith("aggregation ")) {
168: debug("parse line ", " as external fact store: ", line);
169: parseAggregation(line, no);
170: } else if (RULE.matcher(line).matches()) {
171: debug("parse line ", " as rule: ", line);
172: parseRule(line, no);
173: } else if (FACT.matcher(line).matches()) {
174: debug("parse line ", " as fact: ", line);
175: parseFact(line, no);
176: }
177:
178: else {
179: error(
180: no,
181: "Unable to parse this line (unknown syntax type): ",
182: line);
183: }
184:
185: }
186:
187: private void parseAggregation(String line, int no)
188: throws ScriptException {
189: check(no, line, AGGREGATION,
190: "this is not a valid aggregation declaration");
191: line = line.substring(12).trim(); // take off aggregation
192:
193: // name
194: int sep = line.indexOf('='); // separate function name
195: String name = line.substring(0, sep).trim();
196: line = line.substring(sep + 1).trim();
197: AggregationFunction f = new AggregationFunction();
198: f.setName(name);
199:
200: // kind of aggregation
201: sep = line.indexOf(' '); // separate function name
202: String aggFunction = line.substring(0, sep);
203: line = line.substring(sep + 1).trim();
204: if (aggFunction.equals("avg")) {
205: f.setAggregation(nz.org.take.Aggregations.AVG);
206: } else if (aggFunction.equals("sum")) {
207: f.setAggregation(nz.org.take.Aggregations.SUM);
208: } else if (aggFunction.equals("count")) {
209: f.setAggregation(nz.org.take.Aggregations.COUNT);
210: } else if (aggFunction.equals("min")) {
211: f.setAggregation(nz.org.take.Aggregations.MIN);
212: } else if (aggFunction.equals("max")) {
213: f.setAggregation(nz.org.take.Aggregations.MAX);
214: } else {
215: error(no, "Unknown aggregation function ", aggFunction);
216: }
217:
218: // variable
219: sep = line.indexOf(' ');
220: String vname = line.substring(0, sep);
221: line = line.substring(sep + 1).trim();
222: Variable var = this .variables.get(vname);
223: if (var == null)
224: this
225: .error(
226: no,
227: "The variable used in an aggregation must be declared first using the statement \"var <a type> ",
228: vname, "\"");
229: f.setVariable(var);
230:
231: Fact query = this .parseCondition(line, no, false, false);
232: f.setQuery(query);
233:
234: aggregationFunctions.put(f.getName(), f);
235: }
236:
237: private void buildQueries() throws ScriptException {
238: for (QuerySpec spec : this .querySpecs) {
239: String id = this .getId(spec);
240: Predicate p = this .predicatesByName.get(id);
241: if (p == null) {
242: this
243: .error(
244: spec.getLine(),
245: "There is no rule or fact supporting the query predicate ",
246: spec.getPredicate(), " in the script");
247: } else if (p.isNegated()) {
248: this
249: .error(
250: spec.getLine(),
251: "There is no rule or fact supporting the unnegated query predicate ",
252: spec.getPredicate(), " in the script");
253: } else {
254: Query query = new Query();
255: query.setPredicate(p);
256: boolean[] io = new boolean[spec.getIoSpec().size()];
257: for (int i = 0; i < io.length; i++) {
258: io[i] = spec.getIoSpec().get(i);
259: }
260: query.setInputParams(io);
261: query.addAnnotations(spec.getAnnotations());
262: takeOverAnnotations(query);
263: this .kb.add(query);
264: }
265: }
266: }
267:
268: private void error(int no, String line, Pattern pattern,
269: String... description) throws ScriptException {
270: StringBuffer buf = new StringBuffer();
271: buf.append("Parser exception at line ");
272: buf.append(no);
273: buf.append(' ');
274: buf.append(line);
275: buf.append(" does not match pattern ");
276: buf.append(pattern.pattern());
277: buf.append(' ');
278: for (String t : description)
279: buf.append(t);
280: error(no, buf.toString());
281: }
282:
283: private void check(int no, String txt, Pattern pattern,
284: String... errorMessage) throws ScriptException {
285: if (!pattern.matcher(txt).matches())
286: this .error(no, txt, pattern, errorMessage);
287: }
288:
289: private void parseVarDeclaration(String line, int no)
290: throws ScriptException {
291: line = line.substring(4).trim();
292: int sep = line.indexOf(' ');
293: String type = line.substring(0, sep).trim();
294: check(no, type, TYPE_NAME, "this is not a valid type name");
295: Class clazz = this .classForName(type, no);
296: String names = line.substring(sep + 1).trim();
297: String name = null;
298: check(no, names, LIST_OF_NAMES,
299: "this is not a valid (list of) name(s)");
300:
301: StringTokenizer t = new StringTokenizer(names, ",");
302: while (t.hasMoreTokens()) {
303: name = t.nextToken().trim();
304: Variable var = new Variable();
305: var.setType(clazz);
306: var.setName(name);
307: this .variables.put(name, var);
308: }
309: }
310:
311: private void parseRefDeclaration(String line, int no)
312: throws ScriptException {
313: line = line.substring(4).trim();
314: int sep = line.indexOf(' ');
315: String type = line.substring(0, sep).trim();
316: check(no, type, TYPE_NAME, "this is not a valid type name");
317: Class clazz = this .classForName(type, no);
318: String names = line.substring(sep + 1).trim();
319: String name = null;
320: check(no, names, LIST_OF_NAMES,
321: "this is not a valid (list of) name(s)");
322:
323: StringTokenizer t = new StringTokenizer(names, ",");
324: while (t.hasMoreTokens()) {
325: name = t.nextToken().trim();
326: Constant c = new Constant();
327: c.setType(clazz);
328: c.setRef(name);
329: this .constants.put(name, c);
330: }
331: }
332:
333: private void parseQuery(String line, int no) throws ScriptException {
334: line = line.substring(6).trim();
335:
336: QuerySpec query = new QuerySpec();
337: query.setLine(no);
338: this .consumeAnnotations(query);
339:
340: // TODO should we support explicit negation here?
341: if (line.startsWith("not ")) {
342: this .error(no, "Negation is not supported here");
343: //query.setNegated(true);
344: //line = line.substring(4).trim();
345: }
346:
347: int sep = line.indexOf('[');
348: String name = line.substring(0, sep).trim();
349: String sign = line.substring(sep + 1, line.length() - 1).trim();
350:
351: query.setPredicate(name);
352:
353: // parse i/o
354: StringTokenizer tok = new StringTokenizer(sign, ",");
355: while (tok.hasMoreTokens()) {
356: String token = tok.nextToken().trim();
357: query.getIoSpec().add("in".equals(token));
358: }
359:
360: this .querySpecs.add(query);
361: }
362:
363: private void parseRule(String line, int no) throws ScriptException {
364:
365: int sep = line.indexOf(":"); //take off label
366: String label = line.substring(0, sep).trim();
367: line = line.substring(sep);
368: sep = line.indexOf("if ");
369: line = line.substring(sep + 3).trim();
370: List<String> unparsedConditions = new ArrayList<String>();
371: String unparsedHead = null;
372: BitSet neg = new BitSet();
373:
374: StringBuffer b = new StringBuffer();
375:
376: List<String> tokens = Tokenizer.tokenize(line, " and ",
377: " then ");
378: unparsedHead = tokens.remove(tokens.size() - 1).trim();
379: for (int i = 0; i < tokens.size(); i++) {
380: String token = tokens.get(i).trim();
381: if (token.startsWith("not ")) {
382: token = token.substring(3).trim();
383: neg.set(i);
384: }
385: unparsedConditions.add(token);
386: }
387:
388: DerivationRule rule = new DerivationRule();
389: this .checkId(label, no);
390: rule.setId(label);
391: Fact head = this .parseCondition(unparsedHead, no, false, false);
392: rule.setHead(head);
393: for (int i = 0; i < unparsedConditions.size(); i++) {
394: Prerequisite prereq = (Prerequisite) this .parseCondition(
395: unparsedConditions.get(i), no, true, neg.get(i));
396: rule.getBody().add(prereq);
397: }
398: this .consumeAnnotations(rule);
399: this .kb.add(rule);
400:
401: }
402:
403: private void parseFact(String line, int no) throws ScriptException {
404:
405: int sep = line.indexOf(":"); //take off label
406: String label = line.substring(0, sep);
407: line = line.substring(sep + 1).trim();
408: Fact fact = this .parseCondition(line, no, false, false);
409: this .checkId(label, no);
410: // if fact contains variables, it should be added as a rule
411: // TODO: this should better be done by the compiler
412: boolean hasVars = false;
413: for (Term t : fact.getTerms()) {
414: hasVars = hasVars || (t instanceof Variable)
415: || (t instanceof ComplexTerm);
416: }
417:
418: if (hasVars) {
419: DerivationRule rule = new DerivationRule();
420: rule.setId(label);
421: rule.setHead(fact);
422: this .kb.add(rule);
423: } else {
424: fact.setId(label);
425: this .kb.add(fact);
426: }
427:
428: }
429:
430: private void parseImport(String line, int no)
431: throws ScriptException {
432: check(no, line, IMPORT, "this is not a valid import statement");
433: line = line.substring(7).trim();
434:
435: // for convenience, accept ; at the end of the line
436: if (line.endsWith(";")) {
437: line = line.substring(0, line.length() - 1).trim();
438: }
439: // for import, last import wins
440: if (line.endsWith(".*")) {
441: importedPackages.add(0, line
442: .substring(0, line.length() - 2));
443: } else {
444: importedClasses.add(0, line);
445: }
446:
447: }
448:
449: private void parseGlobalAnnotation(String line, int no)
450: throws ScriptException {
451: check(no, line, GLOBAL_ANNOTATION,
452: "this is not a valid global annotation");
453: line = line.substring(2).trim();
454: int sep = line.indexOf('=');
455: String key = line.substring(0, sep).trim();
456: String value = line.substring(sep + 1).trim();
457: this .kb.addAnnotation(key, value);
458: }
459:
460: private void parseLocalAnnotation(String line, int no)
461: throws ScriptException {
462: check(no, line, LOCAL_ANNOTATION,
463: "this is not a valid local annotation");
464: line = line.substring(1).trim();
465: int sep = line.indexOf('=');
466: String key = line.substring(0, sep).trim();
467: String value = line.substring(sep + 1).trim();
468: this .localAnnotations.put(key, value);
469: }
470:
471: private void parseExternalFactStore(String line, int no)
472: throws ScriptException {
473: check(no, line, EXTERNAL,
474: "this is not a valid external fact store");
475: line = line.substring(9).trim(); // take off external
476: int sep = line.indexOf(":"); //take off label
477: String id = line.substring(0, sep);
478: line = line.substring(sep + 1).trim();
479:
480: sep = line.indexOf('[');
481: String name = line.substring(0, sep).trim();
482: String typeNames = line.substring(sep + 1, line.length() - 1)
483: .trim();
484:
485: StringTokenizer tokenizer = new StringTokenizer(typeNames, ",");
486: List<Class> types = new ArrayList<Class>();
487: while (tokenizer.hasMoreTokens()) {
488: String typeName = tokenizer.nextToken().trim();
489: check(no, typeName, TYPE_NAME,
490: "this is not a valid type name");
491: Class clazz = this .classForName(typeName, no);
492: types.add(clazz);
493: }
494: SimplePredicate predicate = new SimplePredicate();
495: predicate.setName(name);
496: predicate.setSlotTypes(types.toArray(new Class[types.size()]));
497: String pid = this .getId(predicate);
498: Predicate existingPredicate = predicatesByName.get(pid);
499: if (existingPredicate == null) {
500: predicatesByName.put(pid, predicate);
501: } else {
502: predicate = (SimplePredicate) existingPredicate;
503: }
504: ExternalFactStore exFacts = new ExternalFactStore();
505: exFacts.setId(id);
506: exFacts.setPredicate(predicate);
507: this .consumeAnnotations(exFacts);
508:
509: this .kb.add(exFacts);
510: }
511:
512: private Class classForName(String type, int line)
513: throws ScriptException {
514:
515: if ("char".equals(type))
516: return Character.TYPE;
517: if ("byte".equals(type))
518: return Byte.TYPE;
519: if ("int".equals(type))
520: return Integer.TYPE;
521: if ("short".equals(type))
522: return Short.TYPE;
523: if ("long".equals(type))
524: return Long.TYPE;
525: if ("double".equals(type))
526: return Double.TYPE;
527: if ("float".equals(type))
528: return Float.TYPE;
529: if ("boolean".equals(type))
530: return Boolean.TYPE;
531:
532: // try whether this is a class name
533: try {
534: return this .classLoader.loadClass(type);
535: } catch (ClassNotFoundException x) {
536: }
537: // try whether there is a matching imported class
538: String xtype = "." + type;
539: for (String c : this .importedClasses) {
540: if (c.endsWith(xtype)) {
541: try {
542: return this .classLoader.loadClass(c);
543: } catch (Exception x) {
544: }
545: }
546: }
547: // try whether there is a matching imported package
548: for (String p : this .importedPackages) {
549: try {
550: return this .classLoader.loadClass(p + '.' + type);
551: } catch (Exception x) {
552: }
553: }
554: this .error(line, "Cannot load the type ", type);
555: return null;
556:
557: }
558:
559: private Fact parseCondition(String s, int no,
560: boolean isPrerequisite, boolean isNegated)
561: throws ScriptException {
562:
563: if (CONDITION1.matcher(s).matches()) {
564: int sep = -1;
565: Fact fact = isPrerequisite ? new Prerequisite()
566: : new Fact();
567: // type of the condition is predicate[terms]
568: sep = s.indexOf('[');
569: String p = s.substring(0, sep);
570:
571: String t = s.substring(sep + 1, s.length() - 1);
572: List<String> unparsedTerms = Tokenizer.tokenize(t, ",");
573: List<Term> terms = new ArrayList<Term>();
574: for (String ut : unparsedTerms) {
575: terms.add(parseTerm(ut, no));
576: }
577: Class[] types = new Class[terms.size()];
578: for (int i = 0; i < terms.size(); i++) {
579: types[i] = terms.get(i).getType();
580: }
581: Predicate predicate = this .buildPredicate(p, isNegated,
582: terms.toArray(new Term[terms.size()]), no);
583: fact.setPredicate(predicate);
584: fact.setTerms(terms.toArray(new Term[terms.size()]));
585: return fact;
586: } else {
587: if (!isPrerequisite) {
588: throw new ScriptException(
589: "Error in line "
590: + no
591: + " - the rule head must have the following form: predicate[term_1,..,term_n]");
592: } else {
593: // parse with JUEL
594: return elParser.parseCondition(s, no, isNegated);
595: }
596: }
597: }
598:
599: private Term parseTerm(String s, int line) throws ScriptException {
600: return this .elParser.parseTerm(s, line);
601: }
602:
603: private void consumeAnnotations(Annotatable a) {
604: for (String gaKey : this .kb.getAnnotations().keySet()) {
605: if (this .annotationPolicy.propagateAnnotation(gaKey)) {
606: a.addAnnotation(gaKey, this .kb.getAnnotation(gaKey));
607: }
608: }
609: a.addAnnotations(this .localAnnotations);
610: this .localAnnotations.clear();
611: }
612:
613: private void checkId(String id, int line) throws ScriptException {
614:
615: if (id == null || id.trim().length() == 0)
616: this .error(line, "element has no proper id");
617: else if (ids.contains(id))
618: this .error(line, "duplicated id ", id);
619: else
620: ids.add(id);
621: }
622:
623: // take over query annotations for the query predicate
624: private void takeOverAnnotations(Query q) {
625: Predicate p = q.getPredicate();
626: for (Entry<String, String> e : q.getAnnotations().entrySet()) {
627: String key = e.getKey();
628: p.addAnnotation(key, e.getValue());
629: if (AnnotationKeys.TAKE_GENERATE_SLOTS.equals(key)) {
630: // set slot names from annotation
631: List<String> slots = new ArrayList<String>();
632: for (StringTokenizer tok = new StringTokenizer(e
633: .getValue(), ","); tok.hasMoreTokens();) {
634: slots.add(tok.nextToken().trim());
635: }
636: if (slots.size() != p.getSlotTypes().length) {
637: // TODO log warning
638: } else {
639: String[] arr = slots.toArray(new String[slots
640: .size()]);
641: if (p instanceof AbstractPredicate)
642: ((AbstractPredicate) p).setSlotNames(arr);
643: //else TODO log warning
644: }
645: }
646: }
647: }
648:
649: private nz.org.take.Predicate buildPredicate(String name,
650: boolean negated, Term[] terms, int line)
651: throws ScriptException {
652: Predicate predicate = null;
653:
654: Method m = getMethod(name, terms);
655: PropertyDescriptor property = null;
656: if (m == null)
657: property = getProperty(name, terms[0].getType());
658:
659: if (m != null) {
660: debug("Interpreting predicate symbol ", name,
661: " as Java method ", m, " in line ", line);
662: JPredicate p = new JPredicate();
663: Class[] paramTypes = getParamTypes(terms);
664: p.setMethod(m);
665: p.setNegated(negated);
666: predicate = p;
667: } else if (property != null) {
668: debug("Interpreting predicate symbol ", name,
669: " as bean property", " in line ", line);
670: PropertyPredicate p = new PropertyPredicate();
671: p.setProperty(property);
672: p.setOwnerType(terms[0].getType());
673: p.setNegated(negated);
674: // todo remove this line that just does lazy initialization
675: predicate = p;
676: } else {
677: debug("Interpreting predicate symbol ", name,
678: " as simple predicate", " in line ", line);
679: SimplePredicate p = new SimplePredicate();
680: p.setName(name);
681: p.setNegated(negated);
682: Class[] types = new Class[terms.length];
683: for (int i = 0; i < terms.length; i++) {
684: types[i] = terms[i].getType();
685: }
686: p.setSlotTypes(types);
687: predicate = p;
688: }
689: String id = this .getId(predicate);
690: Predicate existingPredicate = predicatesByName.get(id);
691: if (existingPredicate == null) {
692: predicatesByName.put(this .getId(predicate), predicate);
693: return predicate;
694: } else
695: return existingPredicate;
696: }
697:
698: private Class[] getParamTypes(nz.org.take.Term[] terms) {
699: Class[] paramTypes = new Class[terms.length - 1];
700: for (int i = 1; i < terms.length; i++) {
701: paramTypes[i - 1] = terms[i].getType();
702: }
703: return paramTypes;
704: }
705:
706: private Method getMethod(String name, nz.org.take.Term[] terms)
707: throws ScriptException {
708: Class clazz = terms[0].getType();
709: Class[] paramTypes = new Class[terms.length - 1];
710: Method m = null;
711: for (int i = 1; i < terms.length; i++) {
712: paramTypes[i - 1] = terms[i].getType();
713: }
714: try {
715: m = clazz.getMethod(name, paramTypes);
716: } catch (Exception x) {
717: }
718: if (m == null) {
719: // start investigating supertypes
720: Method[] methods = clazz.getMethods();
721: for (Method m1 : methods) {
722: if (m1.getReturnType() == Boolean.TYPE
723: && Modifier.isPublic(m1.getModifiers())) {
724: if (m1.getName().equals(name)
725: && m1.getParameterTypes().length == paramTypes.length) {
726: // check types
727: boolean ok = true;
728: for (int i = 0; i < paramTypes.length; i++) {
729: ok = ok
730: && m1.getParameterTypes()[i]
731: .isAssignableFrom(paramTypes[i]);
732: }
733: if (ok) {
734: m = m1;
735: break;
736: }
737: }
738: }
739: }
740: }
741: return m;
742:
743: }
744:
745: private PropertyDescriptor getProperty(String name, Class clazz)
746: throws ScriptException {
747: try {
748: BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
749: PropertyDescriptor[] properties = beanInfo
750: .getPropertyDescriptors();
751: for (PropertyDescriptor property : properties) {
752: if (name.equals(property.getName())
753: && property.getReadMethod() != null) {
754: return property;
755: }
756: }
757: } catch (Exception x) {
758: }
759: return null;
760: }
761:
762: private String getId(Predicate p) {
763: return p.getName() + '_' + p.getSlotTypes().length
764: + (p.isNegated() ? "-" : "+");
765: }
766:
767: private String getId(QuerySpec q) {
768: return q.getPredicate() + '_' + q.getIoSpec().size() + "+";
769: }
770:
771: protected void error(int no, String message) throws ScriptException {
772: if (this .verificationMode) {
773: this .issues.add(new ScriptException(message, no));
774: } else {
775: super .error(no, message);
776: }
777: }
778:
779: protected void error(int no, Exception x, String message)
780: throws ScriptException {
781: if (this .verificationMode) {
782: this .issues.add(new ScriptException(message, x, no));
783: } else {
784: super .error(no, x, message);
785: }
786: }
787:
788: public ClassLoader getClassLoader() {
789: return classLoader;
790: }
791:
792: public void setClassLoader(ClassLoader classloader) {
793: this .classLoader = classloader;
794: }
795:
796: public AnnotationPropagationPolicy getAnnotationPolicy() {
797: return annotationPolicy;
798: }
799:
800: public void setAnnotationPolicy(
801: AnnotationPropagationPolicy annotationPolicy) {
802: this.annotationPolicy = annotationPolicy;
803: }
804:
805: }
|