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}