package org.sakaiproject.citation.impl.openurl; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sakaiproject.citation.impl.openurl.ContextObject.Entity; /** * Class to deal with Key Encoded Value format of OpenURLs. * @author buckett */ public class KEVFormat implements Format { private static final Log log = LogFactory.getLog(KEVFormat.class); private static List<EntityFormatter> entityFormatters = new ArrayList<EntityFormatter>(); public static String FORMAT_ID = "info:ofi/fmt:kev:mtx:ctx"; static { entityFormatters.add(new EntityFormatter("rfe", Entity.REFERRING_ENTITY)); entityFormatters.add(new EntityFormatter("rft", Entity.REFERENT)); entityFormatters.add(new EntityFormatter("req", Entity.REQUESTOR)); entityFormatters.add(new EntityFormatter("svc", Entity.SERVICE_TYPE)); entityFormatters.add(new EntityFormatter("res", Entity.RESOLVER)); entityFormatters.add(new EntityFormatter("rfr", Entity.REFERRER)); } public boolean canHandle(String format) { return FORMAT_ID.equals(format); } public ContextObject parse(String data) { if (data == null) { return null; } // We don't rely on the container todo this as it will probably decode it as // UTF-8 or ISO-8859-1 which might not be correct. Map<String, String[]> source = Utils.split(data); String encoding = Utils.getValue(source, "ctx_enc"); if (encoding == null) { encoding = "UTF-8"; } else { // Primo OpenURLs contain a bug where they specify the encoding incorrectly. if (encoding.startsWith("info:ofi/enc:")) { encoding = encoding.substring("info:ofi/enc:".length()); } } try { source = Utils.decode(source, encoding); } catch (IllegalArgumentException iae) { log.warn("Specified decoding failed. "+ iae.getMessage()); // Fallback to UTF-8 so we can try and get somewhere. source = Utils.decode(source, "UTF-8"); } ContextObject contextObject = new ContextObject(); // Need to put them into a map so we know the encoding first. for(Map.Entry<String, String[]> entry: source.entrySet()) { String key = entry.getKey(); for (String value: entry.getValue()) { if (key == null || key.length() < 1) { log.debug("Ignoring empty key with value: "+ value); continue; } if (key.equals("ctx_ver")) { if (ContextObject.VERSION.equals(value)) { log.warn("Context object doesn't match required version, continuing anyway."); } } else if (key.equals("ctx_tim")) { // The time. } else if (key.equals("ctx_id")) { // The ID. } for(EntityFormatter parser: entityFormatters) { // See if anyone wants to claim this key. if (parser.canParse(key)) { parser.parse(contextObject, key, value); break; } } } } return contextObject; } public String encode(ContextObject contextObject) { StringBuilder output = new StringBuilder(); output.append("&"+"ctx_id"+ "="+ ContextObject.VERSION); output.append("&"+"ctx_enc"+ "="+ "UTF-8"); for(EntityFormatter encoder: entityFormatters) { String coEncoded = encoder.encode(contextObject); if (coEncoded != null && coEncoded.length()>0) { output.append('&'); output.append(coEncoded); } } return output.toString(); } /** * Parser/formatter for an entity. * @author buckett * */ private static class EntityFormatter { private static final String DAT_SUFFIX = "_dat"; private static final String REF_SUFFIX = "_ref"; private static final String REF_FMT_SUFFIX = "_ref_fmt"; private static final String VAL_FMT_SUFFIX = "_val_fmt"; private static final String ID_SUFFIX = "_id"; private String prefix; private Entity type; public EntityFormatter(String prefix, Entity type) { this.type = type; this.prefix = prefix; } public boolean canParse(String key) { return key.startsWith(prefix); } public void parse(ContextObject co, String key, String value) { String keyTail = key.substring(prefix.length()); ContextObjectEntity entity = co.getEntities().get(type); if (entity == null) { entity = new ContextObjectEntity(); co.getEntities().put(type, entity); } if (keyTail.equals(ID_SUFFIX)) { entity.addId(value); } else if (keyTail.equals(VAL_FMT_SUFFIX)) { entity.setFormat(value); } else if (keyTail.equals(REF_FMT_SUFFIX)) { entity.setRefFormat(value); } else if (keyTail.equals(REF_SUFFIX)) { entity.setRef(value); } else if (keyTail.equals(DAT_SUFFIX)) { entity.setData(value); } else if (keyTail.startsWith(".")) { String metaKey = keyTail.substring(1); if (metaKey.length() > 0) { entity.addValue(metaKey, value); } } else { log.debug("Ignoring unreconised key: "+ key); } } public String encode(ContextObject co) { ContextObjectEntity entity = co.getEntities().get(type); if (entity == null){ return ""; } URLBuilder output = new URLBuilder("UTF-8"); for (String id: entity.getIds()) { addKEV(output, ID_SUFFIX, id); } addKEV(output, VAL_FMT_SUFFIX, entity.getFormat()); addKEV(output, REF_FMT_SUFFIX, entity.getRefFormat()); addKEV(output, REF_SUFFIX, entity.getRef()); addKEV(output, DAT_SUFFIX, entity.getData()); for(Entry<String, List<String>> entry: entity.getValues().entrySet()) { for(String entryValue: entry.getValue()) { addKEV(output, "."+ entry.getKey(), entryValue); } } return output.toString(); } public void addKEV(URLBuilder output, String key, Object value) { if (value != null) { output.append(prefix+key, value.toString()); } } } }