001/*- 002 * #%L 003 * HAPI FHIR - Server Framework 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.rest.server; 021 022import ca.uhn.fhir.model.api.annotation.ResourceDef; 023import org.hl7.fhir.instance.model.api.IBaseResource; 024 025import java.util.Collections; 026import java.util.LinkedList; 027import java.util.List; 028import java.util.Optional; 029 030/** 031 * <pre> 032 * When populating the StructureDefinition links in a capability statement, 033 * it can be useful to know the lowest common superclass for the profiles in use for a given resource name. 034 * This class finds this superclass, by incrementally computing the greatest common sequence of ancestor classes in the class hierarchies of registered resources. 035 * For instance, given the following classes 036 * MyPatient extends Patient 037 * MyPatient2 extends MyPatient 038 * MyPatient3 extends MyPatient 039 * MyPatient4 extends MyPatient3 040 * this class will find the common ancestor sequence "IBaseResource -> Patient -> MyPatient". MyPatient is the lowest common superclass in this hierarchy. 041 * </pre> 042 * 043 */ 044public class CommonResourceSupertypeScanner { 045 046 private List<Class<? extends IBaseResource>> greatestSharedAncestorsDescending; 047 private boolean initialized; 048 049 /** 050 * Recomputes the lowest common superclass by adding a new resource definition to the hierarchy. 051 * @param resourceClass The resource class to add. 052 */ 053 public void register(Class<? extends IBaseResource> resourceClass) { 054 List<Class<? extends IBaseResource>> resourceClassesInHierarchy = new LinkedList<>(); 055 Class<?> currentClass = resourceClass; 056 while (IBaseResource.class.isAssignableFrom(currentClass) 057 && currentClass.getAnnotation(ResourceDef.class) != null) { 058 resourceClassesInHierarchy.add((Class<? extends IBaseResource>) currentClass); 059 currentClass = currentClass.getSuperclass(); 060 } 061 Collections.reverse(resourceClassesInHierarchy); 062 if (initialized) { 063 for (int i = 0; 064 i < Math.min(resourceClassesInHierarchy.size(), greatestSharedAncestorsDescending.size()); 065 i++) { 066 if (greatestSharedAncestorsDescending.get(i) != resourceClassesInHierarchy.get(i)) { 067 greatestSharedAncestorsDescending = greatestSharedAncestorsDescending.subList(0, i); 068 break; 069 } 070 } 071 } else { 072 greatestSharedAncestorsDescending = resourceClassesInHierarchy; 073 initialized = true; 074 } 075 } 076 077 /** 078 * @return The lowest common superclass of currently registered resources. 079 */ 080 public Optional<Class<? extends IBaseResource>> getLowestCommonSuperclass() { 081 if (!initialized || greatestSharedAncestorsDescending.isEmpty()) { 082 return Optional.empty(); 083 } 084 return Optional.ofNullable(greatestSharedAncestorsDescending.get(greatestSharedAncestorsDescending.size() - 1)); 085 } 086}