Changeset 29067

Show
Ignore:
Timestamp:
19.05.2014 13:27:25 (6 years ago)
Author:
kjdon
Message:

changed metadataFormat stuff. service uses some info from main OAIConfig.xml. Format for elements and mappings has changed. a lot of code tidy up.

Files:
1 modified

Legend:

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

    r28986 r29067  
    11/* 
    22 *    OAIPMH.java 
    3  *    Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org 
     3 *    Copyright (C) 2010 New Zealand Digital Library, http://www.nzdl.org 
    44 * 
    55 *    This program is free software; you can redistribute it and/or modify 
     
    4343import java.util.Date; 
    4444import java.util.HashMap; 
     45import java.util.HashSet; 
    4546import java.util.Map.Entry; 
    4647 
     
    6465  protected Element list_sets_response = null; 
    6566   
     67  protected Element meta_formats_definition = null; 
     68  protected HashMap<String, HashSet<String>> format_elements_map = null; 
     69  protected HashMap<String, Element> format_response_map = null; 
    6670  /** constructor */ 
    6771  public OAIPMH() { 
     
    161165  } 
    162166 
     167  public boolean configureOAI(Element oai_config_elem) { 
     168    this.meta_formats_definition = this.desc_doc.createElement(OAIXML.LIST_METADATA_FORMATS); 
     169    this.format_response_map = new HashMap<String, Element>(); 
     170    this.format_elements_map = new HashMap<String, HashSet<String>>(); 
     171 
     172    // for now, all we want is the metadata prefix description and the mapping list 
     173    Element main_lmf_elem = (Element) GSXML.getChildByTagName(oai_config_elem, OAIXML.LIST_METADATA_FORMATS); 
     174    if (main_lmf_elem == null) { 
     175      logger.error("No listMetadataFormats element found in OAIConfig.xml"); 
     176      return false; 
     177    } 
     178    NodeList meta_formats_list = this.config_info.getElementsByTagName(OAIXML.METADATA_FORMAT); 
     179    if (meta_formats_list.getLength() == 0) { 
     180      logger.error("no metadataFormat elements found in OAIPMH serviceRack element"); 
     181      return false; 
     182    } 
     183    boolean found_meta_format = false; 
     184    for(int i=0; i<meta_formats_list.getLength(); i++) { 
     185      Element mf = (Element) meta_formats_list.item(i); 
     186      String prefix = mf.getAttribute(OAIXML.METADATA_PREFIX); 
     187      if (prefix.equals("")) { 
     188    logger.error("metadataFormat element had no metadataPrefix attribute"); 
     189    continue; 
     190      } 
     191      // get the right format from OAICOnfig 
     192      Element meta_format = findNamedMetadataFormat(main_lmf_elem, prefix); 
     193      if (meta_format == null) { 
     194    logger.error("Couldn't find metadataFormat named "+prefix+" in OAIConfig.xml"); 
     195    continue; 
     196      } 
     197      // copy the format definition into our stored Element 
     198      Element collection_version_format = (Element) this.desc_doc.importNode(meta_format, true); 
     199      collection_version_format.setAttribute(GSXML.NAME_ATT, prefix); // for convenience 
     200      this.meta_formats_definition.appendChild(collection_version_format); 
     201      // set up the response element for this format 
     202      format_response_map.put(prefix, OAIXML.getMetadataFormatShort(this.desc_doc, collection_version_format)); 
     203      // add in collection specific mappings 
     204      addCollectionMappings(collection_version_format, mf); 
     205      // now set up a list of all collection elements for reverse lookup of the mapping 
     206      format_elements_map.put(prefix, getAllCollectionElements(collection_version_format)); 
     207       
     208    } 
     209    return true; 
     210  } 
     211 
     212  protected Element findNamedMetadataFormat(Element list_meta_formats, String prefix) { 
     213    NodeList formats = list_meta_formats.getElementsByTagName(OAIXML.METADATA_FORMAT); 
     214    for (int i=0; i<formats.getLength(); i++) { 
     215      Element format = (Element)formats.item(i); 
     216      String meta_name = GSXML.getNodeText((Element)GSXML.getChildByTagName(format, OAIXML.METADATA_PREFIX)); 
     217      if (prefix.equals(meta_name)) { 
     218    return format; 
     219      } 
     220    } 
     221    return null; 
     222  } 
     223     
     224    /** goes through the mappings from the collection one, and replaces existing ones in the main one */ 
     225    protected void addCollectionMappings(Element main_meta_format, Element coll_meta_format) { 
     226 
     227      Element element_list = (Element)GSXML.getChildByTagName(main_meta_format, OAIXML.ELEMENT+GSXML.LIST_MODIFIER); 
     228      Document doc = element_list.getOwnerDocument(); 
     229      NodeList coll_elements = coll_meta_format.getElementsByTagName(OAIXML.ELEMENT); 
     230      if (coll_elements.getLength()==0) { 
     231    // no mappings to include 
     232    return; 
     233      } 
     234      for (int i=0; i<coll_elements.getLength(); i++) { 
     235    Element e = (Element)coll_elements.item(i); 
     236    String elem_name = e.getAttribute(GSXML.NAME_ATT); 
     237    Element main_elem = GSXML.getNamedElement(element_list, OAIXML.ELEMENT, GSXML.NAME_ATT, elem_name); 
     238    if (main_elem == null) { 
     239      logger.error(elem_name+" not found in meta format, not using it"); 
     240    } else { 
     241      element_list.replaceChild(doc.importNode(e, true),main_elem ); 
     242      } 
     243      } 
     244    } 
     245 
     246    /** goes through all the mappings and makes a set of all collection 
     247    metadata names that could become an oai meta element - acts as 
     248    a reverse lookup for the mappings */ 
     249    protected HashSet<String> getAllCollectionElements(Element meta_format) { 
     250      HashSet<String> meta_name_set = new HashSet<String>(); 
     251      NodeList elements = meta_format.getElementsByTagName(OAIXML.ELEMENT); 
     252      for (int i=0; i<elements.getLength(); i++) { 
     253    Element e = (Element)elements.item(i); 
     254    Element map = (Element)GSXML.getChildByTagName(e, OAIXML.MAPPING); 
     255    if (map == null) { 
     256      // there is no mapping, just use the element name 
     257      meta_name_set.add(e.getAttribute(GSXML.NAME_ATT)); 
     258    } else { 
     259      String list_of_names = map.getAttribute(OAIXML.ELEMENTS); 
     260      String[] name_array = list_of_names.split(","); 
     261      for (int j=0; j<name_array.length; j++) { 
     262        meta_name_set.add(name_array[j]); 
     263      } 
     264    } 
     265      } 
     266      return meta_name_set; 
     267    } 
     268    
    163269  /** returns a specific service description */ 
    164270  public Element getServiceDescription(Document doc, String service_id, String lang, String subset) { 
     
    199305    return null; 
    200306  } 
    201   // /** return the metadata information about this set of the repository */ 
    202   // protected Element processIdentify(Element req) { 
    203   //   return null; 
    204   // } 
    205   /** return the metadata information  */ 
     307 
     308  /** The list sets service returns all the sets that this collection is/is part of/contains. This is gathered by Receptionist from all collections to answer the OAI ListSets request.  */ 
    206309  protected Element processListSets(Element req) { 
    207310    return list_sets_response; 
    208311  } 
    209   /** return the metadata information */ 
     312  /** returns the actual record element used in the OAI GetRecord response */ 
    210313  protected Element processGetRecord(Element req) { 
    211314    /** arguments: 
     
    224327    } 
    225328     
    226     Element metadata_format = getMetadataFormatElement(prefix); 
    227     if(metadata_format == null) { 
    228       logger.error("metadata prefix is not supported."); 
     329    // check that we support this format 
     330    if (!format_response_map.containsKey(prefix)) { 
     331      logger.error("metadata prefix is not supported for collection "+this.coll_name); 
    229332      return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""); 
    230333    } 
     
    242345    Document doc = XMLConverter.newDOM(); 
    243346    ArrayList<String> keys = new ArrayList<String>(info.getKeys()); 
    244     String oailastmodified = ""; 
    245     if(keys.contains(OAIXML.OAI_LASTMODIFIED)) { 
    246       oailastmodified = info.getInfo(OAIXML.OAI_LASTMODIFIED); 
     347    String oailastmodified = info.getInfo(OAIXML.OAI_LASTMODIFIED); 
     348    if (!oailastmodified.equals("")) { 
     349      // format into a string 
    247350      oailastmodified = OAIXML.getTime(Long.parseLong(oailastmodified)*1000); // java wants dates in milliseconds 
    248351    } 
     
    255358    record.appendChild(createHeaderElement(doc, oid, oailastmodified));       
    256359    //compose the metadata element 
    257     record.appendChild(createMetadataElement(doc, prefix, info, metadata_format)); 
     360    record.appendChild(createMetadataElement(doc, prefix, info)); 
    258361    get_record.appendChild(record); 
    259362    return get_record_response; 
    260363  } 
    261   /** return a list of identifiers  */ 
     364 
     365  /** return a list of records in specified set, containing metadata from specified prefix*/ 
     366  protected Element processListRecords(Element req) { 
     367    return processListIdentifiersOrRecords(req, OAIXML.LIST_RECORDS, true); 
     368  } 
     369 
     370  /** return a list of identifiers in specified set that contain metadata belonging to specified prefix. */ 
    262371  protected Element processListIdentifiers(Element req) { 
     372    return processListIdentifiersOrRecords(req, OAIXML.LIST_IDENTIFIERS, false); 
     373  } 
     374 
     375  // Get a list of records/identifiers that match the parameters. 
     376  protected Element processListIdentifiersOrRecords(Element req, String response_name, boolean include_metadata) { 
    263377    /** arguments: 
    264378        metadataPrefix: required 
     
    303417    }     
    304418 
    305     Element metadata_format = getMetadataFormatElement(prefix); 
    306     if(metadata_format == null) { 
    307       logger.error("metadata prefix is not supported."); 
     419    if (!format_response_map.containsKey(prefix)) { 
     420      logger.error(prefix + " metadata prefix is not supported for collection "+this.coll_name); 
    308421      return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""); 
    309422    } 
     
    315428    // all validation is done 
    316429 
     430    // get the list of elements that are in this metadata prefix 
     431    HashSet<String> set_of_elems = format_elements_map.get(prefix); 
     432 
    317433    Document doc = XMLConverter.newDOM(); 
    318     Element list_identifiers_response = doc.createElement(GSXML.RESPONSE_ELEM); 
    319     Element list_identifiers = doc.createElement(OAIXML.LIST_IDENTIFIERS); 
    320     list_identifiers_response.appendChild(list_identifiers); 
     434    Element list_items_response = doc.createElement(GSXML.RESPONSE_ELEM); 
     435    Element list_items = doc.createElement(response_name); 
     436    list_items_response.appendChild(list_items); 
    321437 
    322438    for(int i=0; i<oid_list.size(); i++) { 
     
    327443        continue; 
    328444      } 
    329       ArrayList<String> keys = new ArrayList<String>(info.getKeys()); 
    330       String oailastmodified = ""; 
    331       if(keys.contains(OAIXML.OAI_LASTMODIFIED)) { 
    332         oailastmodified = info.getInfo(OAIXML.OAI_LASTMODIFIED); 
    333     oailastmodified = OAIXML.getTime(Long.parseLong(oailastmodified)*1000); // java wants dates in milliseconds 
    334       } 
    335445       
    336       Date this_date = OAIXML.getDate(oailastmodified);         
    337       if (from_date != null) { 
    338         if(this_date.before(from_date)) { 
    339           continue; 
    340         } 
    341       } 
    342       if (until_date != null) { 
    343         if (this_date.after(until_date)) { 
    344           continue; 
    345         } 
    346       }       
    347       //compose the header element and append it 
    348       list_identifiers.appendChild(createHeaderElement(doc, oid, oailastmodified));       
     446      String oailastmodified = info.getInfo(OAIXML.OAI_LASTMODIFIED); 
     447      long oailastmodifiedmillis = 0; 
     448      Date this_date = null; 
     449      if (oailastmodified.equals("")) { 
     450    if (from_date != null || until_date !=null) { 
     451      continue; // if this doc doesn't have a date for some reason, and 
     452      // we are doing a date range, then don't include it. 
     453    } 
     454      } else { // check the date against date range from query (if any) 
     455    oailastmodifiedmillis = Long.parseLong(oailastmodified)*1000; // greenstone dates are in secods, and Java wants dates in milliseconds 
     456    this_date = new Date(oailastmodifiedmillis);  
     457  
     458    if (from_date != null) { 
     459      if(this_date.before(from_date)) { 
     460        continue; 
     461      } 
     462    } 
     463    if (until_date != null) { 
     464      if (this_date.after(until_date)) { 
     465        continue; 
     466      } 
     467    }     
     468      }   
     469      //Now check that this id has metadata for the required prefix. 
     470      if (documentContainsMetadata(info, set_of_elems)) { 
     471    // YES, it does have some metadata for this prefix 
     472    if (include_metadata) { 
     473      // compose a record and add header and metadata 
     474      Element record = doc.createElement(OAIXML.RECORD); 
     475      list_items.appendChild(record); 
     476      //compose the header element 
     477      record.appendChild(createHeaderElement(doc, oid, OAIXML.getTime(oailastmodifiedmillis)));       
     478      //compose the metadata element 
     479      record.appendChild(createMetadataElement(doc, prefix, info)); 
     480    } else { 
     481      //compose the header element and append it 
     482      list_items.appendChild(createHeaderElement(doc, oid, OAIXML.getTime(oailastmodifiedmillis)));       
     483    } 
     484      } // otherwise we won't include this oid. 
    349485    }//end of for(int i=0; i<oid_list.size(); i++) of doing thru each record 
    350486     
    351     return list_identifiers_response;         
    352   } 
    353   /** return a list of records  */ 
    354   protected Element processListRecords(Element req) { 
    355     /** the request sent here may contain optional 'from', 'untill', 'metadataPrefix',  
    356      * and 'resumptionToken' params. see doListSets() in OAIReceptionist. 
    357      * if the request contains 'resumptionToken' then it should have been handled by the 
    358      * OAIReceptionist. Therefore, the request sent here must not contain 'resumptionToken' 
    359      * argument but a 'metadataPrefix' param. The OAIReceptionist makes sure of this. 
    360      */ 
    361     NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM); 
    362      
    363     if(params.getLength() == 0) { 
    364       logger.error("must at least have the metadataPrefix parameter, can't be none"); 
    365       return OAIXML.createErrorResponse(OAIXML.BAD_ARGUMENT, ""); 
    366     } 
    367      
    368     HashMap<String, String> param_map = GSXML.getParamMap(params);     
    369  
    370     String prefix = ""; 
    371     Date from_date = null; 
    372     Date until_date = null; 
    373      
    374     if(param_map.containsKey(OAIXML.METADATA_PREFIX) == false) {     
    375       //Just a double-check 
    376       logger.error("A param element containing the metadataPrefix is not present."); 
    377       return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""); 
    378     } 
    379     prefix = param_map.get(OAIXML.METADATA_PREFIX); 
    380     if (prefix == null || prefix.equals("")) { 
    381       //Just a double-check 
    382       logger.error("the value of metadataPrefix att is not present in the request."); 
    383       return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""); 
    384     } 
    385      
    386     if(param_map.containsKey(OAIXML.FROM)) { 
    387       String from = param_map.get(OAIXML.FROM); 
    388       from_date = OAIXML.getDate(from); 
    389     }     
    390     if(param_map.containsKey(OAIXML.UNTIL)) { 
    391       String until = param_map.get(OAIXML.UNTIL); 
    392       until_date = OAIXML.getDate(until); 
    393     }     
    394     Element metadata_format = getMetadataFormatElement(prefix); 
    395     if(metadata_format == null) { 
    396       logger.error("metadata prefix is not supported."); 
    397       return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""); 
    398     } 
    399      
    400     //get a list of identifiers (it contains a list of strings) 
    401     ArrayList<String> oid_list = getChildrenIds(OAIXML.BROWSELIST); 
    402     if (oid_list == null) { 
    403       logger.error("No matched records found in collection: browselist is empty"); 
    404       return OAIXML.createErrorResponse(OAIXML.NO_RECORDS_MATCH, ""); 
    405     } 
    406     // all validation is done 
    407  
    408     Document doc = XMLConverter.newDOM(); 
    409     Element list_records_response = doc.createElement(GSXML.RESPONSE_ELEM); 
    410     Element list_records = doc.createElement(OAIXML.LIST_RECORDS); 
    411     list_records_response.appendChild(list_records); 
    412     for(int i=0; i<oid_list.size(); i++) { 
    413       String oid = oid_list.get(i); 
    414       DBInfo info = this.coll_db.getInfo(oid); 
    415       if (info == null) { 
    416         logger.error("Database does not contains information about oid: " +oid); 
    417         continue; 
    418       } 
    419       ArrayList<String> keys = new ArrayList<String>(info.getKeys()); 
    420       String oailastmodified = ""; 
    421       if(keys.contains(OAIXML.OAI_LASTMODIFIED)) { 
    422         oailastmodified = info.getInfo(OAIXML.OAI_LASTMODIFIED); 
    423     oailastmodified = OAIXML.getTime(Long.parseLong(oailastmodified)*1000); // java wants dates in milliseconds 
    424       } 
    425        
    426       Date this_date = OAIXML.getDate(oailastmodified);         
    427       if (from_date != null) { 
    428         if(this_date.before(from_date)) { 
    429           continue; 
    430         } 
    431       } 
    432       if (until_date != null) { 
    433         if (this_date.after(until_date)) { 
    434           continue; 
    435         } 
    436       } 
    437        
    438       Element record = doc.createElement(OAIXML.RECORD); 
    439       list_records.appendChild(record); 
    440       //compose the header element 
    441       record.appendChild(createHeaderElement(doc, oid, oailastmodified));       
    442       //compose the metadata element 
    443       record.appendChild(createMetadataElement(doc, prefix, info, metadata_format)); 
    444        
    445     }//end of for(int i=0; i<oid_list.size(); i++) of doing thru each record 
    446      
    447     return list_records_response;     
    448   } 
     487    return list_items_response;         
     488     
     489  } 
     490 
    449491   
    450492  // have implemented setDescription as an element, instead of a container containing metadata 
     
    484526    return true; 
    485527  } 
    486   /** get the metadataFormat element from the collectionConfig.xml containing the specified metadata prefix. 
    487    *  return null if not found. 
     528 
     529 /** create the metadata element used when processing ListRecords/GetRecord requests 
    488530   */ 
    489   private Element getMetadataFormatElement(String prefix) { 
    490     Element list_meta_format = (Element)GSXML.getChildByTagName(this.config_info, OAIXML.LIST_METADATA_FORMATS); 
    491     Element metadata_format = GSXML.getNamedElement(list_meta_format, OAIXML.METADATA_FORMAT, OAIXML.METADATA_PREFIX, prefix); 
    492     return metadata_format; 
    493   } 
    494   /** create the metadata element used when processing ListRecords/GetRecord requests 
    495    */ 
    496   private Element createMetadataElement(Document doc, String prefix, DBInfo info, Element metadata_format) { 
    497         //the prefix string is in the form: oai_dc, for example. 
    498         String prfx_str = ""; 
    499         //the metadata namespace used to retrieve metadata in the repository 
    500         //For example, if the prefix is like 'oai_ex' then we used 'ex' to get the metadata 
    501         //Normally we would use 'dc' to find metadata. 
    502         String meta_ns = ""; 
    503         if(prefix.equals(OAIXML.OAI_DC)) { 
    504           if(OAIXML.oai_version.equals(OAIXML.OAI_VERSION2)) { 
    505             prfx_str = prefix + ":" + OAIXML.DC; 
    506           } else { 
    507             prfx_str = OAIXML.DC;//oai version 1 
    508           } 
    509           meta_ns = OAIXML.DC; 
    510         } else { 
    511           prfx_str = prefix.substring(prefix.indexOf("_") + 1); 
    512           meta_ns = prfx_str; 
    513         } 
    514         //create the <metadata> element 
    515         //OAIXML.oai_version is read from OAIConfig.xml and its default value is "2.0" 
    516     Element metadata = doc.createElement(OAIXML.METADATA); 
    517         Element prfx_str_elem = OAIXML.getMetadataPrefixElement(doc, prfx_str, OAIXML.oai_version); 
    518         metadata.appendChild(prfx_str_elem); 
    519         String[] metadata_names = getMetadataNameMapping(metadata_format); 
    520         HashMap meta_map = getInfoByNames(info, metadata_names); 
    521          
    522         // if there's no dc:identifier already after the mapping, we'll add it in 
    523         if(!meta_map.containsKey(OAIXML.DC+":identifier")) { // dc:identifier OAIXML.IDENTIFIER 
    524             outputCustomMetadata(meta_map, info, OAIXML.DC+":identifier"); 
    525         } 
    526      
    527          
    528     if (meta_map == null) { 
    529       return metadata; 
     531  protected Element createMetadataElement(Document doc, String prefix, DBInfo info) { 
     532    // the <metadata> element 
     533    Element metadata = doc.createElement(OAIXML.METADATA); 
     534    // the <oai:dc namespace...> element 
     535    Element prfx_str_elem = OAIXML.getMetadataPrefixElement(doc, prefix, OAIXML.oai_version); 
     536    metadata.appendChild(prfx_str_elem); 
     537 
     538    Element meta_format_element = GSXML.getNamedElement(this.meta_formats_definition, OAIXML.METADATA_FORMAT, GSXML.NAME_ATT, prefix); 
     539    NodeList elements = meta_format_element.getElementsByTagName(OAIXML.ELEMENT); 
     540    // for each element in the definition 
     541    for (int i=0; i<elements.getLength(); i++) { 
     542      Element e = (Element)elements.item(i); 
     543      Element map = (Element)GSXML.getChildByTagName(e, OAIXML.MAPPING); 
     544      if (map == null) { 
     545    // look up the element name 
     546    addMetadata(prfx_str_elem, e.getAttribute(GSXML.NAME_ATT), info); 
     547      } else { 
     548    // we go though the list of names in the mapping 
     549    addMetadata(prfx_str_elem, e.getAttribute(GSXML.NAME_ATT), map.getAttribute(OAIXML.SELECT), map.getAttribute(OAIXML.ELEMENTS), info); 
     550      } 
     551    } 
     552    // output any metadata that is not just a simple mapping 
     553    addCustomMetadata(prfx_str_elem, prefix, info); 
     554    return metadata; 
     555  } 
     556 
     557  /** a simple addMetadata where we look for meta_name metadata, and add as that name*/ 
     558  protected void addMetadata(Element meta_list_elem, String meta_name, DBInfo info) { 
     559    Vector<String> values = info.getMultiInfo(meta_name); 
     560    for (int i=0; i<values.size(); i++) { 
     561      addMetadataElement(meta_list_elem, meta_name, values.get(i)); 
     562    } 
     563  } 
     564 
     565  /** more complicated addMetadata - can add multiple items. */ 
     566  protected void addMetadata(Element meta_list_elem, String new_meta_name, String select_type, String name_list, DBInfo info) { 
     567    String[] names = name_list.split(","); 
     568    for (int i=0; i<names.length; i++) { 
     569      Vector<String> values = info.getMultiInfo(names[i]); 
     570      if (values == null || values.size()==0) { 
     571    continue; 
     572      } 
     573      for (int j=0; j<values.size(); j++) { 
     574    addMetadataElement(meta_list_elem, new_meta_name, values.get(j)); 
     575    if (select_type.equals(OAIXML.SELECT_SINGLE_VALUE)) { 
     576      return; // only want to add one value 
    530577    } 
    531     ArrayList meta_list = new ArrayList(meta_map.entrySet()); 
    532     for (int j=0; j<meta_list.size(); j++) { 
    533       Entry men = (Entry)meta_list.get(j); 
    534       String meta_name = (String)men.getKey(); 
    535       //meta_name = meta_name.replace('.', ':'); // namespace separator should be : for oai 
    536       String meta_value = (String)men.getValue(); 
    537       Element e = doc.createElement(meta_name); 
    538       GSXML.setNodeText(e, meta_value); 
    539       prfx_str_elem.appendChild(e); 
     578      } 
     579      if (select_type.equals(OAIXML.SELECT_FIRST_VALID_META)) { 
     580    return; // we have added all values of this meta elem 
     581      } 
     582      // otherwise, we will keep going through the list and add them all. 
     583    } 
     584  } 
     585   
     586  // specific metadata formats might need to do some custom metadata that is not 
     587  //just a standard mapping. eg oai_dc outputting an identifier that is a link 
     588  protected void addCustomMetadata(Element meta_list_elem, String prefix, DBInfo info) { 
     589     
     590    if (prefix.equals(OAIXML.META_FORMAT_DC)) { 
     591      // we want to add in another dc:identifier element with a link to the resource if possible 
     592      // try gs.OAIResourceURL first, then srclinkFile, then GS version of documnet 
     593      String gsURL = info.getInfo(OAIXML.GS_OAI_RESOURCE_URL); 
     594      if (gsURL.equals("")) { 
     595    String base_url = OAIXML.getBaseURL(); // e.g. e.g. http://host:port/greenstone3/oaiserver 
     596    // try srclinkFile 
     597    gsURL = info.getInfo("srclinkFile"); 
     598    if (!gsURL.equals("")) { 
     599      // make up the link to the file 
     600      gsURL = base_url.replace("oaiserver", "") + "sites/" + this.site_name  
     601        + "/collect/" + this.coll_name + "/index/assoc/"  
     602        + info.getInfo("assocfilepath") + "/" + gsURL;  
     603    } else { 
     604      // no srclink file, lets provide a link to the greenstone doc 
     605      gsURL = base_url.replace("oaiserver", "library") + "/collection/" + this.coll_name + "/document/" + info.getInfo("Identifier"); 
    540606    } 
    541      
    542         return metadata; 
    543   } 
     607      } 
     608      // now we have the url link, add as metadata 
     609      addMetadataElement(meta_list_elem, "dc:identifier", gsURL); 
     610    } 
     611  } 
     612 
     613  /** create the actual metadata element for the list */ 
     614  protected void addMetadataElement(Element meta_list_elem, String name, String value) { 
     615     
     616    Element meta = GSXML.createTextElement(meta_list_elem.getOwnerDocument(), name, value); 
     617    meta_list_elem.appendChild(meta); 
     618  } 
     619 
     620 
    544621  /** create a header element used when processing requests like ListRecords/GetRecord/ListIdentifiers 
    545622   */ 
    546   private Element createHeaderElement(Document doc, String oid, String oailastmodified) {     
     623  protected Element createHeaderElement(Document doc, String oid, String oailastmodified) {     
    547624        Element header = doc.createElement(OAIXML.HEADER); 
    548625        Element identifier = doc.createElement(OAIXML.IDENTIFIER); 
    549         //GSXML.setNodeText(identifier, site_name + ":" + coll_name + ":" + oid); 
    550626    GSXML.setNodeText(identifier, coll_name + ":" + oid); 
    551627        header.appendChild(identifier); 
    552628        Element set_spec = doc.createElement(OAIXML.SET_SPEC); 
    553         //GSXML.setNodeText(set_spec, site_name + ":" + coll_name); 
    554629    GSXML.setNodeText(set_spec, coll_name); 
    555630        header.appendChild(set_spec); 
     
    559634        return header; 
    560635  } 
     636 
    561637  /** return the metadata information  */ 
    562638  protected Element processListMetadataFormats(Element req) { 
     
    586662    } 
    587663     
    588     NodeList meta_list = getMetadataFormatList(); 
    589     if (meta_list == null || meta_list.getLength() == 0) { 
    590       logger.error("No metadata format is present in collectionConfig.xml"); 
    591       return OAIXML.createErrorResponse(OAIXML.NO_METADATA_FORMATS, ""); 
    592     } 
    593  
    594664    Document doc = XMLConverter.newDOM(); 
    595665    Element list_metadata_formats_response = doc.createElement(GSXML.RESPONSE_ELEM); 
     
    599669    boolean has_meta_format = false; 
    600670     
    601     for (int i=0; i<meta_list.getLength(); i++) { 
    602       Element metadata_format = (Element)meta_list.item(i); 
    603       String[] metadata_names = getMetadataNameMapping(metadata_format); 
    604       if (containsMetadata(info, metadata_names) == true) { 
    605         has_meta_format = true; 
    606     // TODO, can we do this in an easier way?? 
    607         Element meta_fmt = doc.createElement(OAIXML.METADATA_FORMAT); 
    608         GSXML.copyNamedElement(meta_fmt, metadata_format, OAIXML.METADATA_PREFIX); 
    609         GSXML.copyNamedElement(meta_fmt, metadata_format, OAIXML.METADATA_NAMESPACE); 
    610         GSXML.copyNamedElement(meta_fmt, metadata_format, OAIXML.SCHEMA); 
    611         list_metadata_formats.appendChild(meta_fmt); 
    612       } 
    613     }//end of for loop 
     671    // for each format in format_elements_map 
     672    Iterator<String> it = format_elements_map.keySet().iterator(); 
     673    while (it.hasNext()) { 
     674      String format = it.next(); 
     675      HashSet<String> set_of_elems = format_elements_map.get(format); 
     676      if (documentContainsMetadata(info, set_of_elems)) { 
     677    // add this format into the response 
     678    has_meta_format = true; 
     679    list_metadata_formats.appendChild(doc.importNode(format_response_map.get(format), true)); 
     680      } 
     681    } 
     682 
    614683    if (has_meta_format == false) { 
    615684      logger.error("Specified metadata names are not contained in the database."); 
     
    620689  } 
    621690 
    622   /** return the ListMetadataFormats element in collectionConfig.xml 
    623    *  Currently, it will only contain one metadata format: oai_dc 
    624    */ 
    625   protected NodeList getMetadataFormatList() { 
    626     Element list_meta_formats = (Element)GSXML.getChildByTagName(this.config_info, OAIXML.LIST_METADATA_FORMATS); 
    627     return GSXML.getChildrenByTagName(list_meta_formats, OAIXML.METADATA_FORMAT); 
    628   } 
    629   /** @param metadata_format - the metadataFormat element in collectionConfig.xml 
    630    */ 
    631   protected String[] getMetadataNameMapping(Element metadata_format) { 
    632    
    633     String[] names = OAIXML.getMetadataMapping(metadata_format); 
    634     if (names != null) { 
    635       return names; 
    636     } 
    637     logger.info("No metadata mappings are provided in collectionConfig.xml. Try for global mapping"); 
    638     names = OAIXML.getGlobalMetadataMapping(metadata_format.getAttribute(OAIXML.METADATA_PREFIX)); 
    639     return names; 
     691  protected boolean documentContainsMetadata(DBInfo info, HashSet<String> set_of_elems) { 
     692    if (set_of_elems.size() == 0) { 
     693      return false; 
     694    } 
     695    Iterator<String> i = set_of_elems.iterator(); 
     696    while (i.hasNext()) { 
     697      if (!info.getInfo(i.next()).equals("")) { 
     698    return true; 
     699      } 
     700    } 
     701    return false; 
    640702  } 
    641703 
     
    677739    } 
    678740    return false; 
    679   }   
    680     /** @param keys - contains a list of keys in string format. 
    681      * Here is a typical record in the collection database, 'keys' contains the values in <...>: 
    682      *---------------------------------------------------------------------- 
    683 [HASH01a84acb0f1aad2380493b3a] 
    684 <doctype>doc 
    685 <hastxt>1 
    686 <Language>en 
    687 <Encoding>windows_1252 
    688 <Plugin>HTMLPlug 
    689 <FileSize>205093 
    690 <Source>wb34te.htm 
    691 <hascover>1 
    692 <dls.Organization>World Bank 
    693 <dls.Title>Development in practice: Toward Gender Equality (wb34te) 
    694 <dls.Language>English 
    695 <dls.AZList>A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z 
    696 <dls.Subject>Women, gender and development, women's organizations 
    697 <dls.Keyword>achieve gender equality 
    698 <URL>http://wb34te/wb34te.htm 
    699 <Title>Development in practice: Toward Gender Equality 
    700 <lastmodified>1178245194 
    701 <assocfilepath>HASH01a8.dir 
    702 <memberof>CL3 
    703 <archivedir>HASH01a8.dir 
    704 <thistype>VList 
    705 <childtype>VList 
    706 <contains>".1;".2;".3;".4;".5;".6;".7;".8;".9 
    707 <docnum>349 
    708 <oailastmodified>1303283795 
    709 <lastmodifieddate>20110412 
    710 <oailastmodifieddate>20110420 
    711 ---------------------------------------------------------------------- 
    712      */ 
    713   public String[] getMetadata(DBInfo info, String names) { 
    714     String[] name_value = new String[2]; 
    715     ArrayList<String> keys = new ArrayList<String>(info.getKeys()); 
    716     for (int i=0; i<keys.size(); i++) { 
    717       String key = keys.get(i); 
    718       String first_name = ""; 
    719       String second_name = ""; 
    720       int index = names.indexOf(","); 
    721       if(index != -1) { 
    722         first_name = names.substring(0, index); 
    723         second_name = names.substring(index + 1); 
    724       } else { 
    725         first_name = second_name = names; 
    726       } 
    727       if(key.equals(second_name)) { 
    728     String meta_value = info.getInfo(key); 
    729         name_value[0] = first_name; 
    730     name_value[1] = meta_value; 
    731     return name_value; 
    732       } 
    733     } 
    734     return null; 
    735   } 
    736   protected HashMap getInfoByNames(DBInfo info, String[] metadata_names) { 
    737  
    738     if (metadata_names == null) { 
    739       return null; 
    740     } 
    741     HashMap<String, String> map = new HashMap<String, String>(); 
    742     boolean empty_map = true; 
    743      
    744     for(int i=0; i<metadata_names.length; i++) { 
    745       String[] name_value = getMetadata(info, metadata_names[i]); 
    746       if(name_value != null) {  
    747         map.put(name_value[0], name_value[1]); 
    748         empty_map = false; 
    749       } 
    750     } 
    751     return (empty_map == true) ? null : map; 
    752   } 
    753    
    754   // GS3 version of GS2's runtime-src/src/oaiservr/dublincore.cpp function output_custom_metadata 
    755   protected void outputCustomMetadata(HashMap meta_map, DBInfo info, String dc_identifier) { 
    756          
    757     // try gs.OAIResourceURL, else srclinkFile, else the GS version of the document 
    758     String identifier_value = info.getInfo(OAIXML.GS_OAI_RESOURCE_URL); 
    759      
    760     if(identifier_value.equals("")) { 
    761         String url = OAIXML.getBaseURL(); // e.g. e.g. http://host:port/greenstone3/library/oaiserver 
    762      
    763         identifier_value = info.getInfo("srclinkFile"); 
    764         if(identifier_value.equals(""))  
    765         { 
    766             // no source file to link to, so link to the GS version of the document (need to use URL-style slashes) 
    767             // e.g. http://host:port/greenstone3/library/collection/lucene-jdbm-demo/document/HASH014602ec89e16b7d431c7627 
    768              
    769             identifier_value = url.replace("oaiserver", "library") + "collection/" + this.coll_name + "/document/" + info.getInfo("identifier"); // OID 
    770         }  
    771         else // use srclinkFile 
    772         {        
    773             // e.g. http://host:port/greenstone3/sites/localsite/collect/backdrop/index/assoc/D0.dir/Cat.jpg 
    774             identifier_value = url.replace("oaiserver", "") + "sites/" + this.site_name  
    775                 + "/collect/" + this.coll_name + "/index/assoc/"  
    776                 + info.getInfo("assocfilepath") + "/" + identifier_value; // srclinkFile 
    777         } 
    778     } // else use gs.OAIResourceURL as-is 
    779      
    780     //logger.info("**** dc:identifier: " + identifier_value); 
    781      
    782     meta_map.put(dc_identifier, identifier_value); 
    783   } 
     741  } 
     742 
    784743} 
    785744