001/*
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2023 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.context;
021
022import ca.uhn.fhir.i18n.Msg;
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                @Override
181                public void remove(IBase theTarget, int theIndex) {
182                        List<IBase> existingList = (List<IBase>) getFieldValue(theTarget, myField);
183                        if (existingList == null) {
184                                throw new IndexOutOfBoundsException(Msg.code(2143) + "Can not remove element at index " + theIndex + " from list - List is null");
185                        }
186                        if (theIndex >= existingList.size()) {
187                                throw new IndexOutOfBoundsException(Msg.code(2144) + "Can not remove element at index " + theIndex + " from list - List size is " + existingList.size());
188                        }
189                        existingList.remove(theIndex);
190                }
191        }
192
193        private final class FieldPlainAccessor implements IAccessor {
194                @Override
195                public List<IBase> getValues(IBase theTarget) {
196                        Object values = getFieldValue(theTarget, myField);
197                        if (values == null) {
198                                return Collections.emptyList();
199                        }
200                        return Collections.singletonList((IBase) values);
201                }
202
203                @Override
204                public <T extends IBase> Optional<T> getFirstValueOrNull(IBase theTarget) {
205                        return Optional.ofNullable(((T)getFieldValue(theTarget, myField)));
206                }
207        }
208
209        protected final class FieldPlainMutator implements IMutator {
210                @Override
211                public void addValue(IBase theTarget, IBase theValue) {
212                        setFieldValue(theTarget, theValue, myField);
213                }
214
215                @Override
216                public void setValue(IBase theTarget, IBase theValue) {
217                        addValue(theTarget, theValue);
218                }
219
220                @Override
221                public void remove(IBase theTarget, int theIndex) {
222                        throw new UnsupportedOperationException(Msg.code(2142) + "Remove by index can only be called on a list-valued field.  '" + myField.getName() + "' is a single-valued field.");
223                }
224        }
225
226        private static void setFieldValue(IBase theTarget, Object theValue, Field theField) {
227                try {
228                        theField.set(theTarget, theValue);
229                } catch (IllegalAccessException e) {
230                        throw new ConfigurationException(Msg.code(1736) + "Failed to set value", e);
231                }
232        }
233
234        private static Object getFieldValue(IBase theTarget, Field theField) {
235                try {
236                        return theField.get(theTarget);
237                } catch (IllegalAccessException e) {
238                        throw new ConfigurationException(Msg.code(1737) + "Failed to get value", e);
239                }
240        }
241
242}