/**********************************************************************************
*
* Copyright (c) 2003, 2004, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package edu.indiana.lib.twinpeaks.search.sru.ss360search;
import java.util.Iterator;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import edu.indiana.lib.osid.base.repository.http.CreatorPartStructure;
import edu.indiana.lib.osid.base.repository.http.DOIPartStructure;
import edu.indiana.lib.osid.base.repository.http.DataSource;
import edu.indiana.lib.osid.base.repository.http.DatePartStructure;
import edu.indiana.lib.osid.base.repository.http.EditionPartStructure;
import edu.indiana.lib.osid.base.repository.http.EndPagePartStructure;
import edu.indiana.lib.osid.base.repository.http.InLineCitationPartStructure;
import edu.indiana.lib.osid.base.repository.http.IsnIdentifierPartStructure;
import edu.indiana.lib.osid.base.repository.http.IssuePartStructure;
import edu.indiana.lib.osid.base.repository.http.LanguagePartStructure;
import edu.indiana.lib.osid.base.repository.http.OpenUrlPartStructure;
import edu.indiana.lib.osid.base.repository.http.PagesPartStructure;
import edu.indiana.lib.osid.base.repository.http.PreferredUrlPartStructure;
import edu.indiana.lib.osid.base.repository.http.PublisherPartStructure;
import edu.indiana.lib.osid.base.repository.http.SourceTitlePartStructure;
import edu.indiana.lib.osid.base.repository.http.StartPagePartStructure;
import edu.indiana.lib.osid.base.repository.http.SubjectPartStructure;
import edu.indiana.lib.osid.base.repository.http.TypePartStructure;
import edu.indiana.lib.osid.base.repository.http.URLPartStructure;
import edu.indiana.lib.osid.base.repository.http.VolumePartStructure;
import edu.indiana.lib.osid.base.repository.http.YearPartStructure;
import edu.indiana.lib.twinpeaks.search.MatchItem;
import edu.indiana.lib.twinpeaks.search.QueryBase;
import edu.indiana.lib.twinpeaks.search.SearchResultBase;
import edu.indiana.lib.twinpeaks.util.DomUtils;
import edu.indiana.lib.twinpeaks.util.LogUtils;
import edu.indiana.lib.twinpeaks.util.SearchException;
import edu.indiana.lib.twinpeaks.util.SessionContext;
import edu.indiana.lib.twinpeaks.util.StatusUtils;
import edu.indiana.lib.twinpeaks.util.StringUtils;
/**
* Parse the 360 Search XML response
*/
public class Response extends SearchResultBase implements Constants
{
private static org.apache.commons.logging.Log _log = LogUtils.getLog(Response.class);
/**
* Session context
*/
private SessionContext sessionContext;
/**
* Constructor
*/
public Response()
{
super();
}
/**
* Save various attributes of the general search request
*
* @param query The QueryBase extension that sent the search request
*/
public void initialize(QueryBase query)
{
super.initialize(query);
sessionContext = SessionContext.getInstance(_sessionId);
}
/**
* Parse the search engine XML response - namespace aware
*
* Overrides <code>SearchResultBase#parseResponse()</code>
*
* @return Response as a DOM Document
*/
protected Document parseResponse() throws SearchException
{
try
{
return DomUtils.parseXmlBytesNS(_searchResponseBytes);
}
catch (Exception exception)
{
throw new SearchException(exception.toString());
}
}
/**
* Parse the response
*/
public void doParse()
{
Document responseDocument = getSearchResponseDocument();
Element responseRoot = responseDocument.getDocumentElement();
Element element;
NodeList recordList;
/*
* Examine each result record
*/
element = DomUtils.getElementNS(NS_SRW, responseRoot, "records");
recordList = DomUtils.getElementListNS(NS_SRW, element, "record");
for (int i = 0; i < recordList.getLength(); i++)
{
MatchItem item;
Element citationElement, recordElement;
String title, description, date;
String recordId, target;
boolean typeAdded;
if (i == 0)
{ /*
* The first record is the provider list (with status details)
*/
continue;
}
/*
* A data record
*
* Locate the citation root
*/
recordElement = (Element) recordList.item(i);
citationElement = DomUtils.getElementNS(NS_CS, recordElement, "citation");
if (citationElement == null)
{
_log.error("No citation element in 360 Search response");
displayXml(recordElement);
throw new SearchException("No citation element in 360 Search response");
}
/*
* Find the target database, increment the hit count
*/
target = DomUtils.getTextNS(NS_CS, citationElement, "databaseId");
if (StringUtils.isNull(target))
{
_log.warn("No database id in 360 Search response, ignoring");
displayXml(citationElement);
continue;
}
StatusUtils.updateHits(sessionContext, target);
/*
* Find the unique record identifier
*/
recordId = DomUtils.getTextNS(NS_DC, citationElement, "identifier");
if (StringUtils.isNull(recordId))
{
recordId = "";
}
/*
* Title, abstract
*/
title = DomUtils.getTextNS(NS_DC, citationElement, "title");
if (StringUtils.isNull(title))
{
title = "";
}
title = ResultUtils.normalize(title, "title", target);
description = DomUtils.getTextNS(NS_DCTERMS, citationElement, "abstract");
if (StringUtils.isNull(description))
{
description = "";
}
description = ResultUtils.normalize(description, "abstract", target);
/*
* Save select search result data
*/
item = new MatchItem();
/*
* Title, abstract, database, record identifier
*/
item.setDisplayName(title);
item.setDescription(description);
item.setDatabase(target);
item.setId(recordId);
/*
* Publisher (not supported - is this right?)
*
* addPartStructure(NS_DC, citationElement, "source", item,
* PublisherPartStructure.getPartStructureId());
*/
/*
* In-line Citation information (is this right?)
*/
addPartStructure(NS_DC, citationElement, "source", item,
InLineCitationPartStructure.getPartStructureId());
/*
* Source title, volume, issue
*/
addPartStructure(NS_DC, citationElement, "source", item,
SourceTitlePartStructure.getPartStructureId());
addPartStructure(NS_CS, citationElement, "volume", item,
VolumePartStructure.getPartStructureId());
addPartStructure(NS_CS, citationElement, "issue", item,
IssuePartStructure.getPartStructureId());
/*
* Pages
*/
addPartStructure(NS_CS, citationElement, "pages", item,
PagesPartStructure.getPartStructureId());
addPartStructure(NS_CS, citationElement, "spage", item,
StartPagePartStructure.getPartStructureId());
/*
* Date and Year
*/
date = null;
/*
* See if we have a normalized date (YYYY-MM-DD) (and/or the first author)
*/
element = DomUtils.getElementNS(NS_CS, citationElement, "normalizedData");
if (element != null)
{
date = DomUtils.getTextNS(NS_DCTERMS, element, "issued");
}
/*
* No normalized date ...
*/
if (StringUtils.isNull(date))
{
addPartStructure(NS_CS, citationElement, "issued", item,
YearPartStructure.getPartStructureId());
}
else
{ /*
* Use the normalized date; set the year as well
*/
addPartStructure(item, DatePartStructure.getPartStructureId(), date);
if (date.length() == 10)
{
String year = date.substring(0, 4);
addPartStructure(item, YearPartStructure.getPartStructureId(), year);
}
}
/*
* Type of publication
*/
typeAdded = addPartStructure(NS_DC, citationElement, "type", item,
TypePartStructure.getPartStructureId());
/*
* URLs
*/
addPartStructureList(NS_CS, citationElement, "url", item,
URLPartStructure.getPartStructureId());
/*
* Identifiers (ISSN, ISBN)
*/
if (addPartStructure(NS_CS, citationElement, "isbn", item,
IsnIdentifierPartStructure.getPartStructureId()))
{
/*
* Can we assume it's a book if we find an ISBN?
*/
if (!typeAdded)
{
addPartStructure(item, TypePartStructure.getPartStructureId(), "book");
}
}
addPartStructure(NS_CS, citationElement, "issn", item,
IsnIdentifierPartStructure.getPartStructureId());
/*
* Author (add each in turn)
*/
addAuthorList(citationElement, item);
/*
* Find the preferred and Open URLs
*/
NodeList urlList = DomUtils.getElementListNS(NS_CS, citationElement, "url");
if (urlList.getLength() == 0) _log.warn("*** No URL element!");
for (int urlIndex = 0; urlIndex < urlList.getLength(); urlIndex++)
{
String type, url;
element = (Element) urlList.item(urlIndex);
type = element.getAttribute("type");
url = DomUtils.getText(element);
_log.debug("link resolver" + " VS " + type);
if (!StringUtils.isNull(url))
{
if ("link resolver".equals(type))
{
int index = url.indexOf("?");
if (index == -1) index = 0;
addPartStructure(item,
OpenUrlPartStructure.getPartStructureId(),
url.substring(index));
}
else
{
addPartStructure(item,
PreferredUrlPartStructure.getPartStructureId(),
url);
}
}
}
/*
* Save the asset component we just created
*/
addItem(item);
}
}
/*
* Helpers
*/
/**
* Locate (and save) the authors (omit the normalized "first author")
* @param citationElement The root element of this citation
* @param item A MatchItem (eg Asset) object
*/
protected void addAuthorList(Element citationElement, MatchItem item)
{
String firstAuthor;
NodeList authorList;
Element element;
/*
* See if we have a normalized "first author"
*/
firstAuthor = null;
element = DomUtils.getElementNS(NS_CS, citationElement, "normalizedData");
if (element != null)
{
firstAuthor = DomUtils.getTextNS(NS_DC, element, "creator");
}
/*
* Find all of the creator elements (this will include the first author)
*/
authorList = DomUtils.getElementListNS(NS_DC, citationElement, "creator");
/*
* An author list? No? Look for a "first author" (shouldn't happen)
*/
if (authorList.getLength() == 0)
{
if (firstAuthor != null)
{
addPartStructure(item,
CreatorPartStructure.getPartStructureId(),
firstAuthor);
return;
}
}
/*
* Add each author in turn (omitting the "first author")
*/
for (int i = 0; i < authorList.getLength(); i++)
{
String author = DomUtils.getText(authorList.item(i));
if (StringUtils.isNull(author))
{
continue;
}
if ((firstAuthor == null) || (!firstAuthor.equalsIgnoreCase(author)))
{
addPartStructure(item,
CreatorPartStructure.getPartStructureId(),
author);
}
}
}
/**
* Locate (and save as PartStructure id/value pairs) all matching items
*
* @param namespace Root element namespace URI
* @param rootElement Start looking here
* @param partDataName Name of the XML element we're looking for
* @param item Current MatchItem (eg Asset)
* @param id Part ID
* @return true If PartStructure data was added, false if none found
*/
private boolean addPartStructureList(String namespace,
Element parentElement,
String partDataName,
MatchItem item,
org.osid.shared.Id id)
{
return addPartStructureList(namespace, parentElement, partDataName,
null, null, item, id);
}
/**
* Locate (and save as PartStructure id/value pairs) all matching items
*
* @param namespace Root element namespace URI
* @param rootElement Start looking here
* @param partDataName Name of the XML element we're looking for
* @param partAttributeName Name of the XML attribute we're looking for
* (use null to skip the attribute check)
* @param partAttributeValue Attribute value we're looking for
* (can be null if partAttributeName is null)
* @param item Current MatchItem (eg Asset)
* @param id Part ID
* @return true If PartStructure data was added, false if none found
*/
private boolean addPartStructureList(String namespace,
Element parentElement,
String partDataName,
String partAttributeName,
String partAttributeValue,
MatchItem item,
org.osid.shared.Id id)
{
NodeList nodeList = DomUtils.getElementListNS(namespace,
parentElement,
partDataName);
boolean partsAdded = false;
for (int i = 0; i < nodeList.getLength(); i++)
{
Element element = (Element) nodeList.item(i);
if ((partAttributeName == null) ||
(element.getAttribute(partAttributeName).equals(partAttributeValue)))
{
String text = DomUtils.getText(element);
if (!StringUtils.isNull(text))
{
addPartStructure(partDataName, item, id, text);
partsAdded = true;
}
}
}
return partsAdded;
}
/**
* Locate (in response XML) and save PartStructure data
*
* @param namespace Namespace specification
* @param parentElement Parent Element - the search starts here
* @param partDataName The name of the child element where Part data is found
* @param item Current MatchItem (eg Asset)
* @param id Part ID
* @return true If Part data was added, false if no data was found
*/
private boolean addPartStructure(String namespace,
Element parentElement,
String partDataName,
MatchItem item,
org.osid.shared.Id id)
{
String value = DomUtils.getTextNS(namespace, parentElement, partDataName);
if (StringUtils.isNull(value))
{
return false;
}
return addPartStructure(partDataName, item, id, value);
}
/**
* Save (add new) PartStructure data
*
* @param item Current MatchItem (eg Asset)
* @param id Part ID
* @param value Part value
* @return true If Part data was added, false if no data was found
*/
private boolean addPartStructure(MatchItem item,
org.osid.shared.Id id,
String value)
{
return addPartStructure(null, item, id, value);
}
/**
* Add new PartStructure data - the data itself is normalized as required
*
* @param partDataName The XML element name of this part
* @param item Current MatchItem (eg Asset)
* @param id Part ID
* @param value Part value
* @return true If Part data was added, false if no data was found
*/
private boolean addPartStructure(String partDataName,
MatchItem item,
org.osid.shared.Id id,
String value)
{
boolean partAdded = false;
String text = value;
if (text != null)
{
text = ResultUtils.normalize(text, partDataName, item.getDatabase());
}
if (!StringUtils.isNull(text))
{
item.addPartStructure(id, text);
partAdded = true;
}
return partAdded;
}
/*
* XML helpers
*/
/**
* Display XML (with optional warning header)
*
* @param errorText Error message (null for none)
* @param recordElement The XML object to display (Document or Element)
*/
private static void displayXml(String errorText, Object xmlObject) {
try
{
LogUtils.displayXml(_log, errorText, xmlObject);
} catch (Exception ignore) { }
}
/**
* Display XML information
*
* @param xmlObject XML to display (Document or Element)
*/
private void displayXml(Object xmlObject) {
try
{
LogUtils.displayXml(_log, xmlObject);
} catch (Exception ignore) { }
}
}