001package ca.uhn.fhir.util; 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.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collections; 026import java.util.IdentityHashMap; 027import java.util.Iterator; 028import java.util.List; 029import java.util.TreeSet; 030 031import org.apache.commons.lang3.Validate; 032import org.hl7.fhir.instance.model.api.IBase; 033import org.hl7.fhir.instance.model.api.IBaseExtension; 034import org.hl7.fhir.instance.model.api.IBaseHasExtensions; 035import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions; 036import org.hl7.fhir.instance.model.api.IBaseReference; 037import org.hl7.fhir.instance.model.api.IBaseResource; 038import org.hl7.fhir.instance.model.api.IPrimitiveType; 039 040import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 041import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 042import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 043import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum; 044import ca.uhn.fhir.context.ConfigurationException; 045import ca.uhn.fhir.context.FhirContext; 046import ca.uhn.fhir.context.RuntimeChildChoiceDefinition; 047import ca.uhn.fhir.context.RuntimeChildDirectResource; 048import ca.uhn.fhir.context.RuntimeResourceDefinition; 049import ca.uhn.fhir.model.api.ExtensionDt; 050import ca.uhn.fhir.model.api.IResource; 051import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; 052import ca.uhn.fhir.model.base.composite.BaseContainedDt; 053import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; 054import ca.uhn.fhir.model.primitive.StringDt; 055import ca.uhn.fhir.parser.DataFormatException; 056 057public class FhirTerser { 058 059 private FhirContext myContext; 060 061 public FhirTerser(FhirContext theContext) { 062 super(); 063 myContext = theContext; 064 } 065 066 private void addUndeclaredExtensions(IBase theElement, BaseRuntimeElementDefinition<?> theDefinition, BaseRuntimeChildDefinition theChildDefinition, IModelVisitor theCallback) { 067 if (theElement instanceof ISupportsUndeclaredExtensions) { 068 ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement; 069 for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) { 070 theCallback.acceptUndeclaredExtension(containingElement, null, theChildDefinition, theDefinition, nextExt); 071 addUndeclaredExtensions(nextExt, theDefinition, theChildDefinition, theCallback); 072 } 073 } 074 075 if (theElement instanceof IBaseHasExtensions) { 076 for (IBaseExtension<?, ?> nextExt : ((IBaseHasExtensions)theElement).getExtension()) { 077 theCallback.acceptElement(nextExt.getValue(), null, theChildDefinition, theDefinition); 078 addUndeclaredExtensions(nextExt, theDefinition, theChildDefinition, theCallback); 079 } 080 } 081 082 if (theElement instanceof IBaseHasModifierExtensions) { 083 for (IBaseExtension<?, ?> nextExt : ((IBaseHasModifierExtensions)theElement).getModifierExtension()) { 084 theCallback.acceptElement(nextExt.getValue(), null, theChildDefinition, theDefinition); 085 addUndeclaredExtensions(nextExt, theDefinition, theChildDefinition, theCallback); 086 } 087 } 088 089 } 090 091 /** 092 * Returns a list containing all child elements (including the resource itself) which are <b>non-empty</b> and are either of the exact type specified, or are a subclass of that type. 093 * <p> 094 * For example, specifying a type of {@link StringDt} would return all non-empty string instances within the message. Specifying a type of {@link IResource} would return the resource itself, as 095 * well as any contained resources. 096 * </p> 097 * <p> 098 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g. 099 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource) 100 * </p> 101 * 102 * @param theResource 103 * The resource instance to search. Must not be null. 104 * @param theType 105 * The type to search for. Must not be null. 106 * @return Returns a list of all matching elements 107 */ 108 public <T extends IBase> List<T> getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class<T> theType) { 109 final ArrayList<T> retVal = new ArrayList<T>(); 110 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 111 visit(new IdentityHashMap<Object, Object>(), theResource, null, null, def, new IModelVisitor() { 112 @SuppressWarnings("unchecked") 113 @Override 114 public void acceptElement(IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) { 115 if (theElement == null || theElement.isEmpty()) { 116 return; 117 } 118 119 if (theType.isAssignableFrom(theElement.getClass())) { 120 retVal.add((T) theElement); 121 } 122 } 123 124 @SuppressWarnings("unchecked") 125 @Override 126 public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, 127 BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) { 128 if (theType.isAssignableFrom(theNextExt.getClass())) { 129 retVal.add((T) theNextExt); 130 } 131 if (theNextExt.getValue() != null && theType.isAssignableFrom(theNextExt.getValue().getClass())) { 132 retVal.add((T) theNextExt.getValue()); 133 } 134 } 135 }); 136 return retVal; 137 } 138 139 public List<ResourceReferenceInfo> getAllResourceReferences(final IBaseResource theResource) { 140 final ArrayList<ResourceReferenceInfo> retVal = new ArrayList<ResourceReferenceInfo>(); 141 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 142 visit(new IdentityHashMap<Object, Object>(),theResource, null, null, def, new IModelVisitor() { 143 @Override 144 public void acceptElement(IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition) { 145 if (theElement == null || theElement.isEmpty()) { 146 return; 147 } 148 if (IBaseReference.class.isAssignableFrom(theElement.getClass())) { 149 retVal.add(new ResourceReferenceInfo(myContext, theResource, thePathToElement, (IBaseReference) theElement)); 150 } 151 } 152 153 @Override 154 public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, 155 BaseRuntimeElementDefinition<?> theDefinition, ExtensionDt theNextExt) { 156 if (theNextExt.getValue() != null && BaseResourceReferenceDt.class.isAssignableFrom(theNextExt.getValue().getClass())) { 157 retVal.add(new ResourceReferenceInfo(myContext, theResource, thePathToElement, (BaseResourceReferenceDt) theNextExt.getValue())); 158 } 159 } 160 }); 161 return retVal; 162 } 163 164 private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, List<String> theSubList) { 165 BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0)); 166 167 if (theSubList.size() == 1) { 168 return nextDef; 169 } else { 170 BaseRuntimeElementCompositeDefinition<?> cmp = (BaseRuntimeElementCompositeDefinition<?>) nextDef.getChildByName(theSubList.get(0)); 171 return getDefinition(cmp, theSubList.subList(1, theSubList.size())); 172 } 173 } 174 175 public BaseRuntimeChildDefinition getDefinition(Class<? extends IBaseResource> theResourceType, String thePath) { 176 RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType); 177 178 BaseRuntimeElementCompositeDefinition<?> currentDef = def; 179 180 List<String> parts = Arrays.asList(thePath.split("\\.")); 181 List<String> subList = parts.subList(1, parts.size()); 182 if (subList.size() < 1) { 183 throw new ConfigurationException("Invalid path: " + thePath); 184 } 185 return getDefinition(currentDef, subList); 186 187 } 188 189 @SuppressWarnings("unchecked") 190 private <T> List<T> getValues(BaseRuntimeElementCompositeDefinition<?> theCurrentDef, Object theCurrentObj, List<String> theSubList, Class<T> theWantedClass) { 191 String name = theSubList.get(0); 192 193 BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(name); 194 List<? extends IBase> values = nextDef.getAccessor().getValues(theCurrentObj); 195 List<T> retVal = new ArrayList<T>(); 196 197 if (theSubList.size() == 1) { 198 if (nextDef instanceof RuntimeChildChoiceDefinition) { 199 for (IBase next : values) { 200 if (next != null) { 201 if (name.endsWith("[x]")) { 202 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 203 retVal.add((T) next); 204 } 205 } else { 206 String childName = nextDef.getChildNameByDatatype(next.getClass()); 207 if (theSubList.get(0).equals(childName)) { 208 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 209 retVal.add((T) next); 210 } 211 } 212 } 213 } 214 } 215 } else { 216 for (IBase next : values) { 217 if (next != null) { 218 if (theWantedClass == null || theWantedClass.isAssignableFrom(next.getClass())) { 219 retVal.add((T) next); 220 } 221 } 222 } 223 } 224 } else { 225 for (IBase nextElement : values) { 226 BaseRuntimeElementCompositeDefinition<?> nextChildDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(nextElement.getClass()); 227 List<T> foundValues = getValues(nextChildDef, nextElement, theSubList.subList(1, theSubList.size()), theWantedClass); 228 retVal.addAll(foundValues); 229 } 230 } 231 return retVal; 232 } 233 234 public List<Object> getValues(IBaseResource theResource, String thePath) { 235 Class<Object> wantedClass = Object.class; 236 237 return getValues(theResource, thePath, wantedClass); 238 239 } 240 241 public <T> List<T> getValues(IBaseResource theResource, String thePath, Class<T> theWantedClass) { 242 RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); 243 244 BaseRuntimeElementCompositeDefinition<?> currentDef = def; 245 Object currentObj = theResource; 246 247 List<String> parts = Arrays.asList(thePath.split("\\.")); 248 List<String> subList = parts.subList(1, parts.size()); 249 if (subList.size() < 1) { 250 throw new ConfigurationException("Invalid path: " + thePath); 251 } 252 return getValues(currentDef, currentObj, subList, theWantedClass); 253 } 254 255 private List<String> addNameToList(List<String> theCurrentList, BaseRuntimeChildDefinition theChildDefinition) { 256 if (theChildDefinition == null) 257 return null; 258 if (theCurrentList == null || theCurrentList.isEmpty()) 259 return new ArrayList<String>(Arrays.asList(theChildDefinition.getElementName())); 260 List<String> newList = new ArrayList<String>(theCurrentList); 261 newList.add(theChildDefinition.getElementName()); 262 return newList; 263 } 264 265 private void visit(IdentityHashMap<Object, Object> theStack, IBase theElement, List<String> thePathToElement, BaseRuntimeChildDefinition theChildDefinition, 266 BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor theCallback) { 267 List<String> pathToElement = addNameToList(thePathToElement, theChildDefinition); 268 269 if (theStack.put(theElement, theElement) != null) { 270 return; 271 } 272 273 theCallback.acceptElement(theElement, pathToElement, theChildDefinition, theDefinition); 274 addUndeclaredExtensions(theElement, theDefinition, theChildDefinition, theCallback); 275 276 BaseRuntimeElementDefinition<?> def = theDefinition; 277 if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) { 278 def = myContext.getElementDefinition(theElement.getClass()); 279 } 280 281 switch (def.getChildType()) { 282 case ID_DATATYPE: 283 case PRIMITIVE_XHTML_HL7ORG: 284 case PRIMITIVE_XHTML: 285 case PRIMITIVE_DATATYPE: 286 // These are primitive types 287 break; 288 case RESOURCE_REF: 289 IBaseReference resRefDt = (IBaseReference) theElement; 290 if (resRefDt.getReferenceElement().getValue() == null && resRefDt.getResource() != null) { 291 IBaseResource theResource = resRefDt.getResource(); 292 if (theResource.getIdElement() == null || theResource.getIdElement().isEmpty() || theResource.getIdElement().isLocal()) { 293 def = myContext.getResourceDefinition(theResource); 294 visit(theStack, theResource, pathToElement, null, def, theCallback); 295 } 296 } 297 break; 298 case RESOURCE: 299 case RESOURCE_BLOCK: 300 case COMPOSITE_DATATYPE: { 301 BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) def; 302 for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { 303 List<? extends IBase> values = nextChild.getAccessor().getValues(theElement); 304 if (values != null) { 305 for (IBase nextValue : values) { 306 if (nextValue == null) { 307 continue; 308 } 309 if (nextValue.isEmpty()) { 310 continue; 311 } 312 BaseRuntimeElementDefinition<?> childElementDef; 313 childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass()); 314 315 if (childElementDef == null) { 316 childElementDef = myContext.getElementDefinition(nextValue.getClass()); 317 } 318 319 if (nextChild instanceof RuntimeChildDirectResource) { 320 // Don't descend into embedded resources 321 theCallback.acceptElement(nextValue, null, nextChild, childElementDef); 322 } else { 323 visit(theStack, nextValue, pathToElement, nextChild, childElementDef, theCallback); 324 } 325 } 326 } 327 } 328 break; 329 } 330 case CONTAINED_RESOURCES: { 331 BaseContainedDt value = (BaseContainedDt) theElement; 332 for (IResource next : value.getContainedResources()) { 333 def = myContext.getResourceDefinition(next); 334 visit(theStack, next, pathToElement, null, def, theCallback); 335 } 336 break; 337 } 338 case CONTAINED_RESOURCE_LIST: 339 case EXTENSION_DECLARED: 340 case UNDECL_EXT: { 341 throw new IllegalStateException("state should not happen: " + def.getChildType()); 342 } 343 } 344 345 theStack.remove(theElement); 346 347 } 348 349 private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition<?> theDefinition, IModelVisitor2 theCallback, List<IBase> theContainingElementPath, 350 List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) { 351 if (theChildDefinition != null) { 352 theChildDefinitionPath.add(theChildDefinition); 353 } 354 theContainingElementPath.add(theElement); 355 theElementDefinitionPath.add(theDefinition); 356 357 theCallback.acceptElement(theElement, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath), 358 Collections.unmodifiableList(theElementDefinitionPath)); 359 360 /* 361 * Visit undeclared extensions 362 */ 363 if (theElement instanceof ISupportsUndeclaredExtensions) { 364 ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement; 365 for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) { 366 theContainingElementPath.add(nextExt); 367 theCallback.acceptUndeclaredExtension(nextExt, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 368 theContainingElementPath.remove(theContainingElementPath.size() - 1); 369 } 370 } 371 372 /* 373 * Now visit the children of the given element 374 */ 375 switch (theDefinition.getChildType()) { 376 case ID_DATATYPE: 377 case PRIMITIVE_XHTML_HL7ORG: 378 case PRIMITIVE_XHTML: 379 case PRIMITIVE_DATATYPE: 380 // These are primitive types, so we don't need to visit their children 381 break; 382 case RESOURCE_REF: 383 IBaseReference resRefDt = (IBaseReference) theElement; 384 if (resRefDt.getReferenceElement().getValue() == null && resRefDt.getResource() != null) { 385 IBaseResource theResource = resRefDt.getResource(); 386 if (theResource.getIdElement() == null || theResource.getIdElement().isEmpty() || theResource.getIdElement().isLocal()) { 387 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 388 visit(theResource, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 389 } 390 } 391 break; 392 case RESOURCE: 393 case RESOURCE_BLOCK: 394 case COMPOSITE_DATATYPE: { 395 BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition; 396 for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) { 397 List<? extends IBase> values = nextChild.getAccessor().getValues(theElement); 398 if (values != null) { 399 for (IBase nextValue : values) { 400 if (nextValue == null) { 401 continue; 402 } 403 if (nextValue.isEmpty()) { 404 continue; 405 } 406 BaseRuntimeElementDefinition<?> childElementDef; 407 childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass()); 408 409 if (childElementDef == null) { 410 StringBuilder b = new StringBuilder(); 411 b.append("Found value of type["); 412 b.append(nextValue.getClass().getSimpleName()); 413 b.append("] which is not valid for field["); 414 b.append(nextChild.getElementName()); 415 b.append("] in "); 416 b.append(childDef.getName()); 417 b.append(" - Valid types: "); 418 for (Iterator<String> iter = new TreeSet<String>(nextChild.getValidChildNames()).iterator(); iter.hasNext();) { 419 BaseRuntimeElementDefinition<?> childByName = nextChild.getChildByName(iter.next()); 420 b.append(childByName.getImplementingClass().getSimpleName()); 421 if (iter.hasNext()) { 422 b.append(", "); 423 } 424 } 425 throw new DataFormatException(b.toString()); 426 } 427 428 if (nextChild instanceof RuntimeChildDirectResource) { 429 // Don't descend into embedded resources 430 theContainingElementPath.add(nextValue); 431 theChildDefinitionPath.add(nextChild); 432 theElementDefinitionPath.add(myContext.getElementDefinition(nextValue.getClass())); 433 theCallback.acceptElement(nextValue, Collections.unmodifiableList(theContainingElementPath), Collections.unmodifiableList(theChildDefinitionPath), 434 Collections.unmodifiableList(theElementDefinitionPath)); 435 theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1); 436 theContainingElementPath.remove(theContainingElementPath.size() - 1); 437 theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1); 438 } else { 439 visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 440 } 441 } 442 } 443 } 444 break; 445 } 446 case CONTAINED_RESOURCES: { 447 BaseContainedDt value = (BaseContainedDt) theElement; 448 for (IResource next : value.getContainedResources()) { 449 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(next); 450 visit(next, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 451 } 452 break; 453 } 454 case EXTENSION_DECLARED: 455 case UNDECL_EXT: { 456 throw new IllegalStateException("state should not happen: " + theDefinition.getChildType()); 457 } 458 case CONTAINED_RESOURCE_LIST: { 459 if (theElement != null) { 460 BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theElement.getClass()); 461 visit(theElement, null, def, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath); 462 } 463 break; 464 } 465 } 466 467 if (theChildDefinition != null) { 468 theChildDefinitionPath.remove(theChildDefinitionPath.size() - 1); 469 } 470 theContainingElementPath.remove(theContainingElementPath.size() - 1); 471 theElementDefinitionPath.remove(theElementDefinitionPath.size() - 1); 472 } 473 474 /** 475 * Visit all elements in a given resource 476 * 477 * <p> 478 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g. 479 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource) 480 * </p> 481 * 482 * @param theResource 483 * The resource to visit 484 * @param theVisitor 485 * The visitor 486 */ 487 public void visit(IBaseResource theResource, IModelVisitor theVisitor) { 488 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 489 visit(new IdentityHashMap<Object, Object>(), theResource, null, null, def, theVisitor); 490 } 491 492 /** 493 * Visit all elements in a given resource 494 * 495 * THIS ALTERNATE METHOD IS STILL EXPERIMENTAL 496 * 497 * <p> 498 * Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g. 499 * {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource) 500 * </p> 501 * 502 * @param theResource 503 * The resource to visit 504 * @param theVisitor 505 * The visitor 506 */ 507 void visit(IBaseResource theResource, IModelVisitor2 theVisitor) { 508 BaseRuntimeElementCompositeDefinition<?> def = myContext.getResourceDefinition(theResource); 509 visit(theResource, null, def, theVisitor, new ArrayList<IBase>(), new ArrayList<BaseRuntimeChildDefinition>(), new ArrayList<BaseRuntimeElementDefinition<?>>()); 510 } 511 512 public Object getSingleValueOrNull(IBase theTarget, String thePath) { 513 Class<Object> wantedType = Object.class; 514 515 return getSingleValueOrNull(theTarget, thePath, wantedType); 516 } 517 518 public <T> T getSingleValueOrNull(IBase theTarget, String thePath, Class<T> theWantedType) { 519 Validate.notNull(theTarget, "theTarget must not be null"); 520 Validate.notBlank(thePath, "thePath must not be empty"); 521 522 BaseRuntimeElementDefinition<?> def = myContext.getElementDefinition(theTarget.getClass()); 523 if (!(def instanceof BaseRuntimeElementCompositeDefinition)) { 524 throw new IllegalArgumentException("Target is not a composite type: " + theTarget.getClass().getName()); 525 } 526 527 BaseRuntimeElementCompositeDefinition<?> currentDef = (BaseRuntimeElementCompositeDefinition<?>) def; 528 Object currentObj = theTarget; 529 530 List<String> parts = Arrays.asList(thePath.split("\\.")); 531 List<T> retVal = getValues(currentDef, currentObj, parts, theWantedType); 532 if (retVal.isEmpty()) { 533 return null; 534 } else { 535 return retVal.get(0); 536 } 537 } 538 539 /** 540 * Clones all values from a source object into the equivalent fields in a target object 541 * @param theSource The source object (must not be null) 542 * @param theTarget The target object to copy values into (must not be null) 543 * @param theIgnoreMissingFields The ignore fields in the target which do not exist (if false, an exception will be thrown if the target is unable to accept a value from the source) 544 */ 545 public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) { 546 Validate.notNull(theSource, "theSource must not be null"); 547 Validate.notNull(theTarget, "theTarget must not be null"); 548 549 if (theSource instanceof IPrimitiveType<?>) { 550 if (theTarget instanceof IPrimitiveType<?>) { 551 ((IPrimitiveType<?>)theTarget).setValueAsString(((IPrimitiveType<?>)theSource).getValueAsString()); 552 return; 553 } else { 554 if (theIgnoreMissingFields) { 555 return; 556 } else { 557 throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName()); 558 } 559 } 560 } 561 562 BaseRuntimeElementCompositeDefinition<?> sourceDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theSource.getClass()); 563 BaseRuntimeElementCompositeDefinition<?> targetDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theTarget.getClass()); 564 565 for (BaseRuntimeChildDefinition nextChild : sourceDef.getChildren()) { 566 for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) { 567 BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(nextChild.getElementName()); 568 if (targetChild == null) { 569 if (theIgnoreMissingFields) { 570 continue; 571 } else { 572 throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + nextChild.getElementName()); 573 } 574 } 575 576 IBase target = targetChild.getChildByName(nextChild.getElementName()).newInstance(); 577 targetChild.getMutator().addValue(theTarget, target); 578 cloneInto(nextValue, target, theIgnoreMissingFields); 579 } 580 } 581 582 } 583 584}