Changeset 24856 for main/trunk


Ignore:
Timestamp:
2011-12-06T11:03:45+13:00 (12 years ago)
Author:
sjm84
Message:

Reformmating this file ahead of some changes

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractGS2FieldSearch.java

    r24394 r24856  
    1818package org.greenstone.gsdl3.service;
    1919
    20 
    2120// Greenstone classes
    2221import org.greenstone.gsdl3.util.*;
     
    2423// XML classes
    2524import org.w3c.dom.Document;
    26 import org.w3c.dom.Element; 
     25import org.w3c.dom.Element;
    2726import org.w3c.dom.NodeList;
    2827
     
    3837import org.apache.log4j.*;
    3938
    40 
    41 abstract public class AbstractGS2FieldSearch
    42     extends AbstractGS2TextSearch
     39abstract public class AbstractGS2FieldSearch extends AbstractGS2TextSearch
    4340{
    4441
    45     // extra services offered by mgpp collections
    46     protected static final String FIELD_QUERY_SERVICE = "FieldQuery";
    47     protected static final String ADVANCED_FIELD_QUERY_SERVICE = "AdvancedFieldQuery";
    48 
    49     // extra parameters used
    50     protected static final String LEVEL_PARAM = "level";
    51     protected static final String RANK_PARAM = "sortBy";
    52     protected static final String RANK_PARAM_RANK = "1";
    53     protected static final String RANK_PARAM_NONE = "0";
    54     protected static final String SIMPLE_FIELD_PARAM = "simpleField";
    55     protected static final String ADVANCED_FIELD_PARAM = "complexField";
    56 
    57     // more params for field query
    58     protected static final String FIELD_QUERY_PARAM = "fqv";
    59     protected static final String FIELD_STEM_PARAM = "fqs";
    60     protected static final String FIELD_CASE_PARAM = "fqc";
    61     protected static final String FIELD_ACCENT_PARAM="fqa";
    62     protected static final String FIELD_FIELD_PARAM = "fqf";
    63     protected static final String FIELD_COMBINE_PARAM = "fqk";
    64     protected static final String FIELD_COMBINE_PARAM_AND = "0";
    65     protected static final String FIELD_COMBINE_PARAM_OR = "1";
    66     protected static final String FIELD_COMBINE_PARAM_NOT = "2";
    67 
    68     // some stuff for config files
    69     protected static final String SEARCH_TYPE_ELEM = "searchType";
    70     protected static final String SEARCH_TYPE_PLAIN = "plain";
    71     protected static final String SEARCH_TYPE_SIMPLE_FORM = "simpleform";
    72     protected static final String SEARCH_TYPE_ADVANCED_FORM = "advancedform";
    73 
    74     protected static final String DEFAULT_LEVEL_ELEM = "defaultLevel";
    75     protected static final String DEFAULT_DB_LEVEL_ELEM = "defaultDBLevel";
    76     protected static final String LEVEL_ELEM = "level";
    77     protected static final String FIELD_ATT = "field";
    78 
    79 
    80     protected static final int TEXT_QUERY = 0;
    81     protected static final int SIMPLE_QUERY = 1;
    82     protected static final int ADVANCED_QUERY = 2;
    83 
    84     protected String AND_OPERATOR = "&";
    85     protected String OR_OPERATOR = "|";
    86     protected String NOT_OPERATOR = "!";
    87    
    88    // the default level for searching
    89     protected String default_level=null;
    90     // default level for collection db
    91     protected String default_db_level = null;
    92     // which search services will we offer??
    93     protected boolean plain_search = false;
    94     protected boolean simple_form_search = false;
    95     protected boolean advanced_form_search = false;
    96 
    97 
    98     static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractGS2FieldSearch.class.getName());
    99 
    100     /** constructor */
    101     public AbstractGS2FieldSearch()
    102     {
    103     }
    104 
    105     public void cleanUp() {
    106     super.cleanUp();
    107     }
    108 
    109     /** configure this service */
    110     public boolean configure(Element info, Element extra_info)
    111     {
    112     if (!super.configure(info, extra_info)){
    113         return false;
    114     }
    115      
    116     // Get the default level out of <defaultLevel> (buildConfig.xml)
    117     Element def = (Element) GSXML.getChildByTagName(info, DEFAULT_LEVEL_ELEM);
    118     if (def != null) {
    119         this.default_level = def.getAttribute(GSXML.SHORTNAME_ATT);
    120     }
    121     if (this.default_level == null || this.default_level.equals("")) {
    122         logger.error("default level not specified!, assuming Doc");
    123         this.default_level = "Doc";
    124     }
    125 
    126     // Get the default DB level
    127     def = (Element) GSXML.getChildByTagName(info, DEFAULT_DB_LEVEL_ELEM);
    128     if (def != null) {
    129         this.default_db_level = def.getAttribute(GSXML.SHORTNAME_ATT);
    130     }
    131     if (this.default_db_level == null || this.default_db_level.equals("")) {
    132         logger.error("default database level (defaultDBLevel) not specified!, assuming Sec");
    133         this.default_db_level = "Sec";
    134     }
    135    
    136     // get stuff from from extra info (which is collectionConfig.xml)
    137     if (extra_info !=null) {
    138 
    139       // the search element
    140       Element config_search = (Element)GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
    141      
    142       NodeList search_types = config_search.getElementsByTagName(SEARCH_TYPE_ELEM);
    143       if (search_types == null) {
    144         // none specified, assume plain only
    145         this.plain_search = true;
    146       }
    147       else {
    148         for (int i=0; i<search_types.getLength(); i++) {
    149           Element t = (Element)search_types.item(i);
    150           String type_name = t.getAttribute(GSXML.NAME_ATT);
    151           if (type_name.equals(SEARCH_TYPE_PLAIN)) {
    152         this.plain_search = true;
    153           } else if (type_name.equals(SEARCH_TYPE_SIMPLE_FORM)) {
    154         this.simple_form_search = true;
    155           } else if (type_name.equals(SEARCH_TYPE_ADVANCED_FORM)) {
    156         this.advanced_form_search = true;
    157           }
    158         }
    159       }
    160      
    161       // AbstractGS2TextSearch has set up the TextQuery service, but we may not want it
    162       if (!this.plain_search) {
    163         // need to remove the TextQuery service
    164         Element tq_service = GSXML.getNamedElement(short_service_info, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, QUERY_SERVICE);
    165         short_service_info.removeChild(tq_service);
    166        
    167       }
    168 
    169         Document owner = info.getOwnerDocument();
    170    
    171         NodeList levels = info.getElementsByTagName(GSXML.LEVEL_ELEM);
    172        
    173        
    174         for (int i=0; i<levels.getLength();i++) {
    175         Element lev = (Element)levels.item(i);
    176         String name = lev.getAttribute(GSXML.NAME_ATT);
    177         Element node_extra = GSXML.getNamedElement(config_search,
    178                                GSXML.LEVEL_ELEM,
    179                                GSXML.NAME_ATT,
    180                                name);
    181         if (node_extra == null) {
    182             logger.error("haven't found extra info for level named "+name);
    183             continue;
    184         }
    185        
    186         // get the display elements if any - displayName
    187         NodeList display_names = node_extra.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
    188         if (display_names !=null) {
    189             for (int j=0; j<display_names.getLength(); j++) {
    190             Element e = (Element)display_names.item(j);
    191             lev.appendChild(owner.importNode(e, true));
    192             }
    193         }
    194         } // for each level
    195     } else {
    196       // for soem reason we don't have the collectionConfig file. assume plain only
    197       this.plain_search = true;
    198     }
    199    
    200    
    201     // the format info is the same for all services
    202     Element format_info = (Element)format_info_map.get(QUERY_SERVICE);
    203    
    204     // set up the extra services which are available for this collection
    205     if (this.simple_form_search) {
    206         // set up short_service_info_ - for now just has id and type - name will be added in on the fly
    207         Element fq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
    208         fq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
    209         fq_service.setAttribute(GSXML.NAME_ATT, FIELD_QUERY_SERVICE);
    210         this.short_service_info.appendChild(fq_service);
    211 
    212         if (format_info != null) {
    213         this.format_info_map.put(FIELD_QUERY_SERVICE, format_info);
    214         }
    215     }
    216    
    217     if (this.advanced_form_search) {
    218         Element afq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
    219         afq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
    220         afq_service.setAttribute(GSXML.NAME_ATT, ADVANCED_FIELD_QUERY_SERVICE);
    221         this.short_service_info.appendChild(afq_service);
    222        
    223         if (format_info != null) {
    224         this.format_info_map.put(ADVANCED_FIELD_QUERY_SERVICE, format_info);
    225         }
    226     }
    227 
    228     return true;
    229     }
    230 
    231 
    232     protected Element getServiceDescription(String service_id, String lang, String subset) {
    233     // should we check that the service is actually on offer? presumably we wont get asked for services that we haven't advertised previously.
    234    
    235     if (!service_id.equals(FIELD_QUERY_SERVICE) && !service_id.equals(ADVANCED_FIELD_QUERY_SERVICE)) {
    236         return super.getServiceDescription(service_id, lang, subset);
    237     }
    238    
    239    
    240     Element service = this.doc.createElement(GSXML.SERVICE_ELEM);
    241     service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
    242     service.setAttribute(GSXML.NAME_ATT, service_id);
    243     if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER)) {
    244         service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service_id+".name", lang)));
    245         service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service_id+".submit", lang)));
    246         service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service_id+".description", lang)));
    247    
    248     }
    249     if (subset == null || subset.equals(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER)) {
    250         Element param_list = this.doc.createElement(GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER); 
    251         service.appendChild(param_list);
    252         if (service_id.equals(FIELD_QUERY_SERVICE)) {
    253        
    254         addCustomQueryParams(param_list, lang);
    255         createParameter(MAXDOCS_PARAM, param_list, lang);
    256         if (!default_index_subcollection.equals("")){
    257             createParameter(INDEX_SUBCOLLECTION_PARAM,param_list, lang);
    258         }
    259         if (!default_index_language.equals("")){
    260             createParameter(INDEX_LANGUAGE_PARAM,param_list, lang);
    261             }
    262         // create a multi param for the fields etc
    263         // text box, field
    264         Element multiparam = null;
    265         Element param=null;
    266         multiparam = GSXML.createParameterDescription(this.doc, SIMPLE_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
    267         multiparam.setAttribute("occurs", "4");
    268         param_list.appendChild(multiparam);
    269        
    270         // the components
    271        
    272         createParameter(FIELD_QUERY_PARAM, multiparam,  lang);
    273         createParameter(FIELD_FIELD_PARAM, multiparam,  lang);
    274                
    275         } else {
    276         createParameter(LEVEL_PARAM, param_list,  lang);
    277         createParameter(RANK_PARAM, param_list, lang);
    278         createParameter(MAXDOCS_PARAM, param_list,  lang);
    279         if (!default_index_subcollection.equals("")){
    280             createParameter(INDEX_SUBCOLLECTION_PARAM,param_list, lang);
    281         }
    282         if (!default_index_language.equals("")){
    283             createParameter(INDEX_LANGUAGE_PARAM,param_list, lang);
    284         }
    285        
    286         // create a multi param for the fields etc
    287         // text box, stem, case, field
    288        
    289         Element multiparam = null;
    290         Element param=null;
    291        
    292         multiparam = GSXML.createParameterDescription(this.doc, ADVANCED_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
    293         multiparam.setAttribute("occurs", "4");
    294         param_list.appendChild(multiparam);
    295        
    296         createParameter(FIELD_COMBINE_PARAM, multiparam, lang);
    297         createParameter(FIELD_QUERY_PARAM, multiparam, lang);
    298         if (this.does_case) {
    299             createParameter(FIELD_CASE_PARAM, multiparam, lang);
    300         }
    301         if (this.does_stem) {
    302             createParameter(FIELD_STEM_PARAM, multiparam, lang);
    303         }
    304         if (this.does_accent) {
    305             createParameter(FIELD_ACCENT_PARAM, multiparam, lang);
    306         }
    307         createParameter(FIELD_FIELD_PARAM, multiparam, lang);   
    308        
    309         }
    310     }
    311     return service;
    312    
    313     }
    314    
    315     /** add in the level params to TextQuery */
    316     protected void addCustomQueryParams(Element param_list, String lang)
    317     {
    318     createParameter(LEVEL_PARAM, param_list, lang);
    319     super.addCustomQueryParams(param_list, lang);
    320     }
    321 
    322     /** create a param and add to the list */
    323     protected void createParameter(String name, Element param_list, String lang)
    324     {
    325     Element param = null;
    326     if (name.equals(LEVEL_PARAM)) {
    327         ArrayList level_ids = new ArrayList();
    328         ArrayList level_names = new ArrayList();
    329         getLevelData(level_ids, level_names, lang);
    330         if (level_ids.size()>1) {
    331         // the first one is the default
    332         //param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param."+LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)level_ids.get(0), level_ids, level_names);
    333         param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param."+LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, this.default_level, level_ids, level_names);
    334         } else {
    335         // we need to set the level, but hidden, in case there is an invalid level saved
    336         //param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, (String)level_ids.get(0), null, null);
    337         param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, this.default_level, null, null);
    338         }
    339     } else if (name.equals(RANK_PARAM)) {
    340         String [] vals1 = {RANK_PARAM_RANK, RANK_PARAM_NONE };
    341         String [] vals1_texts = { getTextString("param."+RANK_PARAM+"."+RANK_PARAM_RANK, lang), getTextString("param."+RANK_PARAM+"."+RANK_PARAM_NONE, lang)};
    342        
    343         param = GSXML.createParameterDescription(this.doc, RANK_PARAM, getTextString("param."+RANK_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, RANK_PARAM_RANK, vals1, vals1_texts );
    344        
    345     } else if (name.equals(FIELD_QUERY_PARAM)) {
    346         param = GSXML.createParameterDescription(this.doc, FIELD_QUERY_PARAM, getTextString("param."+FIELD_QUERY_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
    347        
    348        
    349     } else if (name.equals(FIELD_CASE_PARAM) || name.equals(FIELD_STEM_PARAM) || name.equals(FIELD_ACCENT_PARAM)) {
    350         String[] bool_ops = {"0", "1"};
    351         String[] bool_texts = {getTextString("param.boolean.off", lang, "AbstractTextSearch"),getTextString("param.boolean.on", lang, "AbstractTextSearch")};
    352         param = GSXML.createParameterDescription(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_BOOLEAN, BOOLEAN_PARAM_ON, bool_ops, bool_texts);
    353        
    354     } else if (name.equals(FIELD_FIELD_PARAM)) {
    355         ArrayList fields = new ArrayList();
    356         ArrayList field_names = new ArrayList();
    357         getIndexData(fields, field_names, lang);
    358         // the field list -  read from config file
    359        
    360         // Fix for http://trac.greenstone.org/ticket/245 "java crash, index out of bounds"
    361         // org.greenstone.gsdl3.service.AbstractGS2FieldSearch.createParameter(AbstractGS2FieldSearch.java:362)
    362         // Changed from:
    363         // param = GSXML.createParameterDescription2(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)fields.get(0), fields, field_names );
    364         String default_value = (fields.size() > 0) ? (String)fields.get(0) : null;
    365               // don't want to access element 0 if fields.size()==0, and
    366               // GSXML.createParameterDescription2 checks for default_value==null condition
    367         param = GSXML.createParameterDescription2(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, default_value, fields, field_names );
    368        
    369     } else if (name.equals(FIELD_COMBINE_PARAM)) {
    370        
    371         String []vals = {FIELD_COMBINE_PARAM_AND, FIELD_COMBINE_PARAM_OR, FIELD_COMBINE_PARAM_NOT};
    372         String []val_texts = {getTextString("param."+FIELD_COMBINE_PARAM+"."+FIELD_COMBINE_PARAM_AND, lang), getTextString("param."+FIELD_COMBINE_PARAM+"."+FIELD_COMBINE_PARAM_OR, lang), getTextString("param."+FIELD_COMBINE_PARAM+"."+FIELD_COMBINE_PARAM_NOT, lang)};
    373        
    374        
    375         param = GSXML.createParameterDescription(this.doc, FIELD_COMBINE_PARAM, "", GSXML.PARAM_TYPE_ENUM_SINGLE, FIELD_COMBINE_PARAM_AND, vals, val_texts);
    376         param.setAttribute(GSXML.PARAM_IGNORE_POS_ATT, "0");
    377     }
    378 
    379     if (param != null) {
    380         param_list.appendChild(param);
    381     } else {
    382         super.createParameter(name, param_list, lang);
    383     }
    384     }
    385    
    386     // should cache some of this
    387     protected void getLevelData(ArrayList level_ids, ArrayList level_names, String lang)
    388     {
    389     Element level_list = (Element)GSXML.getChildByTagName(this.config_info, LEVEL_ELEM+GSXML.LIST_MODIFIER);
    390     NodeList levels = level_list.getElementsByTagName(LEVEL_ELEM);
    391     for (int i=0; i<levels.getLength(); i++) {
    392         Element level = (Element)levels.item(i);
    393         String shortname = level.getAttribute(GSXML.SHORTNAME_ATT);
    394         if (shortname.equals("")) {
    395         continue;
    396         }
    397         level_ids.add(shortname);
    398         String display_name = GSXML.getDisplayText(level, GSXML.DISPLAY_TEXT_NAME, lang, "en");
    399         if (display_name.equals("")) {
    400         // we'll use the name, and the dictionary
    401         display_name = level.getAttribute(GSXML.NAME_ATT);
    402         if (display_name.equals("")) {
    403             display_name = shortname;
    404         } else {
    405             display_name = getTextString("level."+display_name, lang);
    406         }
    407         }
    408         level_names.add(display_name);
    409     }
    410     }
    411 
    412     // the following three functions are needed so the base class can
    413     // call the process+SERVICE_NAME methods
    414     /** process a text query */
    415     protected Element processTextQuery(Element request) {
    416     return processAnyQuery(request, TEXT_QUERY);
    417     }
    418 
    419     /** process a field query */   
    420     protected Element processFieldQuery(Element request) {
    421     return processAnyQuery(request, SIMPLE_QUERY);
    422     }
    423 
    424     /** process an advanced field query */
    425     protected Element processAdvancedFieldQuery(Element request) {
    426     return processAnyQuery(request, ADVANCED_QUERY);
    427     }
    428    
    429     /** process a  query */
    430     protected Element processAnyQuery(Element request, int query_type)
    431     {
    432     String service_name=null;
    433     String empty_query_test_param=null;
    434     // set up the type specific bits
    435     switch (query_type) {
    436     case TEXT_QUERY:
    437         service_name = QUERY_SERVICE;
    438         empty_query_test_param = QUERY_PARAM;
    439         break;
    440     case SIMPLE_QUERY:
    441         service_name = FIELD_QUERY_SERVICE;
    442         empty_query_test_param = FIELD_QUERY_PARAM;
    443         break;
    444     case ADVANCED_QUERY:
    445         service_name = ADVANCED_FIELD_QUERY_SERVICE;
    446         empty_query_test_param = FIELD_QUERY_PARAM;
    447         break;
    448     default:
    449         // should never get here
    450         logger.error("wrong query type!!");
    451         return null;
    452     }
    453    
    454     // Create a new (empty) result message
    455     Element result = this.doc.createElement(GSXML.RESPONSE_ELEM);
    456     result.setAttribute(GSXML.FROM_ATT, service_name);
    457     result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
    458    
    459     // Get the parameters of the request
    460     Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
    461     if (param_list == null) {
    462         logger.error("TextQuery request had no paramList.");
    463         return result;  // Return the empty result
    464     }
    465 
    466     // Process the request parameters
    467     HashMap params = GSXML.extractParams(param_list, false);
    468 
    469     // Make sure a query has been specified
    470     String query = (String) params.get(empty_query_test_param);
    471     if (query == null || query.equals("")) {
    472         return result;  // Return the empty result
    473     }
    474 
    475     // If a field hasn't been specified, use the default - for textQuery
    476     String field = (String) params.get(INDEX_PARAM);
    477     if (field == null) {
    478         field = default_index;
    479     }
    480    
    481     // set up the appropriate query system
    482     if (!setUpQueryer(params)) {
    483         return result;
    484     }
    485    
    486     // if field search, create the query string
    487     switch (query_type) {
    488     case TEXT_QUERY:
    489         query = addFieldInfo(query, field);
    490         break;
    491     case SIMPLE_QUERY:
    492         query = parseFieldQueryParams(params);
    493         break;
    494     case ADVANCED_QUERY:
    495         query = parseAdvancedFieldQueryParams(params);
    496         break;
    497     }
    498    
    499     // run the query
    500     Object query_result = runQuery(query);
    501    
    502     // build up the response
    503    
    504     // Create a metadata list to store information about the query results
    505     // should we be using metadataList? or something else?
    506     Element metadata_list = this.doc.createElement(GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
    507     result.appendChild(metadata_list);
    508 
    509     // Add a metadata element specifying the number of matching documents
    510     long totalDocs = numDocsMatched(query_result);
    511    
    512     GSXML.addMetadata(this.doc, metadata_list, "numDocsMatched", ""+totalDocs);
    513 
    514     // Create a document list to store the matching documents, and add them
    515     String [] docs = getDocIDs(query_result);
    516     String [] doc_ranks = getDocRanks(query_result);
    517 
    518     // add a metadata item to specify docs returned
    519     int docs_returned = docs.length;
    520     if (does_paging) {
    521         String maxdocs_str = (String)params.get(MAXDOCS_PARAM);
    522         if (maxdocs_str != null) {
    523         int maxdocs = Integer.parseInt(maxdocs_str);
    524         docs_returned = (maxdocs < (int)totalDocs ? maxdocs : (int)totalDocs);
    525         }
    526     }
    527     GSXML.addMetadata(this.doc, metadata_list, "numDocsReturned", ""+docs_returned);
    528 
    529     // add a metadata item to specify what actual query was done - eg if stuff was stripped out etc. and then we can use the query later, cos we don't know which parameter was the query
    530     GSXML.addMetadata(this.doc, metadata_list, "query", query);
    531     if (docs.length>0) {
    532         Element document_list = this.doc.createElement(GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
    533         result.appendChild(document_list);
    534         for (int d = 0; d < docs.length; d++) {
    535         String doc_id = internalNum2OID(docs[d]);
    536         Element doc_node = createDocNode(doc_id, doc_ranks[d]);
    537         document_list.appendChild(doc_node);
    538         }
    539     }
    540 
    541     // Create a term list to store the term information, and add it
    542     Element term_list = this.doc.createElement(GSXML.TERM_ELEM+GSXML.LIST_MODIFIER);
    543     result.appendChild(term_list);
    544     addTermInfo(term_list, params, query_result);
    545 
    546     return result;
    547    
    548     }
    549 
    550     /** methods to handle actually doing the query */
    551     /** do any initialisation of the query object */
    552     abstract protected boolean setUpQueryer(HashMap params);
    553     /** do the query */
    554     abstract protected Object runQuery(String query);
    555     /** get the total number of docs that match */
    556     abstract protected long numDocsMatched(Object query_result);
    557     /** get the list of doc ids */
    558     abstract protected String [] getDocIDs(Object query_result);
    559     /** get the list of doc ranks */
    560     abstract protected String [] getDocRanks(Object query_result);
    561     /** add in term info if available */
    562     abstract protected boolean addTermInfo(Element term_list, HashMap params,
    563                        Object query_result);
    564    
    565     /** combines all the field params into a single query
    566      * - for simple field query */
    567     /** We assume the combination (AND/OR) is done by the match param */
    568     protected String parseFieldQueryParams(HashMap params) {
    569 
    570     StringBuffer final_query = new StringBuffer(256);
    571     String text_line = (String)params.get(FIELD_QUERY_PARAM);
    572     String[] texts = text_line.split(",", -1);
    573     String field_line = (String)params.get(FIELD_FIELD_PARAM);
    574     String[] fields = field_line.split(",", -1);
    575    
    576     for (int i=0; i<texts.length; i++) {
    577         String q = texts[i].trim();
    578         if (!q.equals("")) {
    579         final_query.append(" "+addFieldInfo(q, fields[i]));
    580         }
    581     }
    582 
    583     return final_query.toString();
    584     }
    585 
    586     abstract protected String addFieldInfo(String query, String field);
    587    
    588     /** combines all the field params into a single query
    589      * - for advanced field query */   
    590     protected String parseAdvancedFieldQueryParams(HashMap params) {
    591 
    592     StringBuffer final_query = new StringBuffer(256);
    593     String text_line = (String)params.get(FIELD_QUERY_PARAM);
    594     String[] texts = text_line.split(",", -1);
    595     String field_line = (String)params.get(FIELD_FIELD_PARAM);
    596     String[] fields = field_line.split(",", -1);
    597     String [] cases = null;
    598     String [] stems = null;
    599     String [] accents = null;
    600     if (does_case) {
    601         String case_line = (String)params.get(FIELD_CASE_PARAM);
    602         if (case_line !=null)
    603         cases = case_line.split(",", -1);
    604     }
    605     if (does_stem) {
    606         String stem_line = (String)params.get(FIELD_STEM_PARAM);
    607         if (stem_line !=null)
    608         stems = stem_line.split(",", -1);
    609     }
    610     if (does_accent) {
    611         String accent_line = (String)params.get(FIELD_ACCENT_PARAM);
    612         if (accent_line !=null)
    613         accents = accent_line.split(",", -1);
    614     }
    615     String combine_line = (String)params.get(FIELD_COMBINE_PARAM);
    616     String [] combines = combine_line.split(",", -1);
    617     String combine = "";
    618     for (int i=0; i<texts.length; i++) {
    619         if (i==0) {// assume first one is blank
    620         combine = "";
    621         } else {
    622         String x = combines[i];
    623         if (x.equals(FIELD_COMBINE_PARAM_AND)) {
    624             combine = AND_OPERATOR;
    625         } else if (x.equals(FIELD_COMBINE_PARAM_OR)) {
    626             combine = OR_OPERATOR;
    627         } else if (x.equals(FIELD_COMBINE_PARAM_NOT)) {
    628             combine = NOT_OPERATOR;
    629         }
    630        
    631         }
    632 
    633         String q = texts[i].trim();
    634         boolean modified = false;
    635         if (!q.equals("")) {
    636         String c = null;
    637         String s = null;
    638         String a = null;
    639         if (does_case) {
    640             modified = true;
    641             c = cases[i];
    642         }
    643         if (does_stem) {
    644             modified = true;
    645             s = stems[i];
    646         }
    647         if (does_accent) {
    648             modified = true;
    649             a = accents[i];
    650         }
    651         if (modified) {
    652             q = addStemOptions(q, s, c, a);
    653         }
    654         addQueryElem(final_query, q, fields[i], combine);
    655         }
    656     }
    657     return final_query.toString();
    658     }
    659    
    660     abstract protected void addQueryElem(StringBuffer final_query,
    661                      String query, String field,
    662                      String combine);
    663     abstract protected String addStemOptions(String query, String stem,
    664                          String casef, String accent);
    665    
     42    // extra services offered by mgpp collections
     43    protected static final String FIELD_QUERY_SERVICE = "FieldQuery";
     44    protected static final String ADVANCED_FIELD_QUERY_SERVICE = "AdvancedFieldQuery";
     45
     46    // extra parameters used
     47    protected static final String LEVEL_PARAM = "level";
     48    protected static final String RANK_PARAM = "sortBy";
     49    protected static final String RANK_PARAM_RANK = "1";
     50    protected static final String RANK_PARAM_NONE = "0";
     51    protected static final String SIMPLE_FIELD_PARAM = "simpleField";
     52    protected static final String ADVANCED_FIELD_PARAM = "complexField";
     53
     54    // more params for field query
     55    protected static final String FIELD_QUERY_PARAM = "fqv";
     56    protected static final String FIELD_STEM_PARAM = "fqs";
     57    protected static final String FIELD_CASE_PARAM = "fqc";
     58    protected static final String FIELD_ACCENT_PARAM = "fqa";
     59    protected static final String FIELD_FIELD_PARAM = "fqf";
     60    protected static final String FIELD_COMBINE_PARAM = "fqk";
     61    protected static final String FIELD_COMBINE_PARAM_AND = "0";
     62    protected static final String FIELD_COMBINE_PARAM_OR = "1";
     63    protected static final String FIELD_COMBINE_PARAM_NOT = "2";
     64
     65    // some stuff for config files
     66    protected static final String SEARCH_TYPE_ELEM = "searchType";
     67    protected static final String SEARCH_TYPE_PLAIN = "plain";
     68    protected static final String SEARCH_TYPE_SIMPLE_FORM = "simpleform";
     69    protected static final String SEARCH_TYPE_ADVANCED_FORM = "advancedform";
     70
     71    protected static final String DEFAULT_LEVEL_ELEM = "defaultLevel";
     72    protected static final String DEFAULT_DB_LEVEL_ELEM = "defaultDBLevel";
     73    protected static final String LEVEL_ELEM = "level";
     74    protected static final String FIELD_ATT = "field";
     75
     76    protected static final int TEXT_QUERY = 0;
     77    protected static final int SIMPLE_QUERY = 1;
     78    protected static final int ADVANCED_QUERY = 2;
     79
     80    protected String AND_OPERATOR = "&";
     81    protected String OR_OPERATOR = "|";
     82    protected String NOT_OPERATOR = "!";
     83
     84    // the default level for searching
     85    protected String default_level = null;
     86    // default level for collection db
     87    protected String default_db_level = null;
     88    // which search services will we offer??
     89    protected boolean plain_search = false;
     90    protected boolean simple_form_search = false;
     91    protected boolean advanced_form_search = false;
     92
     93    static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.AbstractGS2FieldSearch.class.getName());
     94
     95    /** constructor */
     96    public AbstractGS2FieldSearch()
     97    {
     98    }
     99
     100    public void cleanUp()
     101    {
     102        super.cleanUp();
     103    }
     104
     105    /** configure this service */
     106    public boolean configure(Element info, Element extra_info)
     107    {
     108        if (!super.configure(info, extra_info))
     109        {
     110            return false;
     111        }
     112
     113        // Get the default level out of <defaultLevel> (buildConfig.xml)
     114        Element def = (Element) GSXML.getChildByTagName(info, DEFAULT_LEVEL_ELEM);
     115        if (def != null)
     116        {
     117            this.default_level = def.getAttribute(GSXML.SHORTNAME_ATT);
     118        }
     119        if (this.default_level == null || this.default_level.equals(""))
     120        {
     121            logger.error("default level not specified!, assuming Doc");
     122            this.default_level = "Doc";
     123        }
     124
     125        // Get the default DB level
     126        def = (Element) GSXML.getChildByTagName(info, DEFAULT_DB_LEVEL_ELEM);
     127        if (def != null)
     128        {
     129            this.default_db_level = def.getAttribute(GSXML.SHORTNAME_ATT);
     130        }
     131        if (this.default_db_level == null || this.default_db_level.equals(""))
     132        {
     133            logger.error("default database level (defaultDBLevel) not specified!, assuming Sec");
     134            this.default_db_level = "Sec";
     135        }
     136
     137        // get stuff from from extra info (which is collectionConfig.xml)
     138        if (extra_info != null)
     139        {
     140
     141            // the search element
     142            Element config_search = (Element) GSXML.getChildByTagName(extra_info, GSXML.SEARCH_ELEM);
     143
     144            NodeList search_types = config_search.getElementsByTagName(SEARCH_TYPE_ELEM);
     145            if (search_types == null)
     146            {
     147                // none specified, assume plain only
     148                this.plain_search = true;
     149            }
     150            else
     151            {
     152                for (int i = 0; i < search_types.getLength(); i++)
     153                {
     154                    Element t = (Element) search_types.item(i);
     155                    String type_name = t.getAttribute(GSXML.NAME_ATT);
     156                    if (type_name.equals(SEARCH_TYPE_PLAIN))
     157                    {
     158                        this.plain_search = true;
     159                    }
     160                    else if (type_name.equals(SEARCH_TYPE_SIMPLE_FORM))
     161                    {
     162                        this.simple_form_search = true;
     163                    }
     164                    else if (type_name.equals(SEARCH_TYPE_ADVANCED_FORM))
     165                    {
     166                        this.advanced_form_search = true;
     167                    }
     168                }
     169            }
     170
     171            // AbstractGS2TextSearch has set up the TextQuery service, but we may not want it
     172            if (!this.plain_search)
     173            {
     174                // need to remove the TextQuery service
     175                Element tq_service = GSXML.getNamedElement(short_service_info, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, QUERY_SERVICE);
     176                short_service_info.removeChild(tq_service);
     177
     178            }
     179
     180            Document owner = info.getOwnerDocument();
     181
     182            NodeList levels = info.getElementsByTagName(GSXML.LEVEL_ELEM);
     183
     184            for (int i = 0; i < levels.getLength(); i++)
     185            {
     186                Element lev = (Element) levels.item(i);
     187                String name = lev.getAttribute(GSXML.NAME_ATT);
     188                Element node_extra = GSXML.getNamedElement(config_search, GSXML.LEVEL_ELEM, GSXML.NAME_ATT, name);
     189                if (node_extra == null)
     190                {
     191                    logger.error("haven't found extra info for level named " + name);
     192                    continue;
     193                }
     194
     195                // get the display elements if any - displayName
     196                NodeList display_names = node_extra.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
     197                if (display_names != null)
     198                {
     199                    for (int j = 0; j < display_names.getLength(); j++)
     200                    {
     201                        Element e = (Element) display_names.item(j);
     202                        lev.appendChild(owner.importNode(e, true));
     203                    }
     204                }
     205            } // for each level
     206        }
     207        else
     208        {
     209            // for soem reason we don't have the collectionConfig file. assume plain only
     210            this.plain_search = true;
     211        }
     212
     213        // the format info is the same for all services
     214        Element format_info = (Element) format_info_map.get(QUERY_SERVICE);
     215
     216        // set up the extra services which are available for this collection
     217        if (this.simple_form_search)
     218        {
     219            // set up short_service_info_ - for now just has id and type - name will be added in on the fly
     220            Element fq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
     221            fq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
     222            fq_service.setAttribute(GSXML.NAME_ATT, FIELD_QUERY_SERVICE);
     223            this.short_service_info.appendChild(fq_service);
     224
     225            if (format_info != null)
     226            {
     227                this.format_info_map.put(FIELD_QUERY_SERVICE, format_info);
     228            }
     229        }
     230
     231        if (this.advanced_form_search)
     232        {
     233            Element afq_service = this.doc.createElement(GSXML.SERVICE_ELEM);
     234            afq_service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
     235            afq_service.setAttribute(GSXML.NAME_ATT, ADVANCED_FIELD_QUERY_SERVICE);
     236            this.short_service_info.appendChild(afq_service);
     237
     238            if (format_info != null)
     239            {
     240                this.format_info_map.put(ADVANCED_FIELD_QUERY_SERVICE, format_info);
     241            }
     242        }
     243
     244        return true;
     245    }
     246
     247    protected Element getServiceDescription(String service_id, String lang, String subset)
     248    {
     249        // should we check that the service is actually on offer? presumably we wont get asked for services that we haven't advertised previously.
     250
     251        if (!service_id.equals(FIELD_QUERY_SERVICE) && !service_id.equals(ADVANCED_FIELD_QUERY_SERVICE))
     252        {
     253            return super.getServiceDescription(service_id, lang, subset);
     254        }
     255
     256        Element service = this.doc.createElement(GSXML.SERVICE_ELEM);
     257        service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_QUERY);
     258        service.setAttribute(GSXML.NAME_ATT, service_id);
     259        if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
     260        {
     261            service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service_id + ".name", lang)));
     262            service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service_id + ".submit", lang)));
     263            service.appendChild(GSXML.createDisplayTextElement(this.doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service_id + ".description", lang)));
     264
     265        }
     266        if (subset == null || subset.equals(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER))
     267        {
     268            Element param_list = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
     269            service.appendChild(param_list);
     270            if (service_id.equals(FIELD_QUERY_SERVICE))
     271            {
     272
     273                addCustomQueryParams(param_list, lang);
     274                createParameter(MAXDOCS_PARAM, param_list, lang);
     275                if (!default_index_subcollection.equals(""))
     276                {
     277                    createParameter(INDEX_SUBCOLLECTION_PARAM, param_list, lang);
     278                }
     279                if (!default_index_language.equals(""))
     280                {
     281                    createParameter(INDEX_LANGUAGE_PARAM, param_list, lang);
     282                }
     283                // create a multi param for the fields etc
     284                // text box, field
     285                Element multiparam = null;
     286                Element param = null;
     287                multiparam = GSXML.createParameterDescription(this.doc, SIMPLE_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
     288                multiparam.setAttribute("occurs", "4");
     289                param_list.appendChild(multiparam);
     290
     291                // the components
     292
     293                createParameter(FIELD_QUERY_PARAM, multiparam, lang);
     294                createParameter(FIELD_FIELD_PARAM, multiparam, lang);
     295
     296            }
     297            else
     298            {
     299                createParameter(LEVEL_PARAM, param_list, lang);
     300                createParameter(RANK_PARAM, param_list, lang);
     301                createParameter(MAXDOCS_PARAM, param_list, lang);
     302                if (!default_index_subcollection.equals(""))
     303                {
     304                    createParameter(INDEX_SUBCOLLECTION_PARAM, param_list, lang);
     305                }
     306                if (!default_index_language.equals(""))
     307                {
     308                    createParameter(INDEX_LANGUAGE_PARAM, param_list, lang);
     309                }
     310
     311                // create a multi param for the fields etc
     312                // text box, stem, case, field
     313
     314                Element multiparam = null;
     315                Element param = null;
     316
     317                multiparam = GSXML.createParameterDescription(this.doc, ADVANCED_FIELD_PARAM, "", GSXML.PARAM_TYPE_MULTI, null, null, null);
     318                multiparam.setAttribute("occurs", "4");
     319                param_list.appendChild(multiparam);
     320
     321                createParameter(FIELD_COMBINE_PARAM, multiparam, lang);
     322                createParameter(FIELD_QUERY_PARAM, multiparam, lang);
     323                if (this.does_case)
     324                {
     325                    createParameter(FIELD_CASE_PARAM, multiparam, lang);
     326                }
     327                if (this.does_stem)
     328                {
     329                    createParameter(FIELD_STEM_PARAM, multiparam, lang);
     330                }
     331                if (this.does_accent)
     332                {
     333                    createParameter(FIELD_ACCENT_PARAM, multiparam, lang);
     334                }
     335                createParameter(FIELD_FIELD_PARAM, multiparam, lang);
     336
     337            }
     338        }
     339        return service;
     340
     341    }
     342
     343    /** add in the level params to TextQuery */
     344    protected void addCustomQueryParams(Element param_list, String lang)
     345    {
     346        createParameter(LEVEL_PARAM, param_list, lang);
     347        super.addCustomQueryParams(param_list, lang);
     348    }
     349
     350    /** create a param and add to the list */
     351    protected void createParameter(String name, Element param_list, String lang)
     352    {
     353        Element param = null;
     354        if (name.equals(LEVEL_PARAM))
     355        {
     356            ArrayList level_ids = new ArrayList();
     357            ArrayList level_names = new ArrayList();
     358            getLevelData(level_ids, level_names, lang);
     359            if (level_ids.size() > 1)
     360            {
     361                // the first one is the default
     362                //param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param."+LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)level_ids.get(0), level_ids, level_names);
     363                param = GSXML.createParameterDescription2(this.doc, LEVEL_PARAM, getTextString("param." + LEVEL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, this.default_level, level_ids, level_names);
     364            }
     365            else
     366            {
     367                // we need to set the level, but hidden, in case there is an invalid level saved
     368                //param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, (String)level_ids.get(0), null, null);
     369                param = GSXML.createParameterDescription(this.doc, LEVEL_PARAM, "", GSXML.PARAM_TYPE_INVISIBLE, this.default_level, null, null);
     370            }
     371        }
     372        else if (name.equals(RANK_PARAM))
     373        {
     374            String[] vals1 = { RANK_PARAM_RANK, RANK_PARAM_NONE };
     375            String[] vals1_texts = { getTextString("param." + RANK_PARAM + "." + RANK_PARAM_RANK, lang), getTextString("param." + RANK_PARAM + "." + RANK_PARAM_NONE, lang) };
     376
     377            param = GSXML.createParameterDescription(this.doc, RANK_PARAM, getTextString("param." + RANK_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, RANK_PARAM_RANK, vals1, vals1_texts);
     378
     379        }
     380        else if (name.equals(FIELD_QUERY_PARAM))
     381        {
     382            param = GSXML.createParameterDescription(this.doc, FIELD_QUERY_PARAM, getTextString("param." + FIELD_QUERY_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
     383
     384        }
     385        else if (name.equals(FIELD_CASE_PARAM) || name.equals(FIELD_STEM_PARAM) || name.equals(FIELD_ACCENT_PARAM))
     386        {
     387            String[] bool_ops = { "0", "1" };
     388            String[] bool_texts = { getTextString("param.boolean.off", lang, "AbstractTextSearch"), getTextString("param.boolean.on", lang, "AbstractTextSearch") };
     389            param = GSXML.createParameterDescription(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_BOOLEAN, BOOLEAN_PARAM_ON, bool_ops, bool_texts);
     390
     391        }
     392        else if (name.equals(FIELD_FIELD_PARAM))
     393        {
     394            ArrayList fields = new ArrayList();
     395            ArrayList field_names = new ArrayList();
     396            getIndexData(fields, field_names, lang);
     397            // the field list -  read from config file
     398
     399            // Fix for http://trac.greenstone.org/ticket/245 "java crash, index out of bounds"
     400            // org.greenstone.gsdl3.service.AbstractGS2FieldSearch.createParameter(AbstractGS2FieldSearch.java:362)
     401            // Changed from:
     402            // param = GSXML.createParameterDescription2(this.doc, name, getTextString("param."+name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, (String)fields.get(0), fields, field_names );
     403            String default_value = (fields.size() > 0) ? (String) fields.get(0) : null;
     404            // don't want to access element 0 if fields.size()==0, and
     405            // GSXML.createParameterDescription2 checks for default_value==null condition
     406            param = GSXML.createParameterDescription2(this.doc, name, getTextString("param." + name, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, default_value, fields, field_names);
     407
     408        }
     409        else if (name.equals(FIELD_COMBINE_PARAM))
     410        {
     411
     412            String[] vals = { FIELD_COMBINE_PARAM_AND, FIELD_COMBINE_PARAM_OR, FIELD_COMBINE_PARAM_NOT };
     413            String[] val_texts = { getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_AND, lang), getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_OR, lang), getTextString("param." + FIELD_COMBINE_PARAM + "." + FIELD_COMBINE_PARAM_NOT, lang) };
     414
     415            param = GSXML.createParameterDescription(this.doc, FIELD_COMBINE_PARAM, "", GSXML.PARAM_TYPE_ENUM_SINGLE, FIELD_COMBINE_PARAM_AND, vals, val_texts);
     416            param.setAttribute(GSXML.PARAM_IGNORE_POS_ATT, "0");
     417        }
     418
     419        if (param != null)
     420        {
     421            param_list.appendChild(param);
     422        }
     423        else
     424        {
     425            super.createParameter(name, param_list, lang);
     426        }
     427    }
     428
     429    // should cache some of this
     430    protected void getLevelData(ArrayList level_ids, ArrayList level_names, String lang)
     431    {
     432        Element level_list = (Element) GSXML.getChildByTagName(this.config_info, LEVEL_ELEM + GSXML.LIST_MODIFIER);
     433        NodeList levels = level_list.getElementsByTagName(LEVEL_ELEM);
     434        for (int i = 0; i < levels.getLength(); i++)
     435        {
     436            Element level = (Element) levels.item(i);
     437            String shortname = level.getAttribute(GSXML.SHORTNAME_ATT);
     438            if (shortname.equals(""))
     439            {
     440                continue;
     441            }
     442            level_ids.add(shortname);
     443            String display_name = GSXML.getDisplayText(level, GSXML.DISPLAY_TEXT_NAME, lang, "en");
     444            if (display_name.equals(""))
     445            {
     446                // we'll use the name, and the dictionary
     447                display_name = level.getAttribute(GSXML.NAME_ATT);
     448                if (display_name.equals(""))
     449                {
     450                    display_name = shortname;
     451                }
     452                else
     453                {
     454                    display_name = getTextString("level." + display_name, lang);
     455                }
     456            }
     457            level_names.add(display_name);
     458        }
     459    }
     460
     461    // the following three functions are needed so the base class can
     462    // call the process+SERVICE_NAME methods
     463    /** process a text query */
     464    protected Element processTextQuery(Element request)
     465    {
     466        return processAnyQuery(request, TEXT_QUERY);
     467    }
     468
     469    /** process a field query */
     470    protected Element processFieldQuery(Element request)
     471    {
     472        return processAnyQuery(request, SIMPLE_QUERY);
     473    }
     474
     475    /** process an advanced field query */
     476    protected Element processAdvancedFieldQuery(Element request)
     477    {
     478        return processAnyQuery(request, ADVANCED_QUERY);
     479    }
     480
     481    /** process a query */
     482    protected Element processAnyQuery(Element request, int query_type)
     483    {
     484        String service_name = null;
     485        String empty_query_test_param = null;
     486        // set up the type specific bits
     487        switch (query_type)
     488        {
     489        case TEXT_QUERY:
     490            service_name = QUERY_SERVICE;
     491            empty_query_test_param = QUERY_PARAM;
     492            break;
     493        case SIMPLE_QUERY:
     494            service_name = FIELD_QUERY_SERVICE;
     495            empty_query_test_param = FIELD_QUERY_PARAM;
     496            break;
     497        case ADVANCED_QUERY:
     498            service_name = ADVANCED_FIELD_QUERY_SERVICE;
     499            empty_query_test_param = FIELD_QUERY_PARAM;
     500            break;
     501        default:
     502            // should never get here
     503            logger.error("wrong query type!!");
     504            return null;
     505        }
     506
     507        // Create a new (empty) result message
     508        Element result = this.doc.createElement(GSXML.RESPONSE_ELEM);
     509        result.setAttribute(GSXML.FROM_ATT, service_name);
     510        result.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_PROCESS);
     511
     512        // Get the parameters of the request
     513        Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
     514        if (param_list == null)
     515        {
     516            logger.error("TextQuery request had no paramList.");
     517            return result; // Return the empty result
     518        }
     519
     520        // Process the request parameters
     521        HashMap params = GSXML.extractParams(param_list, false);
     522
     523        // Make sure a query has been specified
     524        String query = (String) params.get(empty_query_test_param);
     525        if (query == null || query.equals(""))
     526        {
     527            return result; // Return the empty result
     528        }
     529
     530        // If a field hasn't been specified, use the default - for textQuery
     531        String field = (String) params.get(INDEX_PARAM);
     532        if (field == null)
     533        {
     534            field = default_index;
     535        }
     536
     537        // set up the appropriate query system
     538        if (!setUpQueryer(params))
     539        {
     540            return result;
     541        }
     542
     543        // if field search, create the query string
     544        switch (query_type)
     545        {
     546        case TEXT_QUERY:
     547            query = addFieldInfo(query, field);
     548            break;
     549        case SIMPLE_QUERY:
     550            query = parseFieldQueryParams(params);
     551            break;
     552        case ADVANCED_QUERY:
     553            query = parseAdvancedFieldQueryParams(params);
     554            break;
     555        }
     556
     557        // run the query
     558        Object query_result = runQuery(query);
     559
     560        // build up the response
     561
     562        // Create a metadata list to store information about the query results
     563        // should we be using metadataList? or something else?
     564        Element metadata_list = this.doc.createElement(GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
     565        result.appendChild(metadata_list);
     566
     567        // Add a metadata element specifying the number of matching documents
     568        long totalDocs = numDocsMatched(query_result);
     569
     570        GSXML.addMetadata(this.doc, metadata_list, "numDocsMatched", "" + totalDocs);
     571
     572        // Create a document list to store the matching documents, and add them
     573        String[] docs = getDocIDs(query_result);
     574        String[] doc_ranks = getDocRanks(query_result);
     575
     576        // add a metadata item to specify docs returned
     577        int docs_returned = docs.length;
     578        if (does_paging)
     579        {
     580            String maxdocs_str = (String) params.get(MAXDOCS_PARAM);
     581            if (maxdocs_str != null)
     582            {
     583                int maxdocs = Integer.parseInt(maxdocs_str);
     584                docs_returned = (maxdocs < (int) totalDocs ? maxdocs : (int) totalDocs);
     585            }
     586        }
     587        GSXML.addMetadata(this.doc, metadata_list, "numDocsReturned", "" + docs_returned);
     588
     589        // add a metadata item to specify what actual query was done - eg if stuff was stripped out etc. and then we can use the query later, cos we don't know which parameter was the query
     590        GSXML.addMetadata(this.doc, metadata_list, "query", query);
     591        if (docs.length > 0)
     592        {
     593            Element document_list = this.doc.createElement(GSXML.DOC_NODE_ELEM + GSXML.LIST_MODIFIER);
     594            result.appendChild(document_list);
     595            for (int d = 0; d < docs.length; d++)
     596            {
     597                String doc_id = internalNum2OID(docs[d]);
     598                Element doc_node = createDocNode(doc_id, doc_ranks[d]);
     599                document_list.appendChild(doc_node);
     600            }
     601        }
     602
     603        // Create a term list to store the term information, and add it
     604        Element term_list = this.doc.createElement(GSXML.TERM_ELEM + GSXML.LIST_MODIFIER);
     605        result.appendChild(term_list);
     606        addTermInfo(term_list, params, query_result);
     607
     608        return result;
     609
     610    }
     611
     612    /** methods to handle actually doing the query */
     613    /** do any initialisation of the query object */
     614    abstract protected boolean setUpQueryer(HashMap params);
     615
     616    /** do the query */
     617    abstract protected Object runQuery(String query);
     618
     619    /** get the total number of docs that match */
     620    abstract protected long numDocsMatched(Object query_result);
     621
     622    /** get the list of doc ids */
     623    abstract protected String[] getDocIDs(Object query_result);
     624
     625    /** get the list of doc ranks */
     626    abstract protected String[] getDocRanks(Object query_result);
     627
     628    /** add in term info if available */
     629    abstract protected boolean addTermInfo(Element term_list, HashMap params, Object query_result);
     630
     631    /**
     632     * combines all the field params into a single query - for simple field
     633     * query
     634     */
     635    /** We assume the combination (AND/OR) is done by the match param */
     636    protected String parseFieldQueryParams(HashMap params)
     637    {
     638
     639        StringBuffer final_query = new StringBuffer(256);
     640        String text_line = (String) params.get(FIELD_QUERY_PARAM);
     641        String[] texts = text_line.split(",", -1);
     642        String field_line = (String) params.get(FIELD_FIELD_PARAM);
     643        String[] fields = field_line.split(",", -1);
     644
     645        for (int i = 0; i < texts.length; i++)
     646        {
     647            String q = texts[i].trim();
     648            if (!q.equals(""))
     649            {
     650                final_query.append(" " + addFieldInfo(q, fields[i]));
     651            }
     652        }
     653
     654        return final_query.toString();
     655    }
     656
     657    abstract protected String addFieldInfo(String query, String field);
     658
     659    /**
     660     * combines all the field params into a single query - for advanced field
     661     * query
     662     */
     663    protected String parseAdvancedFieldQueryParams(HashMap params)
     664    {
     665
     666        StringBuffer final_query = new StringBuffer(256);
     667        String text_line = (String) params.get(FIELD_QUERY_PARAM);
     668        String[] texts = text_line.split(",", -1);
     669        String field_line = (String) params.get(FIELD_FIELD_PARAM);
     670        String[] fields = field_line.split(",", -1);
     671        String[] cases = null;
     672        String[] stems = null;
     673        String[] accents = null;
     674        if (does_case)
     675        {
     676            String case_line = (String) params.get(FIELD_CASE_PARAM);
     677            if (case_line != null)
     678                cases = case_line.split(",", -1);
     679        }
     680        if (does_stem)
     681        {
     682            String stem_line = (String) params.get(FIELD_STEM_PARAM);
     683            if (stem_line != null)
     684                stems = stem_line.split(",", -1);
     685        }
     686        if (does_accent)
     687        {
     688            String accent_line = (String) params.get(FIELD_ACCENT_PARAM);
     689            if (accent_line != null)
     690                accents = accent_line.split(",", -1);
     691        }
     692        String combine_line = (String) params.get(FIELD_COMBINE_PARAM);
     693        String[] combines = combine_line.split(",", -1);
     694        String combine = "";
     695        for (int i = 0; i < texts.length; i++)
     696        {
     697            if (i == 0)
     698            {// assume first one is blank
     699                combine = "";
     700            }
     701            else
     702            {
     703                String x = combines[i];
     704                if (x.equals(FIELD_COMBINE_PARAM_AND))
     705                {
     706                    combine = AND_OPERATOR;
     707                }
     708                else if (x.equals(FIELD_COMBINE_PARAM_OR))
     709                {
     710                    combine = OR_OPERATOR;
     711                }
     712                else if (x.equals(FIELD_COMBINE_PARAM_NOT))
     713                {
     714                    combine = NOT_OPERATOR;
     715                }
     716
     717            }
     718
     719            String q = texts[i].trim();
     720            boolean modified = false;
     721            if (!q.equals(""))
     722            {
     723                String c = null;
     724                String s = null;
     725                String a = null;
     726                if (does_case)
     727                {
     728                    modified = true;
     729                    c = cases[i];
     730                }
     731                if (does_stem)
     732                {
     733                    modified = true;
     734                    s = stems[i];
     735                }
     736                if (does_accent)
     737                {
     738                    modified = true;
     739                    a = accents[i];
     740                }
     741                if (modified)
     742                {
     743                    q = addStemOptions(q, s, c, a);
     744                }
     745                addQueryElem(final_query, q, fields[i], combine);
     746            }
     747        }
     748        return final_query.toString();
     749    }
     750
     751    abstract protected void addQueryElem(StringBuffer final_query, String query, String field, String combine);
     752
     753    abstract protected String addStemOptions(String query, String stem, String casef, String accent);
     754
    666755}
    667 
    668 
Note: See TracChangeset for help on using the changeset viewer.