package org.unc.hive.servlet;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.unc.hive.client.ConceptProxy;
import org.unc.hive.server.VocabularyService;
import edu.unc.ils.mrc.hive.api.ConceptNode;
/**
* Term suggestion servlet returns JSON/JSONP formatted data for use with JQuery/Dynatree component.
*
* Parameters:
* cv: comma separated list of vocabularies (required)
* txt: text (required)
* fmt: "tree" or "list" (optional, default "tree")
* mp: minimum phrase occurrences (optional, default 1)
* ex: pipe delimited list of c.v. terms to exclude (optional)
* callback: callback function for JSONP support (optional)
*/
public class TermSuggestionServlet extends HttpServlet
{
private static final long serialVersionUID = 2357815517668304804L;
static final String PARAM_VOCAB = "cv";
static final String PARAM_TEXT = "tx";
static final String PARAM_MIN_PHRASE_OCCURRENCES = "mp";
static final String PARAM_EXISTING_TERMS = "ex";
static final String PARAM_FORMAT = "fmt";
static final String PARAM_CALLBACK = "callback";
static final String FORMAT_TREE = "tree";
static final String FORMAT_LIST = "list";
public TermSuggestionServlet() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
ServletContext context = this.getServletContext();
String path = context.getRealPath("");
VocabularyService service = VocabularyService.getInstance(path + "/WEB-INF/conf/hive.properties");
List<String> errors = new ArrayList<String>();
// Get the selected vocabularies
String vocab = request.getParameter(PARAM_VOCAB);
List<String> vocabs = new ArrayList<String>();
if (!StringUtils.isEmpty(vocab)) {
vocabs = Arrays.asList(vocab.split("\\,"));
} else {
errors.add("No vocabulary specified");
}
// Read the input text
String text = request.getParameter(PARAM_TEXT);
if (StringUtils.isEmpty(text)) {
errors.add("No input text specified");
}
String algorithm = "maui";
// Get the minimum phrase occurrences, if specified. Otherwise default to 1.
String mp = request.getParameter(PARAM_MIN_PHRASE_OCCURRENCES);
int minPhraseOccur = 1;
if (StringUtils.isEmpty(mp)) {
try {
minPhraseOccur = Integer.valueOf(mp);
} catch (NumberFormatException e) {}
}
// Parse the list of existing terms, if any
String existing = request.getParameter(PARAM_EXISTING_TERMS);
List<String> existingTermsList = new ArrayList<String>();
if (!StringUtils.isEmpty(existing))
existingTermsList = Arrays.asList(existing.split("\\|"));
// Get the requested format
String format = request.getParameter(PARAM_FORMAT);
if (StringUtils.isEmpty(format))
format = FORMAT_TREE;
// Get the callback
String callback = request.getParameter(PARAM_CALLBACK);
if (StringUtils.isEmpty(callback))
callback = null;
String json = "";
try
{
json = "[";
if (format.equals(FORMAT_TREE))
{
List<ConceptNode> tree =null;
tree = service.getTagsAsTree(text, vocabs, 15, minPhraseOccur, algorithm);
if (tree.size() > 0)
{
int i =0;
for (ConceptNode node: tree) {
if (i > 0)
json += ", ";
json += getJsonTreeItem(node, existingTermsList);
i++;
}
}
else
{
json += "{" +
"\"title\": \"No suggestions\"," +
"\"hideCheckbox\": true" +
"}";
}
}
else
{
List<ConceptProxy> list =null;
list = service.getTags(text, vocabs, 15, minPhraseOccur, algorithm);
if (list.size() > 0)
{
int i =0;
for (ConceptProxy concept: list) {
if (i > 0)
json += ", ";
json += getJsonListItem(concept, existingTermsList);
i++;
}
}
else
{
json += "{" +
"\"title\": \"No suggestions\"," +
"\"hideCheckbox\": true" +
"}";
}
}
json += "]";
} catch (Exception e) {
e.printStackTrace();
errors.add("A server error has occurred.");
}
if (callback != null)
{
json = callback + "(" + json + ");";
}
response.setContentType("text/json");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF-8"));
if (errors.size() > 0) {
String errorMsg = "";
for (String error: errors)
errorMsg += error;
errorMsg = getJsonError(errorMsg);
writer.write(errorMsg);
} else {
writer.write(json.toString());
}
writer.close();
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected String getJsonError(String message) {
return "[{" +
"\"title\": \"" + message + "\"," +
"\"hideCheckbox\": true" +
"}]";
}
protected String getJsonTreeItem(ConceptNode node, List<String> ignore) {
StringBuffer json = new StringBuffer("{");
json.append("\"title\": \"");
json.append(node.getLabel());
json.append("\", \"key\": \"");
json.append(node.getUri());
json.append("\", \"url\": \"");
json.append(URLEncoder.encode(node.getUri().replaceAll(" ", "")));
json.append("\"");
String tooltip = "";
List<String> altLabels = node.getAltLabels();
if (altLabels != null && altLabels.size() > 0)
{
tooltip = "Other terms: ";
int i=0;
for (String altLabel: altLabels) {
tooltip += altLabel;
if (i < altLabels.size()-1)
tooltip += ", ";
i++;
}
}
json.append(", \"tooltip\": \"");
json.append(tooltip);
json.append("\"");
if (ignore.contains(node.getLabel())) {
json.append(",\"select\": true");
json.append(",\"unselectable\": \"true\"");
}
if (node.getChildren().size() > 0) {
json.append(", \"isFolder\": \"true\", \"expand\": \"true\", ");
json.append("\"children\": [");
int i = 1;
for (ConceptNode child: node.getChildren()) {
String j = getJsonTreeItem(child, ignore);
json.append(j);
if (i < node.getChildren().size()) {
json.append(",");
}
i++;
}
json.append("]");
}
json.append("}");
return json.toString();
}
protected String getJsonListItem(ConceptProxy node, List<String> ignore) {
StringBuffer json = new StringBuffer("{");
json.append("\"title\": \"");
json.append(node.getPreLabel());
json.append("\", \"key\": \"");
json.append(node.getURI());
json.append("\", \"url\": \"");
json.append(URLEncoder.encode(node.getURI().replaceAll(" ", "")));
json.append("\", \"origin\": \"");
json.append(node.getOrigin());
json.append("\", \"score\": \"");
json.append(node.getScore());
json.append("\"");
if (ignore.contains(node.getPreLabel())) {
json.append(",\"select\": true");
json.append(",\"unselectable\": true");
}
String tooltip = "";
List<String> altLabels = node.getAltLabel();
if (altLabels != null && altLabels.size() > 0)
{
tooltip = "Other terms: ";
int i=0;
for (String altLabel: altLabels) {
tooltip += altLabel;
if (i < altLabels.size()-1)
tooltip += ", ";
i++;
}
}
json.append(", \"tooltip\": \"");
json.append(tooltip);
json.append("\"");
HashMap<String, String> broaders = node.getBroader();
if (broaders != null && broaders.size() > 0)
{
json.append(",\"broaders\": [");
int i = 1;
for (String key: broaders.keySet()) {
String uri = broaders.get(key);
json.append("{\"title\": \"");
json.append(key);
json.append("\", \"url\": \"");
json.append(uri);
json.append("\"}");
if (i < broaders.size()) {
json.append(",");
}
i++;
}
json.append("]");
String path = "";
for (String broader : broaders.keySet())
path += broader;
json.append(", \"path\": \"");
json.append(path);
json.append("\"");
}
json.append("}");
return json.toString();
}
}