001package ca.uhn.fhir.context; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2021 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.Child; 024import ca.uhn.fhir.model.api.annotation.Description; 025import ca.uhn.fhir.util.ParametersUtil; 026import ca.uhn.fhir.util.ValidateUtil; 027import org.apache.commons.lang3.Validate; 028import org.hl7.fhir.instance.model.api.IBase; 029 030import java.lang.reflect.Field; 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.List; 034import java.util.Optional; 035 036public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition { 037 private final IAccessor myAccessor; 038 private final String myElementName; 039 private final Field myField; 040 private final String myFormalDefinition; 041 private final int myMax; 042 private final int myMin; 043 private final IMutator myMutator; 044 private final String myShortDefinition; 045 private String myBindingValueSet; 046 private boolean myModifier; 047 private boolean mySummary; 048 049 BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException { 050 super(); 051 Validate.notNull(theField, "No field specified"); 052 ValidateUtil.isGreaterThanOrEqualTo(theChildAnnotation.min(), 0, "Min must be >= 0"); 053 Validate.isTrue(theChildAnnotation.max() == -1 || theChildAnnotation.max() >= theChildAnnotation.min(), "Max must be >= Min (unless it is -1 / unlimited)"); 054 Validate.notBlank(theElementName, "Element name must not be blank"); 055 056 myField = theField; 057 myMin = theChildAnnotation.min(); 058 myMax = theChildAnnotation.max(); 059 mySummary = theChildAnnotation.summary(); 060 myModifier = theChildAnnotation.modifier(); 061 myElementName = theElementName; 062 if (theDescriptionAnnotation != null) { 063 myShortDefinition = theDescriptionAnnotation.shortDefinition(); 064 myFormalDefinition = ParametersUtil.extractDescription(theDescriptionAnnotation); 065 } else { 066 myShortDefinition = null; 067 myFormalDefinition = null; 068 } 069 070 myField.setAccessible(true); 071 if (List.class.equals(myField.getType())) { 072 // TODO: verify that generic type is IElement 073 myAccessor = new FieldListAccessor(); 074 myMutator = new FieldListMutator(); 075 } else { 076 myAccessor = new FieldPlainAccessor(); 077 myMutator = new FieldPlainMutator(); 078 } 079 080 } 081 082 @Override 083 public IAccessor getAccessor() { 084 return myAccessor; 085 } 086 087 public String getBindingValueSet() { 088 return myBindingValueSet; 089 } 090 091 void setBindingValueSet(String theBindingValueSet) { 092 myBindingValueSet = theBindingValueSet; 093 } 094 095 @Override 096 public String getElementName() { 097 return myElementName; 098 } 099 100 public Field getField() { 101 return myField; 102 } 103 104 public String getFormalDefinition() { 105 return myFormalDefinition; 106 } 107 108 @Override 109 public int getMax() { 110 return myMax; 111 } 112 113 @Override 114 public int getMin() { 115 return myMin; 116 } 117 118 @Override 119 public IMutator getMutator() { 120 return myMutator; 121 } 122 123 public String getShortDefinition() { 124 return myShortDefinition; 125 } 126 127 public boolean isModifier() { 128 return myModifier; 129 } 130 131 protected void setModifier(boolean theModifier) { 132 myModifier = theModifier; 133 } 134 135 @Override 136 public boolean isSummary() { 137 return mySummary; 138 } 139 140 private final class FieldListAccessor implements IAccessor { 141 @SuppressWarnings("unchecked") 142 @Override 143 public List<IBase> getValues(IBase theTarget) { 144 List<IBase> retVal = (List<IBase>) getFieldValue(theTarget, myField); 145 if (retVal == null) { 146 retVal = Collections.emptyList(); 147 } 148 return retVal; 149 } 150 151 } 152 153 protected final class FieldListMutator implements IMutator { 154 @Override 155 public void addValue(IBase theTarget, IBase theValue) { 156 addValue(theTarget, theValue, false); 157 } 158 159 private void addValue(IBase theTarget, IBase theValue, boolean theClear) { 160 @SuppressWarnings("unchecked") 161 List<IBase> existingList = (List<IBase>) getFieldValue(theTarget, myField); 162 if (existingList == null) { 163 existingList = new ArrayList<>(2); 164 setFieldValue(theTarget, existingList, myField); 165 } 166 if (theClear) { 167 existingList.clear(); 168 if (theValue == null) { 169 return; 170 } 171 } 172 existingList.add(theValue); 173 } 174 175 @Override 176 public void setValue(IBase theTarget, IBase theValue) { 177 addValue(theTarget, theValue, true); 178 } 179 } 180 181 private final class FieldPlainAccessor implements IAccessor { 182 @Override 183 public List<IBase> getValues(IBase theTarget) { 184 Object values = getFieldValue(theTarget, myField); 185 if (values == null) { 186 return Collections.emptyList(); 187 } 188 return Collections.singletonList((IBase) values); 189 } 190 191 @Override 192 public <T extends IBase> Optional<T> getFirstValueOrNull(IBase theTarget) { 193 return Optional.ofNullable(((T)getFieldValue(theTarget, myField))); 194 } 195 } 196 197 protected final class FieldPlainMutator implements IMutator { 198 @Override 199 public void addValue(IBase theTarget, IBase theValue) { 200 setFieldValue(theTarget, theValue, myField); 201 } 202 203 @Override 204 public void setValue(IBase theTarget, IBase theValue) { 205 addValue(theTarget, theValue); 206 } 207 } 208 209 private static void setFieldValue(IBase theTarget, Object theValue, Field theField) { 210 try { 211 theField.set(theTarget, theValue); 212 } catch (IllegalAccessException e) { 213 throw new ConfigurationException("Failed to set value", e); 214 } 215 } 216 217 private static Object getFieldValue(IBase theTarget, Field theField) { 218 try { 219 return theField.get(theTarget); 220 } catch (IllegalAccessException e) { 221 throw new ConfigurationException("Failed to get value", e); 222 } 223 } 224 225}