DatatypeServiceImpl.java
/**
* The contents of this file are subject to the OpenMRS Public License
* Version 1.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://license.openmrs.org
*
* 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.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.api.impl;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openmrs.api.DatatypeService;
import org.openmrs.api.context.Context;
import org.openmrs.api.db.ClobDatatypeStorage;
import org.openmrs.api.db.DatatypeDAO;
import org.openmrs.customdatatype.CustomDatatype;
import org.openmrs.customdatatype.CustomDatatypeException;
import org.openmrs.customdatatype.CustomDatatypeHandler;
import org.springframework.transaction.annotation.Transactional;
/**
* Standard implementation of {@link DatatypeService}
* @since 1.9
*/
@Transactional
public class DatatypeServiceImpl extends BaseOpenmrsService implements DatatypeService {
private List<Class<? extends CustomDatatype>> datatypeClasses;
private List<Class<? extends CustomDatatypeHandler>> handlerClasses;
private transient Map<Class<? extends CustomDatatype>, Class<? extends CustomDatatypeHandler>> prioritizedHandlerClasses;
private DatatypeDAO dao;
/**
* Sets the dao
*
* @param dao the dao to set
*/
public void setDao(DatatypeDAO dao) {
this.dao = dao;
}
/**
* @see org.openmrs.api.DatatypeService#getAllDatatypeClasses()
*/
@Override
@Transactional(readOnly = true)
public Set<Class<? extends CustomDatatype<?>>> getAllDatatypeClasses() {
if (datatypeClasses == null) {
populateBeanListsFromContext();
}
return new LinkedHashSet(datatypeClasses);
}
/**
* @see org.openmrs.api.DatatypeService#getAllHandlerClasses()
*/
@Override
@Transactional(readOnly = true)
public Set<Class<? extends CustomDatatypeHandler<?, ?>>> getAllHandlerClasses() {
if (handlerClasses == null) {
populateBeanListsFromContext();
}
return new LinkedHashSet(handlerClasses);
}
private synchronized void populateBeanListsFromContext() {
if (datatypeClasses == null) {
List<CustomDatatype> datatypeBeans = Context.getRegisteredComponents(CustomDatatype.class);
datatypeClasses = new ArrayList<Class<? extends CustomDatatype>>();
for (CustomDatatype<?> dt : datatypeBeans)
datatypeClasses.add(dt.getClass());
}
if (handlerClasses == null) {
List<CustomDatatypeHandler> handlerBeans = Context.getRegisteredComponents(CustomDatatypeHandler.class);
handlerClasses = new ArrayList<Class<? extends CustomDatatypeHandler>>();
for (CustomDatatypeHandler<?, ?> h : handlerBeans)
handlerClasses.add(h.getClass());
}
}
/**
* @see org.openmrs.api.DatatypeService#getDatatype(java.lang.Class, java.lang.String)
*/
@Override
@Transactional(readOnly = true)
public <T extends CustomDatatype<?>> T getDatatype(Class<T> clazz, String config) {
try {
T dt = clazz.newInstance();
dt.setConfiguration(config);
return dt;
}
catch (Exception ex) {
throw new CustomDatatypeException("Failed to instantiate " + clazz + " with config " + config, ex);
}
}
/**
* @see org.openmrs.api.DatatypeService#getHandlerClasses(Class)
*/
@Override
@Transactional(readOnly = true)
public List<Class<? extends CustomDatatypeHandler>> getHandlerClasses(Class<? extends CustomDatatype<?>> datatype) {
List<Class<? extends CustomDatatypeHandler>> ret = new ArrayList<Class<? extends CustomDatatypeHandler>>();
for (Class<? extends CustomDatatypeHandler<?, ?>> candidate : getAllHandlerClasses()) {
if (datatypeClassHandled(candidate).equals(datatype))
ret.add(candidate);
}
// TODO sort the preferred one to the top
return ret;
}
/**
* @param t
* @return the generic type of t or an interface it implements that is a CustomDatatype
*/
private Class datatypeClassHandled(Type t) {
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Type first = pt.getActualTypeArguments()[0];
if (first instanceof Class && CustomDatatype.class.isAssignableFrom((Class) first)) {
return (Class) first;
} else {
return datatypeClassHandled(pt.getRawType());
}
} else if (t instanceof Class) {
for (Type candidate : ((Class) t).getGenericInterfaces()) {
Class ret = datatypeClassHandled(candidate);
if (ret != null)
return ret;
}
}
return null;
}
/**
* @see org.openmrs.api.DatatypeService#getHandler(org.openmrs.customdatatype.CustomDatatype, java.lang.String)
*/
@Override
@Transactional(readOnly = true)
public CustomDatatypeHandler<?, ?> getHandler(CustomDatatype<?> datatype, String handlerConfig) {
if (prioritizedHandlerClasses == null)
prioritizeHandlers();
Class<? extends CustomDatatypeHandler> clazz = prioritizedHandlerClasses.get(datatype.getClass());
if (clazz == null) {
return null;
}
try {
CustomDatatypeHandler<?, ?> ret = clazz.newInstance();
ret.setHandlerConfiguration(handlerConfig);
return ret;
}
catch (Exception ex) {
throw new CustomDatatypeException("Failed to instantiate handler for " + datatype + " with config "
+ handlerConfig, ex);
}
}
/**
* private method that prioritizes all registered handlers so we can quickly determine which to use for
* each datatype
*/
private synchronized void prioritizeHandlers() {
if (prioritizedHandlerClasses == null) {
prioritizedHandlerClasses = new LinkedHashMap<Class<? extends CustomDatatype>, Class<? extends CustomDatatypeHandler>>();
for (Class dt : getAllDatatypeClasses()) {
List<Class<? extends CustomDatatypeHandler>> handlerClasses = getHandlerClasses(dt);
if (handlerClasses == null || handlerClasses.size() == 0) {
prioritizedHandlerClasses.put(dt, null);
} else {
prioritizedHandlerClasses.put(dt, handlerClasses.get(0));
}
}
}
}
/**
* @see org.openmrs.api.DatatypeService#getClobDatatypeStorage(java.lang.Integer)
*/
@Override
@Transactional(readOnly = true)
public ClobDatatypeStorage getClobDatatypeStorage(Integer id) {
return dao.getClobDatatypeStorage(id);
}
/**
* @see org.openmrs.api.DatatypeService#getClobDatatypeStorageByUuid(java.lang.String)
*/
@Override
@Transactional(readOnly = true)
public ClobDatatypeStorage getClobDatatypeStorageByUuid(String uuid) {
return dao.getClobDatatypeStorageByUuid(uuid);
}
/**
* @see org.openmrs.api.DatatypeService#saveClobDatatypeStorage(org.openmrs.api.db.ClobDatatypeStorage)
*/
@Override
public ClobDatatypeStorage saveClobDatatypeStorage(ClobDatatypeStorage storage) {
return dao.saveClobDatatypeStorage(storage);
}
/**
* @see org.openmrs.api.DatatypeService#deleteClobDatatypeStorage(org.openmrs.api.db.ClobDatatypeStorage)
*/
@Override
public void deleteClobDatatypeStorage(ClobDatatypeStorage storage) {
dao.deleteClobDatatypeStorage(storage);
}
}