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}