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