001package ca.uhn.fhir.jpa.model.util;
002
003/*
004 * #%L
005 * HAPI FHIR JPA Model
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import java.io.InputStream;
024import java.math.BigDecimal;
025
026import ca.uhn.fhir.rest.param.QuantityParam;
027import org.fhir.ucum.Decimal;
028import org.fhir.ucum.Pair;
029import org.fhir.ucum.UcumEssenceService;
030import org.fhir.ucum.UcumException;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import ca.uhn.fhir.util.ClasspathUtil;
035
036import javax.annotation.Nullable;
037
038/**
039 * It's a wrapper of UcumEssenceService
040 *
041 */
042public class UcumServiceUtil {
043
044        private static final Logger ourLog = LoggerFactory.getLogger(UcumServiceUtil.class);
045
046        public static final String UCUM_CODESYSTEM_URL = "http://unitsofmeasure.org";
047        private static final String UCUM_SOURCE = "/ucum-essence.xml";
048
049        private static UcumEssenceService myUcumEssenceService = null;
050
051        private UcumServiceUtil() {
052        }
053
054        // lazy load UCUM_SOURCE only once
055        private static void init() {
056
057                if (myUcumEssenceService != null)
058                        return;
059
060                synchronized (UcumServiceUtil.class) {
061                        InputStream input = ClasspathUtil.loadResourceAsStream(UCUM_SOURCE);
062                        try {
063                                myUcumEssenceService = new UcumEssenceService(input);
064
065                        } catch (UcumException e) {
066                                ourLog.warn("Failed to load ucum code from ", UCUM_SOURCE, e);
067                        } finally {
068                                ClasspathUtil.close(input);
069                        }
070                }
071        }
072
073        /**
074         * Get the canonical form of a code, it's define at
075         * <link>http://unitsofmeasure.org</link>
076         * 
077         * e.g. 12cm -> 0.12m where m is the canonical form of the length.
078         * 
079         * @param theSystem must be http://unitsofmeasure.org
080         * @param theValue  the value in the original form e.g. 0.12
081         * @param theCode   the code in the original form e.g. 'cm'
082         * @return the CanonicalForm if no error, otherwise return null
083         */
084        public static Pair getCanonicalForm(String theSystem, BigDecimal theValue, String theCode) {
085
086                // -- only for http://unitsofmeasure.org
087                if (!UCUM_CODESYSTEM_URL.equals(theSystem) || theValue == null || theCode == null)
088                        return null;
089
090                init();
091                Pair theCanonicalPair;
092
093                try {
094                        Decimal theDecimal = new Decimal(theValue.toPlainString(), theValue.precision());
095                        theCanonicalPair = myUcumEssenceService.getCanonicalForm(new Pair(theDecimal, theCode));
096                        // For some reason code [degF], degree Fahrenheit, can't be converted. it returns value null.
097                        if (theCanonicalPair.getValue() == null)
098                                return null;
099                } catch (UcumException e) {
100                        return null;
101                }
102
103                return theCanonicalPair;
104        }
105
106    @Nullable
107    public static QuantityParam toCanonicalQuantityOrNull(QuantityParam theQuantityParam) {
108        Pair canonicalForm = getCanonicalForm(theQuantityParam.getSystem(), theQuantityParam.getValue(), theQuantityParam.getUnits());
109        if (canonicalForm != null) {
110            BigDecimal valueValue = new BigDecimal(canonicalForm.getValue().asDecimal());
111            String unitsValue = canonicalForm.getCode();
112            return new QuantityParam()
113                .setSystem(theQuantityParam.getSystem())
114                .setValue(valueValue)
115                .setUnits(unitsValue)
116                .setPrefix(theQuantityParam.getPrefix());
117        } else {
118            return null;
119        }
120    }
121}