diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java index a10c2a540..2f7ec2f8f 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/IndividualController.java @@ -81,7 +81,7 @@ public class IndividualController extends FreemarkerHttpServlet { private static final String TEMPLATE_INDIVIDUAL_DEFAULT = "individual.ftl"; private static final String TEMPLATE_HELP = "individual-help.ftl"; - + private static MapqsMap; @Override protected ResponseValues processRequest(VitroRequest vreq) { @@ -486,42 +486,36 @@ public class IndividualController extends FreemarkerHttpServlet { */ protected ContentType checkForLinkedDataRequest(String url, VitroRequest vreq ) { try { - ContentType contentType = null; Matcher m; // Check for url param specifying format String formatParam = (String) vreq.getParameter("format"); if (formatParam != null) { m = RDFXML_FORMAT.matcher(formatParam); if ( m.matches() ) { - return new ContentType(RDFXML_MIMETYPE); + return ContentType.RDFXML; } m = N3_FORMAT.matcher(formatParam); if( m.matches() ) { - return new ContentType(N3_MIMETYPE); + return ContentType.N3; } m = TTL_FORMAT.matcher(formatParam); if( m.matches() ) { - return new ContentType(TTL_MIMETYPE); + return ContentType.TURTLE; } } //check the accept header String acceptHeader = vreq.getHeader("accept"); - if (acceptHeader != null) { - List actualContentTypes = new ArrayList(); - actualContentTypes.add(new ContentType( XHTML_MIMETYPE )); - actualContentTypes.add(new ContentType( HTML_MIMETYPE )); - - actualContentTypes.add(new ContentType( RDFXML_MIMETYPE )); - actualContentTypes.add(new ContentType( N3_MIMETYPE )); - actualContentTypes.add(new ContentType( TTL_MIMETYPE )); - - contentType = ContentType.getBestContentType(acceptHeader,actualContentTypes); - if (contentType!=null && ( - RDFXML_MIMETYPE.equals(contentType.getMediaType()) || - N3_MIMETYPE.equals(contentType.getMediaType()) || - TTL_MIMETYPE.equals(contentType.getMediaType()) )) - return contentType; + if (acceptHeader != null) { + String ctStr = ContentType.getBestContentType( + ContentType.getTypesAndQ(acceptHeader), + getAcceptedContentTypes()); + + if (ctStr!=null && ( + RDFXML_MIMETYPE.equals(ctStr) || + N3_MIMETYPE.equals(ctStr) || + TTL_MIMETYPE.equals(ctStr) )) + return new ContentType(ctStr); } /* @@ -532,15 +526,15 @@ public class IndividualController extends FreemarkerHttpServlet { */ m = RDF_REQUEST.matcher(url); if( m.matches() ) { - return new ContentType(RDFXML_MIMETYPE); + return ContentType.RDFXML; } m = N3_REQUEST.matcher(url); if( m.matches() ) { - return new ContentType(N3_MIMETYPE); + return ContentType.N3; } m = TTL_REQUEST.matcher(url); if( m.matches() ) { - return new ContentType(TTL_MIMETYPE); + return ContentType.TURTLE; } @@ -550,10 +544,6 @@ public class IndividualController extends FreemarkerHttpServlet { return null; } - private ContentType getContentTypeFromString(String string) { - - return null; - } @SuppressWarnings("unused") private boolean checkForSunset(VitroRequest vreq, Individual entity) { @@ -750,4 +740,21 @@ public class IndividualController extends FreemarkerHttpServlet { return new TemplateResponseValues(Template.TITLED_ERROR_MESSAGE.toString(), body); } + public static Map getAcceptedContentTypes() { + if( qsMap == null ){ + HashMap map = new HashMap(); + map.put(HTML_MIMETYPE , 0.5f); + map.put(XHTML_MIMETYPE, 0.5f); + map.put("application/xml", 0.5f); + map.put(RDFXML_MIMETYPE, 1.0f); + map.put(N3_MIMETYPE, 1.0f); + map.put(TTL_MIMETYPE, 1.0f); + qsMap = map; + } + return qsMap; + } + +// static String getAcceptedContentType(String acceptHeader,Mapqs){ +// +// } } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/ContentType.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/ContentType.java index 3cce67e01..02693d654 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/web/ContentType.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/web/ContentType.java @@ -16,15 +16,14 @@ package edu.cornell.mannlib.vitro.webapp.web; */ /* + * THIS CODE HAS BEEN MODIFIED: + * The members of the Vitro/VIVO project have modified this code. It has + * been modified from the version produced by Google Inc. + * * The code in this file is from the Google data API project 1.40.3 on 2010-03-08. - * See full license from gdata at bottom of this file. - * The Vitro project only uses the method getBestContentType(). + * See full license from gdata at bottom of this file. */ -//package com.google.gdata.util; - -//import com.google.gdata.client.Service; - import java.io.Serializable; import java.util.Collections; import java.util.HashMap; @@ -215,6 +214,26 @@ public class ContentType implements Serializable { */ public static final ContentType ANY = new ContentType("*/*").lock(); + + + /** + * A ContetType that describes RDF/XML. + * Added by Brian Caruso for VIVO. + */ + public final static ContentType RDFXML = new ContentType("application/rdf+xml").lock(); + + /** + * A ContetType that describes N3 RDF, this is unofficial and unregistered + * Added by Brian Caruso for VIVO. + */ + public final static ContentType N3 = new ContentType("text/n3").lock(); + + /** + * A ContetType that describes turtle RDF, this is unofficial and unregistered + * Added by Brian Caruso for VIVO. + */ + public final static ContentType TURTLE = new ContentType("text/turtle").lock(); + /** * Determines the best "Content-Type" header to use in a servlet response * based on the "Accept" header from a servlet request. @@ -301,6 +320,81 @@ public class ContentType implements Serializable { return null; } +/** + * Gets the best content type based weighted q from client accept header and + * the server weighted q of the extent that the type conveys the resource. + * + * From suggestions by Tim Berners-Lee at http://www.w3.org/DesignIssues/Conneg + * + * @param clentAcceptsTypes types the client can accept with Q weights. + * @param serverTypes types the server can provide with Q weights. + * @return returns content type of best match or null if no match. + */ + public static String getBestContentType( + Map clientAcceptsTypes, + Map serverTypes) { + float maxQ = 0.0f; + String type = null; + for( String serverType: serverTypes.keySet()){ + float serverQ = serverTypes.get(serverType); + Float clientQ = clientAcceptsTypes.get(serverType); + if( clientQ != null && ((serverQ * clientQ)+ 0.001) > (maxQ + 0.001) ){ + maxQ = (serverQ * clientQ); + type = serverType; + } + } + return type; + } + + /** + * This method was added by Brian Caruso of the VIVO project. March 15 2011. + * + * @param acceptHeader + * @return the types and the q values from the accept header + */ + public static Map getTypesAndQ(String acceptHeader){ + if (acceptHeader == null) { + return Collections.emptyMap(); + } + + Map qcMap = new HashMap(); + // iterate over all of the accepted content types + String[] acceptedTypes = acceptHeader.split(","); + for (String acceptedTypeString : acceptedTypes) { + + // create the content type object + ContentType acceptedContentType; + try { + acceptedContentType = new ContentType(acceptedTypeString.trim()); + } catch (IllegalArgumentException ex) { + // ignore exception + continue; + } + + // parse the "q" value (default of 1) + float curQ = 1; + try { + String qAttr = acceptedContentType.getAttribute("q"); + if (qAttr != null) { + float qValue = Float.valueOf(qAttr); + if (qValue <= 0 || qValue > 1) { + continue; + } + curQ = qValue + 0.0001F; + } + } catch (NumberFormatException ex) { + // ignore exception + continue; + } + + if( acceptedContentType != null ){ + qcMap.put(acceptedContentType.getMediaType(), curQ); + } + } + + return qcMap; + } + /** * Constructs a new instance with default media type */ @@ -407,6 +501,8 @@ public class ContentType implements Serializable { return sb.toString(); } + private Float q=1.0f; + private HashMap attributes = new HashMap(); /** @@ -445,6 +541,17 @@ public class ContentType implements Serializable { return attributes.get(name); } + /** + * returns q associated with content type. + */ + public float getQ(){ + return q; + } + + public void setQ(float q){ + this.q = q; + } + /* * Returns the charset attribute of the content type or null if the * attribute has not been set. @@ -545,6 +652,7 @@ public class ContentType implements Serializable { return (type.hashCode() * 31 + subType.hashCode()) * 31 + attributes .hashCode(); } + } /* diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/web/ContentTypeTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/web/ContentTypeTest.java new file mode 100644 index 000000000..bef88155c --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/web/ContentTypeTest.java @@ -0,0 +1,57 @@ +package edu.cornell.mannlib.vitro.webapp.web; + +import java.util.Map; + +import junit.framework.Assert; + +import org.junit.Test; + +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.IndividualController; + + +public class ContentTypeTest { + + @Test + public void typeAndQTest1(){ + Map map = ContentType.getTypesAndQ( + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/rdf+xml;q=0.93,text/rdf+n3;q=0.5"); + Assert.assertEquals(1.0f, map.get("text/html"), 0.01f); + Assert.assertEquals(1.0f, map.get("application/xhtml+xml"), 0.01f); + Assert.assertEquals(0.9f, map.get("application/xml"), 0.01f); + Assert.assertEquals(0.93f,map.get("application/rdf+xml"), 0.01f); + Assert.assertEquals(0.5f, map.get("text/rdf+n3"), 0.01f); + Assert.assertEquals(0.8f,map.get("*/*"), 0.01f); + } + + @Test + public void typeAndQTest2(){ + Map map = ContentType.getTypesAndQ( + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + Assert.assertEquals(1.0f, map.get("text/html"), 0.01f); + Assert.assertEquals(1.0f, map.get("application/xhtml+xml"), 0.01f); + Assert.assertEquals(0.9f, map.get("application/xml"), 0.01f); + Assert.assertEquals(0.8f,map.get("*/*"), 0.01f); + } + + @Test + public void testWeightedBestContentTypeForTabulator(){ + //accept header from tabulator + Map clientAccepts = ContentType.getTypesAndQ( + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/rdf+xml;q=0.93,text/rdf+n3;q=0.5"); + + Map serverTypes = IndividualController.getAcceptedContentTypes(); + + Assert.assertEquals("application/rdf+xml", ContentType.getBestContentType(clientAccepts, serverTypes)); + } + + @Test + public void testWeightedBestContentTypeForFirefox(){ + //accept header from normal firefox + Map clientAccepts = ContentType.getTypesAndQ( + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); + + Map serverTypes = IndividualController.getAcceptedContentTypes(); + + Assert.assertEquals("application/xhtml+xml", ContentType.getBestContentType(clientAccepts, serverTypes)); + } +}