001package ca.uhn.fhir.context;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
006 * %%
007 * Copyright (C) 2014 - 2016 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;
033
034public abstract class BaseRuntimeDeclaredChildDefinition extends BaseRuntimeChildDefinition {
035        private final IAccessor myAccessor;
036        private final String myElementName;
037        private final Field myField;
038        private final String myFormalDefinition;
039        private final int myMax;
040        private final int myMin;
041        private boolean myModifier;
042        private final IMutator myMutator;
043        private final String myShortDefinition;
044        private boolean mySummary;
045
046        BaseRuntimeDeclaredChildDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName) throws ConfigurationException {
047                super();
048                Validate.notNull(theField, "No field speficied");
049                Validate.inclusiveBetween(0, Integer.MAX_VALUE, theChildAnnotation.min(), "Min must be >= 0");
050                Validate.isTrue(theChildAnnotation.max() == -1 || theChildAnnotation.max() >= theChildAnnotation.min(), "Max must be >= Min (unless it is -1 / unlimited)");
051                Validate.notBlank(theElementName, "Element name must not be blank");
052
053                myField = theField;
054                myMin = theChildAnnotation.min();
055                myMax = theChildAnnotation.max();
056                mySummary = theChildAnnotation.summary();
057                myModifier = theChildAnnotation.modifier();
058                myElementName = theElementName;
059                if (theDescriptionAnnotation != null) {
060                        myShortDefinition = theDescriptionAnnotation.shortDefinition();
061                        myFormalDefinition = theDescriptionAnnotation.formalDefinition();
062                } else {
063                        myShortDefinition = null;
064                        myFormalDefinition = null;
065                }
066
067                myField.setAccessible(true);
068                if (List.class.equals(myField.getType())) {
069                        // TODO: verify that generic type is IElement
070                        myAccessor = new FieldListAccessor();
071                        myMutator = new FieldListMutator();
072                } else {
073                        myAccessor = new FieldPlainAccessor();
074                        myMutator = new FieldPlainMutator();
075                }
076
077        }
078
079        @Override
080        public IAccessor getAccessor() {
081                return myAccessor;
082        }
083
084        @Override
085        public String getElementName() {
086                return myElementName;
087        }
088
089        public Field getField() {
090                return myField;
091        }
092
093        public String getFormalDefinition() {
094                return myFormalDefinition;
095        }
096
097        @Override
098        public int getMax() {
099                return myMax;
100        }
101
102        @Override
103        public int getMin() {
104                return myMin;
105        }
106
107        @Override
108        public IMutator getMutator() {
109                return myMutator;
110        }
111
112        public String getShortDefinition() {
113                return myShortDefinition;
114        }
115
116        public BaseRuntimeElementDefinition<?> getSingleChildOrThrow() {
117                if (getValidChildNames().size() != 1) {
118                        throw new IllegalStateException("This child has " + getValidChildNames().size() + " children, expected 1. This is a HAPI bug. Found: " + getValidChildNames());
119                }
120                return getChildByName(getValidChildNames().iterator().next());
121        }
122
123        public boolean isModifier() {
124                return myModifier;
125        }
126
127        public boolean isSummary() {
128                return mySummary;
129        }
130
131        private final class FieldListAccessor implements IAccessor {
132                @SuppressWarnings("unchecked")
133                @Override
134                public List<IBase> getValues(Object theTarget) {
135                        List<IBase> retVal;
136                        try {
137                                retVal = (List<IBase>) myField.get(theTarget);
138                        } catch (Exception e) {
139                                throw new ConfigurationException("Failed to get value", e);
140                        }
141                        
142                        if (retVal == null) {
143                                retVal = Collections.emptyList();
144                        }
145                        return retVal;
146                }
147        }
148
149        protected final class FieldListMutator implements IMutator {
150                @Override
151                public void addValue(Object theTarget, IBase theValue) {
152                        addValue(theTarget, theValue, false);
153                }
154
155                private void addValue(Object theTarget, IBase theValue, boolean theClear) {
156                        try {
157                                @SuppressWarnings("unchecked")
158                                List<IBase> existingList = (List<IBase>) myField.get(theTarget);
159                                if (existingList == null) {
160                                        existingList = new ArrayList<IBase>(2);
161                                        myField.set(theTarget, existingList);
162                                }
163                                if (theClear) {
164                                        existingList.clear();
165                                }
166                                existingList.add(theValue);
167                        } catch (Exception e) {
168                                throw new ConfigurationException("Failed to set value", e);
169                        }
170                }
171
172                @Override
173                public void setValue(Object theTarget, IBase theValue) {
174                        addValue(theTarget, theValue, true);
175                }
176        }
177
178        private final class FieldPlainAccessor implements IAccessor {
179                @Override
180                public List<IBase> getValues(Object theTarget) {
181                        try {
182                                Object values = myField.get(theTarget);
183                                if (values == null) {
184                                        return Collections.emptyList();
185                                }
186                                List<IBase> retVal = Collections.singletonList((IBase) values);
187                                return retVal;
188                        } catch (Exception e) {
189                                throw new ConfigurationException("Failed to get value", e);
190                        }
191                }
192        }
193
194        protected final class FieldPlainMutator implements IMutator {
195                @Override
196                public void addValue(Object theTarget, IBase theValue) {
197                        try {
198                                myField.set(theTarget, theValue);
199                        } catch (Exception e) {
200                                throw new ConfigurationException("Failed to set value", e);
201                        }
202                }
203
204                @Override
205                public void setValue(Object theTarget, IBase theValue) {
206                        addValue(theTarget, theValue);
207                }
208        }
209
210}