capture
[rrq/gorite.git] / com / intendico / data / addon / Language.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
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.
10
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.
15
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 **********************************************************************/
19
20 package com.intendico.data.addon;
21
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;
29 import java.util.Map;
30 import java.util.Vector;
31
32 /**
33  * This class defines the textual predicate language. The detailed
34  * syntax is as follows:
35  *
36  * <pre>
37  * rule ::= &lt;predicate&gt; '=&gt; &lt;predicate&gt;
38  *
39  * predicate ::= 'And '( &lt;predicates&gt; ')
40  *   | 'Or '( &lt;predicates&gt; ')
41  *   | 'Not &lt;predicate&gt;
42  *   | 'Lost &lt;predicate&gt;
43  *   | 'Equals '( &lt;arguments&gt; ')
44  *   | 'Distinct '( &lt;arguments&gt; ')
45  *   | &lt;name&gt; '( &lt;arguments&gt; ')
46  *
47  * predicates ::= &lt;predicate&gt; ', &lt;predicates&gt;
48  *   | &lt;predicate&gt;
49  *
50  * arguments ::= &lt;argument&gt; ', &lt;arguments&gt;
51  *   | &lt;argument&gt;
52  *
53  * argument ::= &lt;number&gt;
54  *   | &lt;string&gt;         -- double-quoted
55  *   | &lt;xstring&gt;        -- single-quoted
56  *   | '$ &lt;name&gt;
57  * </pre>
58  *
59  * <p> Note that this lanugage allows single-quotes as well as
60  * double-quotes for strings (though the latter are called xstring in
61  * the grammar).
62  *
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.
67  * 
68  * <p> Translation is not multi-thread safe.
69  */
70 public class Language extends BNFGrammar {
71
72     private static String SYNTAX =
73         "rule ::= <predicate> '=> <predicate> " +
74
75         "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 " +
83
84         "predicates ::= <predicate> ', <predicates> # Enlist " +
85         " | <predicate> # Enlist " +
86
87         "arguments ::= <argument> ', <arguments> # Enlist " +
88         " | <argument> # Enlist " +
89
90         "argument ::= <number> # NUMBER" +
91         " | <string> # STRING " +
92         " | <xstring> # STRING " +
93         " | '$ <name> # VARIABLE " +
94         ""
95         ;
96
97     /**
98      * Constant that describes the construction argument types for
99      * value queries such as {@link Equals} and {@link Distinct}.
100      */
101     private final static Class [] QUERYARGVAL =
102         new Class [] { Object[].class };
103
104     /**
105      * Constant that describes the construction argument types for
106      * single query queries such as {@link Not} and {@link Lost}.
107      */
108     private final static Class [] QUERYARGONE =
109         new Class [] { Query.class };
110
111     /**
112      * Constant that describes the construction argument types for
113      * multi query queries such as {@link And} and {@link Or}.
114      */
115     private final static Class [] QUERYARGMANY =
116         new Class [] { Query[].class };
117
118     /**
119      * Base class for queries taking multiple value arguments.
120      */
121     public class ArgsQuery extends Production {
122         /**
123          * Holds the target constructor for this kind of queries.
124          */
125         private Constructor cnr;
126
127         /**
128          * Constructor for a given target class.
129          */
130         public ArgsQuery(Class c) {
131             try {
132                 cnr = c.getConstructor( QUERYARGVAL );
133             } catch (Exception e) {
134                 e.printStackTrace();
135                 throw new Error( "BUG 1" );
136             }
137         }
138
139         /**
140          * The rule action. Create an instance of the target class for
141          * the given arguments.
142          */
143         public Object action(Vector v) {
144             try {
145                 v = (Vector) v.get( 0 );
146                 return cnr.newInstance(
147                     new Object [] { v.toArray( new Object [ v.size() ] ) } );
148             } catch (Exception e) {
149                 e.printStackTrace();
150                 throw new Error( "BUG 2" );
151             }
152         }
153     }
154
155     /**
156      * Base class for queries taking multiple arguments.
157      */
158     public class ManyQuery extends Production {
159         /**
160          * Holds the target constructor for this kind of queries.
161          */
162         private Constructor cnr;
163
164         /**
165          * Constructor for a given target class.
166          */
167         public ManyQuery(Class c) {
168             try {
169                 cnr = c.getConstructor( QUERYARGMANY );
170             } catch (Exception e) {
171                 e.printStackTrace();
172                 throw new Error( "BUG 1" );
173             }
174         }
175
176         /**
177          * The rule action. Create an instance of the target class for
178          * the given arguments.
179          */
180         public Object action(Vector v) {
181             try {
182                 v = (Vector) v.get( 0 );
183                 return cnr.newInstance( new Object [] {
184                         (Query[]) v.toArray( new Query [ v.size() ] ) } );
185             } catch (Exception e) {
186                 e.printStackTrace();
187                 throw new Error( "BUG 2" );
188             }
189         }
190     }
191
192     /**
193      * Base class for queries taking a single argument.
194      */
195     public class OneQuery extends Production {
196         /**
197          * Holds the target constructor for this kind of queries.
198          */
199         private Constructor cnr;
200
201         /**
202          * Constructor for a given target class.
203          */
204         public OneQuery(Class c) {
205             try {
206                 cnr = c.getConstructor( QUERYARGONE );
207             } catch (Exception e) {
208                 e.printStackTrace();
209                 throw new Error( "BUG 1" );
210             }
211         }
212
213         /**
214          * The rule action. Create an instance of the target class for
215          * the given argument.
216          */
217         public Object action(Vector v) {
218             try {
219                 return cnr.newInstance( new Object[] { (Query) v.get( 0 ) } );
220             } catch (Exception e) {
221                 e.printStackTrace();
222                 throw new Error( "BUG 2" );
223             }
224         }
225     }
226
227     /**
228      * Rule action for making an {@link And} query
229      */
230     public class AND extends ManyQuery {
231         public AND() {
232             super( And.class );
233         }
234     }
235
236     /**
237      * Rule action for making an {@link Or} query
238      */
239     public class OR extends ManyQuery {
240         public OR() {
241             super( Or.class );
242         }
243     }
244
245     /**
246      * Rule action for making a {@link Not} query
247      */
248     public class NOT extends OneQuery {
249         public NOT() {
250             super( Not.class );
251         }
252     }
253
254     /**
255      * Rule action for making a {@link Lost} query
256      */
257     public class LOST extends OneQuery {
258         public LOST() {
259             super( Lost.class );
260         }
261     }
262
263     /**
264      * Rule action for making an {@link Equals} query
265      */
266     public class EQUALS extends ArgsQuery {
267         public EQUALS() {
268             super( Equals.class );
269         }
270     }
271
272     /**
273      * Rule action for making a {@link Distinct} query
274      */
275     public class DISTINCT extends ArgsQuery {
276         public DISTINCT() {
277             super( Distinct.class );
278         }
279     }
280
281     /**
282      * Rule action for making an {@link Inquirable#get} query.
283      */
284     public class GET extends Production {
285         public Object action(Vector v) {
286             String name = (String) v.get( 0 );
287             try {
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) {
292                 e.printStackTrace();
293                 throw new Error( "Unknown Inquirable: " + name );
294             }
295         }
296     }
297
298     /**
299      * Rule action for making a number value.
300      */
301     public class NUMBER extends Production {
302         /**
303          * Makes an Integer object for the given number
304          */
305         public Object action(Vector v) {
306             int i = 0;
307             try {
308                 i = Integer.parseInt( (String) v.get( 0 ) );
309             } catch (Exception e) {
310                 e.printStackTrace();
311             }
312             return Integer.valueOf( i );
313         }
314     }
315
316     /**
317      * Rule action for making a string value.
318      */
319     public class STRING extends Production {
320         /**
321          * Makes a String object for the given string
322          */
323         public Object action(Vector v) {
324             return v.get( 0 );
325         }
326     }
327
328     /**
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.
335      */
336     public class VARIABLE extends Production {
337         /**
338          * Makes a String object for the given string
339          */
340         public Object action(Vector v) {
341             String name = "$" + (String) v.get( 0 );
342             Ref x = (Ref) variables.get( name );
343             if ( x == null ) {
344                 x = new Ref( name );
345                 variables.put( name, x );
346             }
347             if ( data != null && x.get() == null ) {
348                 Object value = data.getValue( name );
349                 if ( value != null )
350                     x.set( value );
351             }
352             return x;
353         }
354     }
355
356     /**
357      * Data context for pre-assignment of variables.
358      */
359     private Data data;
360
361     /**
362      * Collection of variables.
363      */
364     private Map variables = new HashMap();
365
366     /**
367      * Table of Inquirables.
368      */
369     private Map inquirables = new HashMap();
370
371     /**
372      * Constructor, which installs the language.
373      */
374     public Language() {
375         install( SYNTAX );
376         addRule( new Identifier( "name" ) );
377         addRule( new QuotedString( "string", '"' ) );
378         addRule( new QuotedString( "xstring", '\'' ) );
379         addRule( new Number( "number" ) );
380         
381     }
382
383     /**
384      * Holds the singleton grammar.
385      */
386     private static Language grammar;
387
388     /**
389      * Returns the singleton Language object, creating it on first
390      * call.
391      */
392     public static Language getLanguage() {
393         if ( grammar == null )
394             grammar = new Language();
395         return grammar;
396     }
397
398     /**
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.
408      */
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 );
419             }
420         }
421         g.data = settings;
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 );
427             }
428         }
429         try {
430             return (Query) g.parseAndProcess( "predicate", text );
431         } catch (Throwable t) {
432             t.printStackTrace();
433             return null;
434         }
435     }
436
437     /**
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
441      * {@link String}
442      */
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 );
452             }
453         }
454         g.variables = new HashMap/*<String,Ref>*/();
455         g.data = null;
456         try {
457             return (Vector/*<Query>*/) g.parseAndProcess( "rule", text );
458         } catch (Throwable t) {
459             t.printStackTrace();
460             return null;
461         }
462     }
463
464 }