package se.cambio.openehr.controller.terminology;
import org.apache.log4j.Logger;
import org.openehr.rm.datatypes.text.CodePhrase;
import org.openehr.rm.datatypes.text.DvCodedText;
import se.cambio.cm.model.generic.dao.GenericCMElementDAO;
import se.cambio.cm.model.facade.terminology.vo.TerminologyNodeVO;
import se.cambio.cm.model.terminology.dto.TerminologyDTO;
import se.cambio.cm.model.util.CMElementDAOFactory;
import se.cambio.openehr.controller.terminology.plugins.CSVTerminologyServicePlugin;
import se.cambio.openehr.controller.terminology.plugins.TerminologyServicePlugin;
import se.cambio.openehr.util.ExceptionHandler;
import se.cambio.openehr.util.exceptions.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
public class TerminologyServiceImpl implements TerminologyService {
private Map<String, TerminologyService> terminologyPlugins;
private Set<String> _supportedTerminologies = null;
private static TerminologyServiceImpl soleInstance;
private static Logger log = Logger.getLogger(TerminologyServiceImpl.class);
public TerminologyServiceImpl(){
init();
}
public void init(){
_supportedTerminologies = new HashSet<String>();
try {
_supportedTerminologies.addAll(getDAO().searchAllIds());
} catch (InternalErrorException e) {
ExceptionHandler.handle(e);
}
}
private TerminologyServicePlugin loadTeminology(TerminologyDTO terminologyDTO){
try{
log.debug("Loading terminology : " + terminologyDTO.getId());
InputStream input = new ByteArrayInputStream(terminologyDTO.getSource().getBytes());
String clazz = TerminologyServiceConfiguration.getInstance().getPluginSourceClass(terminologyDTO.getId());
TerminologyServicePlugin terminologyServicePlugin = null;
if(clazz != null) {
try {
terminologyServicePlugin = (TerminologyServicePlugin)Class.forName(clazz).newInstance();
} catch (Exception e) {
Logger.getLogger(TerminologyServiceImpl.class).error("ERROR instantiating class '"+clazz+"'");
}
} else {
terminologyServicePlugin =
new CSVTerminologyServicePlugin(terminologyDTO.getId());
}
if (terminologyServicePlugin!=null){
terminologyServicePlugin.init(input);
this.registerTerminologyServicePlugin(terminologyServicePlugin);
}
return terminologyServicePlugin;
} catch (Exception e) {
InternalErrorException iee = new InternalErrorException(new Exception("failed to load terminology '"+terminologyDTO.getId()+"'", e));
ExceptionHandler.handle(iee);
}
return null;
}
public static TerminologyServiceImpl getInstance(){
if (soleInstance == null) {
soleInstance = new TerminologyServiceImpl();
}
return soleInstance;
}
public boolean isSubclassOf(CodePhrase a, CodePhrase b)
throws UnsupportedTerminologyException, InvalidCodeException {
log.debug("Checking isSubclassOf (" + a + ", " + b + ")");
checkTerminologySupported(a);
checkTerminologySupported(b);
// TODO Assuming both terminologies are equal
String terminologyId = a.getTerminologyId().getValue();
boolean ret = false;
if (isOntology(terminologyId)) {
//TODO Ontology support
} else {
ret = getTerminologyServicePlugin(terminologyId).isSubclassOf(a, b);
}
log.debug("isSubclassOf: " + ret);
return ret;
}
public boolean isSubclassOf(CodePhrase code, Set<CodePhrase> codes)
throws UnsupportedTerminologyException, InvalidCodeException {
log.debug("Checking isSubclassOf (" + code + ", " + codes + ")");
checkTerminologySupported(code);
for (CodePhrase cp : codes) {
checkTerminologySupported(cp);
}
// TODO Assuming both terminologies are equal
// otherwise mapping files are needed
String terminologyId = code.getTerminologyId().getValue();
boolean ret = false;
if (isOntology(terminologyId)) {
/*TODO Ontology support:
log.debug("Checking isSubclassOf using ontology..");
for (CodePhrase cp : codes) {
if (checkSubclassOf(code, cp)) {
ret = true;
break;
}
}
*/
} else {
log.debug("Checking isSubclassOf using classification..");
ret = getTerminologyServicePlugin(terminologyId).isSubclassOf(code, codes);
}
log.debug("isSubclassOf: " + ret);
return ret;
}
public boolean isTerminologySupported(String terminologyId) {
return _supportedTerminologies.contains(terminologyId);
}
private boolean isOntology(String terminologyId) {
return false;//TODO Ontology support: ontologies.containsKey(terminologyId);
}
/*TODO Ontology support:
private boolean checkSubclassOf(CodePhrase a, CodePhrase b)
throws UnsupportedTerminologyException, InvalidCodeException {
// TODO: fix support for other terminologies/ontologies using mappings
String terminology = a.getId().getValue();
OWLDataFactory dataFactory = dataFactories.get(terminology);
OWLReasoner reasoner = reasoners.get(terminology);
String url = configuration.terminologyURL(terminology);
OWLClass subClass = dataFactory.getOWLClass(IRI.create(url
+ a.getCodeString()));
OWLClass superClass = dataFactory.getOWLClass(IRI.create(url
+ b.getCodeString()));
OWLAxiom axiom = dataFactory
.getOWLSubClassOfAxiom(subClass, superClass);
return reasoner.isEntailed(axiom);
}
*/
private void checkTerminologySupported(CodePhrase code)
throws UnsupportedTerminologyException {
checkTerminologySupported(code.getTerminologyId().getValue());
}
private void checkTerminologySupported(String terminology)
throws UnsupportedTerminologyException {
if (!isTerminologySupported(terminology)) {
throw new UnsupportedTerminologyException(terminology + " not supported");
}
}
public boolean isTerminologySupported(CodePhrase code) {
return isTerminologySupported(code.getTerminologyId().getValue());
}
public TerminologyNodeVO retrieveAllSubclasses(CodePhrase concept, CodePhrase language)
throws UnsupportedTerminologyException,
UnsupportedLanguageException, InvalidCodeException {
log.debug("retrieve all subclasses of " + concept);
TerminologyNodeVO node = null;
String terminologyId = concept.getTerminologyId().getValue();
if (isOntology(terminologyId)) {
/*TODO Ontology support:
String terminology = concept.getId().getValue();
OWLDataFactory dataFactory = dataFactories.get(terminology);
checkLanguageSupported(language);
String url = configuration.terminologyURL(terminology);
OWLClass klass = dataFactory.getOWLClass(IRI.create(url
+ concept.getCodeString()));
node = retrieveSubclass(klass, terminology);
*/
} else {
TerminologyService ts = getTerminologyServicePlugin(terminologyId);
if (ts != null) {
node = ts.retrieveAllSubclasses(concept, language);
} else {
throw new UnsupportedTerminologyException(
"Unknown terminology '" + terminologyId + "'");
}
}
return node;
}
/*TODO Ontology support:
private TerminologyNodeVO retrieveSubclass(OWLClass klass, String terminology) {
OWLReasoner reasoner = reasoners.get(terminology);
String lable = retrieveLable(klass, terminology);
String url = configuration.terminologyURL(terminology);
String code = klass.getIRI().toString().substring(url.length());
TerminologyNodeVO node = new TerminologyNodeVO(new DvCodedText(lable, new CodePhrase(terminology,
code)));
NodeSet<OWLClass> subclasses = reasoner.getSubClasses(klass, true);
log.debug("subclasses isEmpty: " + subclasses.isEmpty());
Set<org.semanticweb.owlapi.reasoner.Node<OWLClass>> nodes = subclasses
.getNodes();
for (org.semanticweb.owlapi.reasoner.Node<OWLClass> child : nodes) {
OWLClass owlk = child.getRepresentativeElement();
if (!owlk.isOWLNothing()) {
node.addChild(retrieveSubclass(
child.getRepresentativeElement(), terminology));
}
}
return node;
}
private String retrieveLable(OWLClass klass, String terminology) {
OWLOntology ontology = ontologies.get(terminology);
String label = null;
Set<OWLAnnotation> annotations = klass.getAnnotations(ontology);
for (OWLAnnotation ann : annotations) {
if (ann.getProperty().isLabel()) {
OWLAnnotationValue v = ann.getValue();
if (v instanceof OWLLiteral) {
OWLLiteral l = (OWLLiteral) v;
label = l.getLiteral();
break;
}
}
}
log.debug("retrieve lable (" + label + ") for class " + klass);
return label;
}
*/
private void checkLanguageSupported(CodePhrase language)
throws UnsupportedLanguageException {
if (!TerminologyServiceConfiguration.languageSupported(language)) {
throw new UnsupportedLanguageException("Language (" + language
+ ") not supported.");
}
}
public boolean hasAttributeOfValue(CodePhrase concept,
CodePhrase attribute, CodePhrase value)
throws UnsupportedTerminologyException, UnknownPropertyException,
UnsupportedLanguageException {
// TODO Auto-generated method stub
return false;
}
public List<DvCodedText> retrieveAllPossibleValues(CodePhrase attribute,
CodePhrase language) throws UnsupportedTerminologyException,
UnknownPropertyException, UnsupportedLanguageException {
// TODO Auto-generated method stub
return null;
}
public List<TerminologyNodeVO> retrieveAll(String terminologyId, CodePhrase language)
throws UnsupportedTerminologyException, UnsupportedLanguageException {
checkLanguageSupported(language);
if (isOntology(terminologyId)) {
// TODO
return null;
} else {
TerminologyService ts = getTerminologyServicePlugin(terminologyId);
if (ts != null) {
return ts.retrieveAll(terminologyId, language);
} else {
throw new UnsupportedTerminologyException(
"Unknown terminology '" + terminologyId + "'");
}
}
}
public List<TerminologyNodeVO> retrieve(String expression, CodePhrase language)
throws UnsupportedTerminologyException,
UnsupportedLanguageException {
// TODO Auto-generated method stub
return null;
}
public boolean hasPropertyOfValue(CodePhrase concept, CodePhrase property,
CodePhrase value) throws UnsupportedTerminologyException,
UnknownPropertyException {
// TODO Auto-generated method stub
return false;
}
public String retrieveTerm(CodePhrase concept, CodePhrase language)
throws UnsupportedTerminologyException,
UnsupportedLanguageException {
String terminologyId = concept.getTerminologyId().getValue();
if (isOntology(terminologyId)) {
// TODO
return null;
} else {
TerminologyService ts = getTerminologyServicePlugin(terminologyId);
if (ts != null) {
return ts.retrieveTerm(concept, language);
} else {
throw new UnsupportedTerminologyException(
"Unknown terminology '" + terminologyId + "'");
}
}
}
public boolean isValidCodePhrase(CodePhrase codePhrase) {
String terminologyId = codePhrase.getTerminologyId().getValue();
if (isOntology(terminologyId)) {
// TODO Check valid codes
return true;
} else {
TerminologyService ts = getTerminologyServicePlugin(terminologyId);
if (ts != null) {
return ts.isValidCodePhrase(codePhrase);
} else {
return false;
}
}
}
private TerminologyService getTerminologyServicePlugin(String terminologyId) {
TerminologyService terminologyService = getTerminologyServicePluginMap().get(terminologyId);
if (terminologyService == null && isSupported(terminologyId)){
try {
Collection<TerminologyDTO> terminologyDTOs = getDAO().searchByIds(Collections.singleton(terminologyId));
TerminologyDTO terminologyDTO = terminologyDTOs.iterator().next();
terminologyService = loadTeminology(terminologyDTO);
} catch (InstanceNotFoundException e) {
ExceptionHandler.handle(e);
} catch (InternalErrorException e) {
ExceptionHandler.handle(e);
}
}
return terminologyService;
}
public void registerTerminologyServicePlugin(
TerminologyServicePlugin terminologyService) {
getTerminologyServicePluginMap().put(
terminologyService.getTerminologyId(), terminologyService);
}
private Map<String, TerminologyService> getTerminologyServicePluginMap() {
if (terminologyPlugins == null) {
terminologyPlugins = new HashMap<String, TerminologyService>();
}
return terminologyPlugins;
}
private boolean isSupported(String terminologyId){
return getSupportedTerminologies().contains(terminologyId);
}
public Collection<String> getSupportedTerminologies() {
return Collections.unmodifiableCollection(_supportedTerminologies);
}
public GenericCMElementDAO<TerminologyDTO> getDAO() throws InternalErrorException {
return CMElementDAOFactory.getInstance().getDAO(TerminologyDTO.class);
}
}
/*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 2.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an 'AS IS' basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
*
* The Initial Developers of the Original Code are Iago Corbal and Rong Chen.
* Portions created by the Initial Developer are Copyright (C) 2012-2013
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Software distributed under the License is distributed on an 'AS IS' basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* ***** END LICENSE BLOCK *****
*/