package com.apothesource.pillfill.rxnorm.service.ndfrt;
import com.apothesource.pillfill.rxnorm.datamodel.ndf.*;
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;
/**
* Default implementation of {@link NdfrtService} backed by the live NIH services.
* http://rxnav.nlm.nih.gov/NdfrtAPIs.html#
*
* Created by Michael Ramirez (michael@pillfill.com) on 7/17/15.
*/
public class NihNdfrtServiceProxy implements NdfrtService {
public static final String URL_BASE = "https://rxnav.nlm.nih.gov/REST/Ndfrt";
public static final String URL_ALL_CONCEPTS_TEMPLATE = URL_BASE + "/allconcepts.json?kind=%s";
public static final String URL_ID_TEMPLATE = URL_BASE + "/id.json?idType=%s&idString=%s";
public static final String URL_SEARCH_NAME_TEMPLATE = URL_BASE + "/search.son?conceptName=%s&kindName=%s";
public static final String URL_ALL_TEMPLATE = URL_BASE + "/allInfo.json?nui=%s";
public static final String URL_CHILDREN_TEMPLATE = URL_BASE + "/childConcepts.json?nui=%s&transitive=%s";
public static final String URL_PARENTS_TEMPLATE = URL_BASE + "/parentConcepts.json?nui=%s&transitive=%s";
public static final String URL_SEARCH_PROPERTY_TEMPLATE = URL_BASE + "/concept.json?propertyName=%s&propertyValue=%s";
public static final String URL_PROPERTY_TEMPLATE = URL_BASE + "/properties.json?nui=%s&propertyName=%s";
public static final String URL_REVERSE_ROLE_TEMPLATE = URL_BASE + "/reverse.json?nui=%s&roleName=%s&transitive=%s";
public static final String URL_ROLE_TEMPLATE = URL_BASE + "/role.json?nui=%s&roleName=%s&transitive=%s";
public static final String URL_ASSOCIATION_TEMPLATE = URL_BASE + "/associations.json?nui=%s&associationName=%s";
public static final String URL_EPCC_TEMPLATE = URL_BASE + "/EPCC.json?nui=%s";
public static final String URL_VAMEMBER_TEMPLATE = URL_BASE + "/VAMember.json?nui=%s";
public static final String URL_VA_TEMPLATE = URL_BASE + "/VA.json?nui=%s";
public static final String URL_VERSION = URL_BASE + "/version.json";
private final Gson gson = new Gson();
@Override
public GroupConceptResponse findConceptsByID(IdTypeNames idType, String idString) throws IOException{
return getResponseFromNihServer(GroupConceptResponse.class,
URL_ID_TEMPLATE,
new String[]{idType.toString(),idString});
}
@Override
public FullConceptResponse getAllInfo(String nui) throws IOException {
return getResponseFromNihServer(FullConceptResponse.class,
URL_ALL_TEMPLATE,
new String[]{nui});
}
@Override
public GroupConceptResponse findConceptsByName(String conceptName, KindNames kindName) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_SEARCH_NAME_TEMPLATE,
new String[]{conceptName, kindName.toString()});
}
@Override
public GroupConceptResponse getChildConcepts(String nui, Boolean transitive) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_CHILDREN_TEMPLATE,
new String[]{nui, transitive.toString()});
}
@Override
public GroupConceptResponse getParentConcepts(String nui, Boolean transitive) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_PARENTS_TEMPLATE,
new String[]{nui, transitive.toString()});
}
@Override
public GroupPropertyResponse getConceptProperties(String nui, PropertyNames propertyName) throws IOException {
return getResponseFromNihServer(GroupPropertyResponse.class,
URL_PROPERTY_TEMPLATE,
new String[]{nui, propertyName.toString()});
}
@Override
public GroupConceptResponse getConceptsByProperty(PropertyNames propertyName, String propertyValue) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_SEARCH_PROPERTY_TEMPLATE,
new String[]{propertyName.toString(), propertyValue});
}
@Override
public GroupConceptResponse getRelatedConceptsByReverseRole(String nui, RoleNames roleName, Boolean transitive) throws IOException{
return getResponseFromNihServer(GroupConceptResponse.class,
URL_REVERSE_ROLE_TEMPLATE,
new String[]{nui, roleName.toString(), transitive.toString()});
}
@Override
public GroupConceptResponse getRelatedConceptsByRole(String nui, RoleNames roleName, Boolean transitive) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_ROLE_TEMPLATE,
new String[]{nui, roleName.toString(), transitive.toString()});
}
@Override
public GroupAssociationResponse getRelatedConceptsByAssociation(String nui, AssociationNames assocName) throws IOException {
return getResponseFromNihServer(GroupAssociationResponse.class,
URL_ASSOCIATION_TEMPLATE,
new String[]{nui, assocName.toString()});
}
@Override
public GroupConceptResponse getEPCClassOfConcept(String nui) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_EPCC_TEMPLATE, new String[]{nui});
}
@Override
public GroupConceptResponse getVAClassMembers(String nui) throws IOException {
return getResponseFromNihServer(GroupConceptResponse.class,
URL_VAMEMBER_TEMPLATE, new String[]{nui});
}
@Override
public ConceptListResponse getVAClassOfConcept(String nui) throws IOException {
return getResponseFromNihServer(ConceptListResponse.class,
URL_VA_TEMPLATE, new String[]{nui});
}
@Override
public VersionResponse getNdfrtVersion() throws IOException {
return getResponseFromNihServer(
new URL(URL_VERSION),
VersionResponse.class
);
}
/**
* Gnarly stream parsing of concept list.
* TODO: Create a cleaner, reusable stream parser
*/
@Override
public void getAllConceptsByKind(List<KindNames> kinds, ConceptHandler handler){
StringBuilder kindsParam = new StringBuilder();
for(KindNames kind : kinds){
kindsParam = kindsParam.append(kind.toString()).append("+");
}
String urlString = String.format(URL_ALL_CONCEPTS_TEMPLATE, kindsParam.toString());
JsonReader reader = null;
try {
HttpURLConnection connection = (HttpURLConnection) new URL(urlString).openConnection();
reader = new JsonReader(new InputStreamReader(connection.getInputStream()));
reader.beginObject();
while(reader.hasNext()){
if(reader.peek() == JsonToken.NAME){
String name = reader.nextName();
if(name.equalsIgnoreCase("groupConcepts")){
reader.beginArray();
reader.beginObject();
}else if(name.equalsIgnoreCase("concept")){
reader.beginArray();
while(reader.peek() == JsonToken.BEGIN_OBJECT){
Concept concept = new Concept();
reader.beginObject();
while(reader.peek() == JsonToken.NAME) {
name = reader.nextName();
String value = reader.nextString();
if (name.equalsIgnoreCase("conceptName")) {
concept.setConceptName(value);
} else if (name.equalsIgnoreCase("conceptNui")) {
concept.setConceptNui(value);
} else if (name.equalsIgnoreCase("conceptKind")) {
concept.setConceptKind(KindNames.valueOf(value));
}
}
handler.handleConcept(concept);
reader.endObject();
}
}
}else{
reader.skipValue();
}
}
handler.onCompleted();
} catch (IOException e) {
handler.onError(e);
} finally {
if(reader != null) try {
reader.close();
} catch (IOException e) {
handler.onError(e);
}
}
}
protected <T> T getResponseFromNihServer(Class<T> classType, String template, String[] parameters) throws IOException{
for(int i = 0; i < parameters.length; i++){
parameters[i] = URLEncoder.encode(parameters[i], "UTF-8");
}
String urlString = String.format(template, (Object[]) parameters);
return getResponseFromNihServer(new URL(urlString), classType);
}
protected <T> T getResponseFromNihServer(URL url, Class<T> classType) throws IOException{
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStreamReader inputReader = new InputStreamReader(connection.getInputStream());
T response = gson.fromJson(inputReader, classType);
inputReader.close();
return response;
}
}