001package ca.uhn.fhir.rest.server;
002
003/*-
004 * #%L
005 * HAPI FHIR - Server Framework
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 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}