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