001package ca.uhn.fhir.jpa.model.search; 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 org.hibernate.search.engine.backend.document.DocumentElement; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027import javax.annotation.Nonnull; 028import java.util.Arrays; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032 033/** 034 * Provide a lookup of created Hibernate Search DocumentElement entries. 035 * 036 * The Hibernate Search DocumentElement api only supports create - it does not support fetching an existing element. 037 * This class demand-creates object elements for a given path. 038 */ 039public class HibernateSearchElementCache { 040 private static final Logger ourLog = LoggerFactory.getLogger(HibernateSearchElementCache.class); 041 private final DocumentElement myRoot; 042 private final Map<String, DocumentElement> myCache = new HashMap<>(); 043 044 /** 045 * Create the helper rooted on the given DocumentElement 046 * @param theRoot the document root 047 */ 048 public HibernateSearchElementCache(DocumentElement theRoot) { 049 this.myRoot = theRoot; 050 } 051 052 /** 053 * Fetch or create an Object DocumentElement with thePath from the root element. 054 * 055 * @param thePath the property names of the object path. E.g. "sp","code","token" 056 * @return the existing or created element 057 */ 058 public DocumentElement getObjectElement(@Nonnull String... thePath) { 059 return getObjectElement(Arrays.asList(thePath)); 060 } 061 062 /** 063 * Fetch or create an Object DocumentElement with thePath from the root element. 064 * 065 * @param thePath the property names of the object path. E.g. "sp","code","token" 066 * @return the existing or created element 067 */ 068 public DocumentElement getObjectElement(@Nonnull List<String> thePath) { 069 if (thePath.size() == 0) { 070 return myRoot; 071 } 072 String key = String.join(".", thePath); 073 // re-implement computeIfAbsent since we're recursive, and it isn't rentrant. 074 DocumentElement result = myCache.get(key); 075 if (result == null) { 076 DocumentElement parent = getObjectElement(thePath.subList(0, thePath.size() - 1)); 077 String lastSegment = thePath.get(thePath.size() - 1); 078 assert (lastSegment.indexOf('.') == -1); 079 result = parent.addObject(lastSegment); 080 myCache.put(key, result); 081 } 082 ourLog.trace("getNode {}: {}", key, result); 083 return result; 084 } 085}