View Javadoc
1   package ca.uhn.fhir.parser;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import static org.apache.commons.lang3.StringUtils.isBlank;
24  
25  import java.io.IOException;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.io.StringWriter;
29  import java.io.Writer;
30  import java.util.ArrayList;
31  import java.util.HashSet;
32  import java.util.IdentityHashMap;
33  import java.util.List;
34  import java.util.Set;
35  
36  import org.apache.commons.lang3.StringUtils;
37  
38  import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
39  import ca.uhn.fhir.context.BaseRuntimeDeclaredChildDefinition;
40  import ca.uhn.fhir.context.ConfigurationException;
41  import ca.uhn.fhir.context.FhirContext;
42  import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
43  import ca.uhn.fhir.model.api.Bundle;
44  import ca.uhn.fhir.model.api.IElement;
45  import ca.uhn.fhir.model.api.IResource;
46  import ca.uhn.fhir.model.api.TagList;
47  import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
48  import ca.uhn.fhir.model.primitive.IdDt;
49  
50  public abstract class BaseParser implements IParser {
51  
52  	private ContainedResources myContainedResources;
53  	private FhirContext myContext;
54  	private boolean mySuppressNarratives;
55  
56  	public BaseParser(FhirContext theContext) {
57  		myContext = theContext;
58  	}
59  
60  	protected String fixContainedResourceId(String theValue) {
61  		if (StringUtils.isNotBlank(theValue)&&theValue.charAt(0)=='#') {
62  			return theValue.substring(1);
63  		}
64  		return theValue;
65  	}
66  
67  	
68  	private void containResourcesForEncoding(ContainedResources theContained, IResource theResource, IResource theTarget) {
69  		List<ResourceReferenceDt> allElements = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, ResourceReferenceDt.class);
70  
71  		Set<String> allIds = new HashSet<String>();
72  
73  		for (IResource next : theTarget.getContained().getContainedResources()) {
74  			String nextId = next.getId().getValue();
75  			if (StringUtils.isNotBlank(nextId)) {
76  				allIds.add(nextId);
77  			}
78  		}
79  
80  		for (ResourceReferenceDt next : allElements) {
81  			IResource resource = next.getResource();
82  			if (resource != null) {
83  				if (resource.getId().isEmpty() || resource.getId().isLocal()) {
84  					theContained.addContained(resource);
85  				} else {
86  					continue;
87  				}
88  
89  				containResourcesForEncoding(theContained, resource, theTarget);
90  			}
91  		}
92  
93  	}
94  
95  	public void containResourcesForEncoding(IResource theResource) {
96  		ContainedResources contained = new ContainedResources();
97  		containResourcesForEncoding(contained, theResource, theResource);
98  		myContainedResources = contained;
99  	}
100 
101 	@Override
102 	public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
103 		if (theBundle == null) {
104 			throw new NullPointerException("Bundle can not be null");
105 		}
106 		StringWriter stringWriter = new StringWriter();
107 		try {
108 			encodeBundleToWriter(theBundle, stringWriter);
109 		} catch (IOException e) {
110 			throw new Error("Encountered IOException during write to string - This should not happen!");
111 		}
112 
113 		return stringWriter.toString();
114 	}
115 
116 	@Override
117 	public String encodeResourceToString(IResource theResource) throws DataFormatException {
118 		Writer stringWriter = new StringWriter();
119 		try {
120 			encodeResourceToWriter(theResource, stringWriter);
121 		} catch (IOException e) {
122 			throw new Error("Encountered IOException during write to string - This should not happen!");
123 		}
124 		return stringWriter.toString();
125 	}
126 
127 	@Override
128 	public String encodeTagListToString(TagList theTagList) {
129 		Writer stringWriter = new StringWriter();
130 		try {
131 			encodeTagListToWriter(theTagList, stringWriter);
132 		} catch (IOException e) {
133 			throw new Error("Encountered IOException during write to string - This should not happen!");
134 		}
135 		return stringWriter.toString();
136 	}
137 
138 	ContainedResources getContainedResources() {
139 		return myContainedResources;
140 	}
141 
142 	/**
143 	 * If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the encoded values.
144 	 */
145 	public boolean getSuppressNarratives() {
146 		return mySuppressNarratives;
147 	}
148 
149 	@Override
150 	public Bundle parseBundle(Reader theReader) {
151 		return parseBundle(null, theReader);
152 	}
153 
154 	@Override
155 	public Bundle parseBundle(String theXml) throws ConfigurationException, DataFormatException {
156 		StringReader reader = new StringReader(theXml);
157 		return parseBundle(reader);
158 	}
159 
160 	@SuppressWarnings("cast")
161 	@Override
162 	public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
163 		StringReader reader = new StringReader(theMessageString);
164 		return (T) parseResource(theResourceType, reader);
165 	}
166 
167 	@Override
168 	public IResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException {
169 		return parseResource(null, theReader);
170 	}
171 
172 	@Override
173 	public IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException {
174 		return parseResource(null, theMessageString);
175 	}
176 
177 	@Override
178 	public TagList parseTagList(String theString) {
179 		return parseTagList(new StringReader(theString));
180 	}
181 
182 	@Override
183 	public IParser setSuppressNarratives(boolean theSuppressNarratives) {
184 		mySuppressNarratives = theSuppressNarratives;
185 		return this;
186 	}
187 
188 	protected void throwExceptionForUnknownChildType(BaseRuntimeChildDefinition nextChild, Class<? extends IElement> type) {
189 		if (nextChild instanceof BaseRuntimeDeclaredChildDefinition) {
190 			StringBuilder b = new StringBuilder();
191 			b.append(((BaseRuntimeDeclaredChildDefinition) nextChild).getElementName());
192 			b.append(" has type ");
193 			b.append(type);
194 			b.append(" but this is not a valid type for this element");
195 			if (nextChild instanceof RuntimeChildChoiceDefinition) {
196 				RuntimeChildChoiceDefinition choice = (RuntimeChildChoiceDefinition) nextChild;
197 				b.append(" - Expected one of: " + choice.getValidChildTypes());
198 			}
199 			throw new DataFormatException(b.toString());
200 		}
201 		throw new DataFormatException(nextChild + " has no child of type " + type);
202 	}
203 
204 	protected String determineReferenceText(ResourceReferenceDt theRef) {
205 		String reference = theRef.getReference().getValue();
206 		if (isBlank(reference)) {
207 			if (theRef.getResource() != null) {
208 				IdDt containedId = getContainedResources().getResourceId(theRef.getResource());
209 				if (containedId != null && !containedId.isEmpty()) {
210 					if (containedId.isLocal()) {
211 						reference = containedId.getValue();
212 					} else {
213 						reference = "#" + containedId.getValue();
214 					}
215 				} else if (theRef.getResource().getId() != null && theRef.getResource().getId().hasIdPart()) {
216 					reference = theRef.getResource().getId().getValue();
217 				}
218 			}
219 		}
220 		return reference;
221 	}
222 
223 	
224 	static class ContainedResources {
225 		private long myNextContainedId = 1;
226 
227 		private IdentityHashMap<IResource, IdDt> myResourceToId = new IdentityHashMap<IResource, IdDt>();
228 		private List<IResource> myResources = new ArrayList<IResource>();
229 
230 		public void addContained(IResource theResource) {
231 			if (myResourceToId.containsKey(theResource)) {
232 				return;
233 			}
234 			
235 			IdDt newId;
236 			if (theResource.getId().isLocal()) {
237 				newId = theResource.getId();
238 			} else {
239 				// TODO: make this configurable between the two below (and something else?)
240 				// newId = new IdDt(UUID.randomUUID().toString());
241 				newId = new IdDt(myNextContainedId++);
242 			}
243 			
244 			myResourceToId.put(theResource, newId);
245 			myResources.add(theResource);
246 		}
247 
248 		public List<IResource> getContainedResources() {
249 			return myResources;
250 		}
251 		
252 		public IdDt getResourceId(IResource theResource) {
253 			return myResourceToId.get(theResource);
254 		}
255 
256 		public boolean isEmpty() {
257 			return myResourceToId.isEmpty();
258 		}
259 
260 	}
261 
262 }