001package org.hl7.fhir.convertors.context; 002 003import java.util.ArrayList; 004import java.util.Stack; 005 006import org.hl7.fhir.exceptions.FHIRException; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/* 011 Copyright (c) 2011+, HL7, Inc. 012 All rights reserved. 013 014 Redistribution and use in source and binary forms, with or without modification, 015 are permitted provided that the following conditions are met: 016 017 * Redistributions of source code must retain the above copyright notice, this 018 list of conditions and the following disclaimer. 019 * Redistributions in binary form must reproduce the above copyright notice, 020 this list of conditions and the following disclaimer in the documentation 021 and/or other materials provided with the distribution. 022 * Neither the name of HL7 nor the names of its contributors may be used to 023 endorse or promote products derived from this software without specific 024 prior written permission. 025 026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 029 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 030 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 031 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 032 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 033 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 034 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 035 POSSIBILITY OF SUCH DAMAGE. 036 */ 037 038/** 039 * @param <T> 040 */ 041public class VersionConvertorContext<T> { 042 043 private final Logger logger = LoggerFactory.getLogger(VersionConvertorContext.class); 044 045 /** 046 * Each conversion thread instantiates it's own instance of a convertor, which is stored in a {@link ThreadLocal} for 047 * access. 048 */ 049 private final ThreadLocal<T> threadLocalVersionConverter = new ThreadLocal<>(); 050 051 /** 052 * We store the current state of the path as a {@link Stack<String>}. Each Fhir type is pushed onto the stack 053 * as we progress, and popped off once converted. The conversions are traversed in a depth first manner, which 054 * makes this possible. 055 */ 056 private final ThreadLocal<Stack<String>> threadLocalPath = new ThreadLocal<>(); 057 058 /** 059 * Initializes the conversion context. If a context already exists, this will just add the path to the current tracked 060 * path for the conversion context. 061 * 062 * @param versionConvertor Instance of the version convertor context to use. 063 * @param path Current path (i.e. String label) for the given conversion. 064 */ 065 public void init(T versionConvertor, String path) { 066 if (versionConvertor == null) { 067 throw new FHIRException("Null convertor is not allowed!"); 068 } 069 if (path == null) { 070 throw new FHIRException("Null path type is not allowed!"); 071 } 072 073 if (threadLocalVersionConverter.get() == null) { 074 threadLocalVersionConverter.set(versionConvertor); 075 } 076 077 Stack<String> stack = threadLocalPath.get(); 078 if (stack == null) { 079 stack = new Stack<>(); 080 } 081 stack.push(path); 082 // logger.debug("Pushing path <" + path + "> onto stack. Current path -> " + String.join(",", stack)); 083 threadLocalPath.set(stack); 084 } 085 086 /** 087 * Closes the current path. This removes the label from the current stored path. 088 * If there is no remaining path set after this path is removed, the context convertor and path are cleared from 089 * memory. 090 * 091 * @param path {@link String} label path to add. 092 */ 093 public void close(String path) { 094 Stack<String> stack = threadLocalPath.get(); 095 if (stack == null) { 096 throw new FHIRException("Cannot close path <" + path + ">. Reached unstable state, no stack path available."); 097 } 098 String currentPath = stack.pop(); 099// logger.debug("Popping path <" + currentPath + "> off stack. Current path -> " + String.join(",", stack)); 100 if (!path.equals(currentPath)) { 101 throw new FHIRException("Reached unstable state, current path doesn't match expected path."); 102 } 103 if (stack.isEmpty()) { 104 threadLocalVersionConverter.remove(); 105 threadLocalPath.remove(); 106 } else { 107 threadLocalPath.set(stack); 108 } 109 } 110 111 /** 112 * Will return the {@link String} corresponding to the current conversion "path". 113 * ex: "Bundle.Appointment" 114 * 115 * @return {@link ArrayList<String>} 116 */ 117 public String getPath() throws FHIRException { 118 if (threadLocalPath.get() == null) { 119 throw new FHIRException("No current path is set."); 120 } 121 return String.join(".", new ArrayList<>(threadLocalPath.get())); 122 } 123 124 /** 125 * Returns the current instance of the version convertor. 126 */ 127 public T getVersionConvertor() { 128 T result = threadLocalVersionConverter.get(); 129 if (result != null && logger != null) { 130// logger.debug(result.toString()); 131 } 132 return result; 133 } 134}