001package ca.uhn.fhir.rest.server; 002 003/*- 004 * #%L 005 * HAPI FHIR - Server Framework 006 * %% 007 * Copyright (C) 2014 - 2019 University Health Network 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 ca.uhn.fhir.model.api.annotation.ResourceDef; 024import java.util.Collections; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Optional; 028import org.hl7.fhir.instance.model.api.IBaseResource; 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; i < Math.min(resourceClassesInHierarchy.size(), greatestSharedAncestorsDescending.size()); i++) { 064 if (greatestSharedAncestorsDescending.get(i) != resourceClassesInHierarchy.get(i)) { 065 greatestSharedAncestorsDescending = greatestSharedAncestorsDescending.subList(0, i); 066 break; 067 } 068 } 069 } else { 070 greatestSharedAncestorsDescending = resourceClassesInHierarchy; 071 initialized = true; 072 } 073 } 074 075 /** 076 * @return The lowest common superclass of currently registered resources. 077 */ 078 public Optional<Class<? extends IBaseResource>> getLowestCommonSuperclass() { 079 if (!initialized || greatestSharedAncestorsDescending.isEmpty()) { 080 return Optional.empty(); 081 } 082 return Optional.ofNullable(greatestSharedAncestorsDescending.get(greatestSharedAncestorsDescending.size() - 1)); 083 } 084 085}