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