1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
4 This file is part of GORITE.
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE. If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
20 package com.intendico.data.addon;
22 import com.intendico.data.*;
23 import com.intendico.gorite.Data;
24 import com.intendico.sdp.BNFGrammar;
25 import java.lang.reflect.Constructor;
26 import java.util.Collection;
27 import java.util.HashMap;
28 import java.util.Iterator;
30 import java.util.Vector;
33 * This class defines the textual predicate language. The detailed
34 * syntax is as follows:
37 * rule ::= <predicate> '=> <predicate>
39 * predicate ::= 'And '( <predicates> ')
40 * | 'Or '( <predicates> ')
41 * | 'Not <predicate>
42 * | 'Lost <predicate>
43 * | 'Equals '( <arguments> ')
44 * | 'Distinct '( <arguments> ')
45 * | <name> '( <arguments> ')
47 * predicates ::= <predicate> ', <predicates>
50 * arguments ::= <argument> ', <arguments>
53 * argument ::= <number>
54 * | <string> -- double-quoted
55 * | <xstring> -- single-quoted
59 * <p> Note that this lanugage allows single-quotes as well as
60 * double-quotes for strings (though the latter are called xstring in
63 * <p> The Language object includes three lookup tables: {@link
64 * #inquirables} for lookup of relations (or primitive query names),
65 * {@link #data} for translation time binding of variables, and {@link
66 * #variables} for collating and re-using same named variables.
68 * <p> Translation is not multi-thread safe.
70 public class Language extends BNFGrammar {
72 private static String SYNTAX =
73 "rule ::= <predicate> '=> <predicate> " +
76 " 'And '( <predicates> ') # AND " +
77 " | 'Or '( <predicates> ') # OR " +
78 " | 'Lost <predicate> # LOST " +
79 " | 'Not <predicate> # NOT " +
80 " | 'Equals '( <arguments> ') # EQUALS " +
81 " | 'Distinct '( <arguments> ') # DISTINCT " +
82 " | <name> '( <arguments> ') # GET " +
84 "predicates ::= <predicate> ', <predicates> # Enlist " +
85 " | <predicate> # Enlist " +
87 "arguments ::= <argument> ', <arguments> # Enlist " +
88 " | <argument> # Enlist " +
90 "argument ::= <number> # NUMBER" +
91 " | <string> # STRING " +
92 " | <xstring> # STRING " +
93 " | '$ <name> # VARIABLE " +
98 * Constant that describes the construction argument types for
99 * value queries such as {@link Equals} and {@link Distinct}.
101 private final static Class [] QUERYARGVAL =
102 new Class [] { Object[].class };
105 * Constant that describes the construction argument types for
106 * single query queries such as {@link Not} and {@link Lost}.
108 private final static Class [] QUERYARGONE =
109 new Class [] { Query.class };
112 * Constant that describes the construction argument types for
113 * multi query queries such as {@link And} and {@link Or}.
115 private final static Class [] QUERYARGMANY =
116 new Class [] { Query[].class };
119 * Base class for queries taking multiple value arguments.
121 public class ArgsQuery extends Production {
123 * Holds the target constructor for this kind of queries.
125 private Constructor cnr;
128 * Constructor for a given target class.
130 public ArgsQuery(Class c) {
132 cnr = c.getConstructor( QUERYARGVAL );
133 } catch (Exception e) {
135 throw new Error( "BUG 1" );
140 * The rule action. Create an instance of the target class for
141 * the given arguments.
143 public Object action(Vector v) {
145 v = (Vector) v.get( 0 );
146 return cnr.newInstance(
147 new Object [] { v.toArray( new Object [ v.size() ] ) } );
148 } catch (Exception e) {
150 throw new Error( "BUG 2" );
156 * Base class for queries taking multiple arguments.
158 public class ManyQuery extends Production {
160 * Holds the target constructor for this kind of queries.
162 private Constructor cnr;
165 * Constructor for a given target class.
167 public ManyQuery(Class c) {
169 cnr = c.getConstructor( QUERYARGMANY );
170 } catch (Exception e) {
172 throw new Error( "BUG 1" );
177 * The rule action. Create an instance of the target class for
178 * the given arguments.
180 public Object action(Vector v) {
182 v = (Vector) v.get( 0 );
183 return cnr.newInstance( new Object [] {
184 (Query[]) v.toArray( new Query [ v.size() ] ) } );
185 } catch (Exception e) {
187 throw new Error( "BUG 2" );
193 * Base class for queries taking a single argument.
195 public class OneQuery extends Production {
197 * Holds the target constructor for this kind of queries.
199 private Constructor cnr;
202 * Constructor for a given target class.
204 public OneQuery(Class c) {
206 cnr = c.getConstructor( QUERYARGONE );
207 } catch (Exception e) {
209 throw new Error( "BUG 1" );
214 * The rule action. Create an instance of the target class for
215 * the given argument.
217 public Object action(Vector v) {
219 return cnr.newInstance( new Object[] { (Query) v.get( 0 ) } );
220 } catch (Exception e) {
222 throw new Error( "BUG 2" );
228 * Rule action for making an {@link And} query
230 public class AND extends ManyQuery {
237 * Rule action for making an {@link Or} query
239 public class OR extends ManyQuery {
246 * Rule action for making a {@link Not} query
248 public class NOT extends OneQuery {
255 * Rule action for making a {@link Lost} query
257 public class LOST extends OneQuery {
264 * Rule action for making an {@link Equals} query
266 public class EQUALS extends ArgsQuery {
268 super( Equals.class );
273 * Rule action for making a {@link Distinct} query
275 public class DISTINCT extends ArgsQuery {
277 super( Distinct.class );
282 * Rule action for making an {@link Inquirable#get} query.
284 public class GET extends Production {
285 public Object action(Vector v) {
286 String name = (String) v.get( 0 );
288 v = (Vector) v.get( 1 );
289 Inquirable inquirable = (Inquirable) inquirables.get( name );
290 return inquirable.get( v.toArray( new Object[ v.size() ] ) );
291 } catch (Exception e) {
293 throw new Error( "Unknown Inquirable: " + name );
299 * Rule action for making a number value.
301 public class NUMBER extends Production {
303 * Makes an Integer object for the given number
305 public Object action(Vector v) {
308 i = Integer.parseInt( (String) v.get( 0 ) );
309 } catch (Exception e) {
312 return Integer.valueOf( i );
317 * Rule action for making a string value.
319 public class STRING extends Production {
321 * Makes a String object for the given string
323 public Object action(Vector v) {
329 * Rule action for making a variable. The name is first looked up
330 * in the {@link #variables} collection, and if not found, a new
331 * variable is created and added to the collection. Further, if
332 * the {@link #data} context is set and the variable is unbound,
333 * then the variable is also initialised from the equally named
334 * data element, if any.
336 public class VARIABLE extends Production {
338 * Makes a String object for the given string
340 public Object action(Vector v) {
341 String name = "$" + (String) v.get( 0 );
342 Ref x = (Ref) variables.get( name );
345 variables.put( name, x );
347 if ( data != null && x.get() == null ) {
348 Object value = data.getValue( name );
357 * Data context for pre-assignment of variables.
362 * Collection of variables.
364 private Map variables = new HashMap();
367 * Table of Inquirables.
369 private Map inquirables = new HashMap();
372 * Constructor, which installs the language.
376 addRule( new Identifier( "name" ) );
377 addRule( new QuotedString( "string", '"' ) );
378 addRule( new QuotedString( "xstring", '\'' ) );
379 addRule( new Number( "number" ) );
384 * Holds the singleton grammar.
386 private static Language grammar;
389 * Returns the singleton Language object, creating it on first
392 public static Language getLanguage() {
393 if ( grammar == null )
394 grammar = new Language();
399 * Utility method that translates a {@link String} into a {@link
400 * Query} with given collections of inquirables and variables, and
401 * data context. The inquirables are used for lookup of relation
402 * symbols, and the {@link Data} context is used for initialising
403 * variables. Variables mentioned in the {@link String} are
404 * located from the variables collection, or created as needed.
405 * The inquirables collection must define all relations that are
406 * mentioned in the {@link String}, while the variables collection
407 * and the data context may be given as null.
409 public static Query textToQuery(
410 String text,Collection/*<Inquirable>*/ inquirables,
411 Collection/*<Ref>*/ vars,Data settings) {
412 Language g = getLanguage();
413 g.inquirables = new HashMap/*<String,Inquirable>*/();
414 if ( inquirables != null ) {
415 for ( Iterator/*<Inquirable>*/ si =
416 inquirables.iterator(); si.hasNext(); ) {
417 Inquirable inquirable = (Inquirable) si.next();
418 g.inquirables.put( inquirable.getName(), inquirable );
422 g.variables = new HashMap/*<String,Ref>*/();
423 if ( vars != null ) {
424 for ( Iterator/*<Ref>*/ vi = vars.iterator(); vi.hasNext(); ) {
425 Ref variable = (Ref) vi.next();
426 g.variables.put( variable.getName(), variable );
430 return (Query) g.parseAndProcess( "predicate", text );
431 } catch (Throwable t) {
438 * Utility method that translates a {@link String} into a {@link
439 * Rule} with a given collection of inquirables. The inquirables
440 * collection must define all relations that are mentioned in the
443 public static Vector/*<Query>*/ textToRule(
444 String text,Collection/*<Inquirable>*/ inquirables) {
445 Language g = getLanguage();
446 g.inquirables = new HashMap/*<String,Inquirable>*/();
447 if ( inquirables != null ) {
448 for ( Iterator/*<Inquirable>*/ si =
449 inquirables.iterator(); si.hasNext(); ) {
450 Inquirable inquirable = (Inquirable) si.next();
451 g.inquirables.put( inquirable.getName(), inquirable );
454 g.variables = new HashMap/*<String,Ref>*/();
457 return (Vector/*<Query>*/) g.parseAndProcess( "rule", text );
458 } catch (Throwable t) {