001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2017 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;
033import ca.uhn.fhir.util.ValidateUtil;
034
035public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
036        private final IAccessor myAccessor;
037        private String myBindingValueSet;
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 boolean myModifier;
044
045        private final IMutator myMutator;
046        private final String myShortDefinition;
047        private boolean mySummary;
048        BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
049                super();
050                Validate.notNull(theField, "No field speficied");
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        @Override
091        public String getElementName() {
092                return myElementName;
093        }
094
095        public Field getField() {
096                return myField;
097        }
098
099        public String getFormalDefinition() {
100                return myFormalDefinition;
101        }
102
103        @Override
104        public int getMax() {
105                return myMax;
106        }
107
108        @Override
109        public int getMin() {
110                return myMin;
111        }
112
113        @Override
114        public IMutator getMutator() {
115                return myMutator;
116        }
117
118        public String getShortDefinition() {
119                return myShortDefinition;
120        }
121
122        public BaseRuntimeElementDefinition<?> getSingleChildOrThrow() {
123                if (getValidChildNames().size() != 1) {
124                        throw new IllegalStateException("This child has " + getValidChildNames().size() + " children, expected 1. This is a HAPI bug. Found: " + getValidChildNames());
125                }
126                return getChildByName(getValidChildNames().iterator().next());
127        }
128
129        public boolean isModifier() {
130                return myModifier;
131        }
132
133        @Override
134        public boolean isSummary() {
135                return mySummary;
136        }
137
138        void setBindingValueSet(String theBindingValueSet) {
139                myBindingValueSet = theBindingValueSet;
140        }
141
142        protected void setModifier(boolean theModifier) {
143                myModifier = theModifier;
144        }
145
146        private final class FieldListAccessor implements IAccessor {
147                @SuppressWarnings("unchecked")
148                @Override
149                public List<IBase> getValues(Object theTarget) {
150                        List<IBase> retVal;
151                        try {
152                                retVal = (List<IBase>) myField.get(theTarget);
153                        } catch (Exception e) {
154                                throw new ConfigurationException("Failed to get value", e);
155                        }
156                        
157                        if (retVal == null) {
158                                retVal = Collections.emptyList();
159                        }
160                        return retVal;
161                }
162        }
163
164        protected final class FieldListMutator implements IMutator {
165                @Override
166                public void addValue(Object theTarget, IBase theValue) {
167                        addValue(theTarget, theValue, false);
168                }
169
170                private void addValue(Object theTarget, IBase theValue, boolean theClear) {
171                        try {
172                                @SuppressWarnings("unchecked")
173                                List<IBase> existingList = (List<IBase>) myField.get(theTarget);
174                                if (existingList == null) {
175                                        existingList = new ArrayList<IBase>(2);
176                                        myField.set(theTarget, existingList);
177                                }
178                                if (theClear) {
179                                        existingList.clear();
180                                }
181                                existingList.add(theValue);
182                        } catch (Exception e) {
183                                throw new ConfigurationException("Failed to set value", e);
184                        }
185                }
186
187                @Override
188                public void setValue(Object theTarget, IBase theValue) {
189                        addValue(theTarget, theValue, true);
190                }
191        }
192
193        private final class FieldPlainAccessor implements IAccessor {
194                @Override
195                public List<IBase> getValues(Object theTarget) {
196                        try {
197                                Object values = myField.get(theTarget);
198                                if (values == null) {
199                                        return Collections.emptyList();
200                                }
201                                List<IBase> retVal = Collections.singletonList((IBase) values);
202                                return retVal;
203                        } catch (Exception e) {
204                                throw new ConfigurationException("Failed to get value", e);
205                        }
206                }
207        }
208
209        protected final class FieldPlainMutator implements IMutator {
210                @Override
211                public void addValue(Object theTarget, IBase theValue) {
212                        try {
213                                myField.set(theTarget, theValue);
214                        } catch (Exception e) {
215                                throw new ConfigurationException("Failed to set value", e);
216                        }
217                }
218
219                @Override
220                public void setValue(Object theTarget, IBase theValue) {
221                        addValue(theTarget, theValue);
222                }
223        }
224
225}