package com.jaivox.tools; import java.io.*; import java.util.*; import com.jaivox.util.*; /** * Check an input specification and report errors. */ public class Check { Questgen quest; Properties kv; String [] names; TreeMap <String, Infonode> infos; Infonode [] specs; int N; String dataname; String datafile; String msgWP = "Fields should specify question form, such as \"What\", \"Who\" etc."; String msgELS = "Please specify how the user may ask for alternative answers, suggestion \"besides\""; String msgNN = "Please specify othe synonyms for this field"; String msgNNS = "Please specify plural forms corresponding to noun forms"; String msgNNP = "Please indicate the column of the table containing proper names"; // add a note here about the syntax? String msgJJ_P = "Please specify positive adjective forms of this attribute"; String msgJJ_N = "Please specify negative adjdtive forms of this attribute"; String msgRB = "Please specify positive and negative adverbial forms of this attribute"; String msgRBR = "Please specify the positive and negative comparative adverbial forms of this attribute"; String msgRBS = "Please specify the positive and negative superlative adverbial forms of this attribute"; /** * See if the specifications available to generate questions are given * in the correct format. @param q */ public Check (Questgen q) { quest = q; TreeMap <String, Infonode> infos = quest.infos; boolean OK = false; OK = createspecs (infos); if (!OK) { Log.severe ("Check: error in specifications, returning ..."); return; } } boolean createspecs (TreeMap <String, Infonode> inf) { try { infos = inf; Set <String> keys = infos.keySet (); N = infos.size (); names = new String [N]; specs = new Infonode [N]; int i = 0; for (Iterator<String> it = keys.iterator (); it.hasNext (); ) { String key = it.next (); Infonode info = infos.get (key); names [i] = key; specs [i] = info; i++; } if (i < N) { Log.warning ("Specifications table in Questgen counting incorrectly."); return false; } else { return true; } } catch (Exception e) { Log.severe ("Check: createspecs - "+e.toString ()); return false; } } /** * called from Questgen to check whether everything is ok before generating * questions. This function calls several other functions to check various * details of the specified data. @return */ public boolean checkAll () { boolean OK = false; OK = checkForTable (); if (!OK) return false; OK = checkFields (); if (!OK) return false; OK = checkAttributes (); return OK; } boolean checkForTable () { try { // see if there is a table and whether that exists for (int i=0; i<N; i++) { Infonode node = specs [i]; if (node.name == null) { Log.severe ("Check: No name for specification #"+i); return false; } String type = node.tagvals ("type"); if (type == null) { Log.severe ("Check: No type in specification "+node.name); return false; } if (type.equalsIgnoreCase ("table")) { boolean OK = checkTableForDetails (i, node); return OK; } } // if we came here did not find the table Log.severe ("No specification for data table, of type \"table\""); return false; } catch (Exception e) { Log.severe ("Check: checkForTable - "+e.toString ()); return false; } } boolean checkTableForDetails (int pos, Infonode node) { try { String name = node.name; // assume this is file in the source directory String specdir = quest.kv.getProperty ("source"); Log.fine ("Assumes "+name+" is a file in "+specdir); datafile = specdir + name; dataname = name; File F = new File (datafile); if (!F.exists ()) { Log.severe ("Data file "+datafile+" does not exist."); return false; } // check that the data file contains the columns that are mentioned String fields [] = quest.fields; // is it possible to have multiple fields and attributes in the same table? // fa will contain the value "" for fields and the name of the field // itself for attributes. Hashtable <String, String> fa = new Hashtable <String, String> (); for (int i=0; i<fields.length; i++) { String field = fields [i]; if (field.equals ("")) { Log.warning ("Field "+i+" is an empty string, ingored"); continue; } fa.put (field, ""); Infonode finfo = infos.get (field); String [] attrs = finfo.tagval.get ("attributes"); for (int j=0; j<attrs.length; j++) { fa.put (attrs [j], field); // check that there are nodes defined for each of these attributes Infonode atnode = infos.get (attrs [j]); if (atnode == null) { Log.severe ("Atrribute "+attrs [j]+" for field "+field+" not specified."); Log.severe ("Each attribute should have a detailed specification"); return false; } } } // get the columns of the table String columns [] = node.tagvalarray ("columns"); if (columns == null) { Log.severe ("Specifications for "+name+" missing line tagged \"columns\""); return false; } // first check if the column names are in the fa field for (int i=0; i<columns.length; i++) { String col = columns [i]; if (fa.get (col) == null) { Log.severe ("No specifications for column "+col+ " in "+datafile); return false; } } boolean OK = checkData (dataname, datafile, columns, fa); return OK; } catch (Exception e) { Log.severe ("Check: checkTableForDetails - "+e.toString ()); return false; } } boolean checkData (String dataname, String datafile, String [] columns, Hashtable <String, String> fa) { try { Log.fine ("Looking for comma separated data in "+datafile); BufferedReader in = new BufferedReader (new FileReader (datafile)); String line; int n = columns.length; boolean found = false; while ((line = in.readLine ()) != null) { if (line.trim ().length () == 0) continue; StringTokenizer st = new StringTokenizer (line, ","); int m = st.countTokens (); if (m == n) found = true; else { Log.severe ("Expected "+n+" comma separated fields in "+line); Log.severe ("Data check failed"); in.close (); return false; } for (int j=0; j<n; j++) { String token = st.nextToken ().trim (); String col = columns [j]; // checked in checkTableForDetails that the following is non null String f = fa.get (col); if (!f.equals ("")) { // it is an attribute if (!isNumeric (token)) { Log.severe ("Attribute "+col+" in line "+line+" should be numeric"); in.close (); return false; } } } } if (!found) { Log.severe ("Data file "+datafile+" does not contain "+n+" comma=separated data"); return false; } Log.info ("Data seems to be in correct format."); return true; } catch (Exception e) { Log.severe ("Check: checkData - "+e.toString ()); return false; } } boolean isNumeric (String s) { try { new Double (s).doubleValue (); return true; } catch (Exception e) { return false; } } boolean checkFields () { try { String fields [] = quest.fields; for (int i=0; i<fields.length; i++) { boolean OK = checkField (fields [i]); if (!OK) return OK; } Log.info ("Fields seem to be properly specified."); return true; } catch (Exception e) { Log.severe ("Check: checkFields - "+e.toString ()); return false; } } boolean checkField (String field) { try { // get the infonode Infonode fnode = null; for (int i=0; i<specs.length; i++) { Infonode node = specs [i]; if (node.name.equals (field)) { fnode = node; break; } } if (fnode == null) { Log.severe ("No secifications found for field "+field); return false; } // continue with checking fnode // node should have attributes // if there are attributes, we have already checked in checkTableForDetails // that they attributes are specified in detail String attrs [] = fnode.tagvalarray ("attributes"); if (attrs == null) { Log.severe ("Field "+field+" has no attributes"); return false; } // check various required grammar tags if (!checkGrammar (field, fnode, "WP", msgWP)) return false; if (!checkGrammar (field, fnode, "ELS", msgELS)) return false; if (!checkGrammar (field, fnode, "NN", msgNN)) return false; if (!checkGrammar (field, fnode, "NNS", msgNNS)) return false; if (!checkGrammar (field, fnode, "NNP", msgNNP)) return false; // NNP may need an extra check of the table Log.info ("Field "+field+" seems to be specified correctly."); return true; } catch (Exception e) { Log.severe ("Check: checkField - "+e.toString ()); return false; } } boolean checkGrammar (String head, Infonode node, String tag, String msg) { try { String vals [] = node.tagvalarray (tag); if (vals == null) { Log.severe ("Expected values for "+tag+" in specs for "+head); Log.severe (msg); return false; } Log.info ("Grammar tag "+tag+" used correctly for "+head); return true; } catch (Exception e) { Log.severe ("Check: xxx - "+e.toString ()); return false; } } boolean checkAttributes () { try { String fields [] = quest.fields; for (int i=0; i<fields.length; i++) { boolean OK = checkFieldAttributes (fields [i]); if (!OK) return OK; } Log.info ("Attributes seem to be correctly specified."); return true; } catch (Exception e) { Log.severe ("Check: checkFields - "+e.toString ()); return false; } } boolean checkFieldAttributes (String field) { try { // get the infonode Infonode fnode = null; for (int i=0; i<specs.length; i++) { Infonode node = specs [i]; if (node.name.equals (field)) { fnode = node; break; } } // already checked in checkField whether fnode is null String attrs [] = fnode.tagvalarray ("attributes"); // already checked in checkField whether attrs is null for (int i=0; i<attrs.length; i++) { if (!checkAttribute (field, attrs [i])) return false; } Log.info ("Attributes for field "+field+" seem to be correctly specified."); return true; } catch (Exception e) { Log.severe ("Check: checkFieldAttributes - "+e.toString ()); return false; } } boolean checkAttribute (String field, String attr) { try { // looks like we just have to check the various tags Infonode anode = infos.get (attr); if (anode == null) { Log.severe ("No specifications for attribute "+attr+" of field "+field); return false; } if (!checkGrammar (attr, anode, "JJ-P", msgJJ_P)) return false; if (!checkGrammar (attr, anode, "JJ-N", msgJJ_N)) return false; if (!checkGrammar (attr, anode, "RB", msgRB)) return false; if (!checkGrammar (attr, anode, "RBR", msgRBR)) return false; if (!checkGrammar (attr, anode, "RBS", msgRBS)) return false; Log.info ("Attribute "+attr+" for field "+field+" seems to be correctly specified."); return true; } catch (Exception e) { Log.severe ("Check: checkAttribute - "+e.toString ()); return false; } } }