From 8b84b8ffea0070008d0cadc78fce257ac3cdd290 Mon Sep 17 00:00:00 2001 From: David Maplesden Date: Tue, 10 Sep 2019 12:41:16 +1200 Subject: [PATCH] Cache the composite children that need encoding to improve performance --- .../java/ca/uhn/fhir/parser/BaseParser.java | 170 ++++++++++-------- 1 file changed, 97 insertions(+), 73 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 8226600c24a..c9d433a89b8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -68,6 +68,47 @@ public abstract class BaseParser implements IParser { private boolean mySuppressNarratives; private Set myDontStripVersionsFromReferencesAtPaths; + private Map> compositeChildrenCache = new HashMap<>(); + + private static class Key { + private final BaseRuntimeElementCompositeDefinition resDef; + private final boolean theContainedResource; + private final CompositeChildElement theParent; + private final EncodeContext theEncodeContext; + + public Key(BaseRuntimeElementCompositeDefinition resDef, final boolean theContainedResource, final CompositeChildElement theParent, EncodeContext theEncodeContext) { + this.resDef = resDef; + this.theContainedResource = theContainedResource; + this.theParent = theParent; + this.theEncodeContext = theEncodeContext; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((resDef == null) ? 0 : resDef.hashCode()); + result = prime * result + (theContainedResource ? 1231 : 1237); + result = prime * result + ((theParent == null) ? 0 : theParent.hashCode()); + result = prime * result + ((theEncodeContext == null) ? 0 : theEncodeContext.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Key) { + final Key that = (Key) obj; + return Objects.equals(this.resDef, that.resDef) && + this.theContainedResource == that.theContainedResource && + Objects.equals(this.theParent, that.theParent) && + Objects.equals(this.theEncodeContext, that.theEncodeContext); + } + return false; + } + } + /** * Constructor */ @@ -129,85 +170,37 @@ public abstract class BaseParser implements IParser { } protected Iterable compositeChildIterator(IBase theCompositeElement, final boolean theContainedResource, final CompositeChildElement theParent, EncodeContext theEncodeContext) { - BaseRuntimeElementCompositeDefinition elementDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(theCompositeElement.getClass()); - final List children = elementDef.getChildrenAndExtension(); + return compositeChildrenCache.computeIfAbsent(new Key(elementDef, theContainedResource, theParent, theEncodeContext), (k) -> { + + final List children = elementDef.getChildrenAndExtension(); + final List result = new ArrayList<>(children.size()); - return new Iterable() { + for(final BaseRuntimeChildDefinition child: children) { + CompositeChildElement myNext = new CompositeChildElement(theParent, child, theEncodeContext); - @Override - public Iterator iterator() { - - return new Iterator() { - private Iterator myChildrenIter; - private Boolean myHasNext = null; - private CompositeChildElement myNext; - - /** - * Constructor - */ { - myChildrenIter = children.iterator(); + /* + * There are lots of reasons we might skip encoding a particular child + */ + if (myNext.getDef().getElementName().equals("id")) { + continue; + } else if (!myNext.shouldBeEncoded(theContainedResource)) { + continue; + } else if (myNext.getDef() instanceof RuntimeChildNarrativeDefinition) { + if (isSuppressNarratives() || isSummaryMode()) { + continue; + } else if (theContainedResource) { + continue; } - - @Override - public boolean hasNext() { - if (myHasNext != null) { - return myHasNext; - } - - myNext = null; - do { - if (myChildrenIter.hasNext() == false) { - myHasNext = Boolean.FALSE; - return false; - } - - myNext = new CompositeChildElement(theParent, myChildrenIter.next(), theEncodeContext); - - /* - * There are lots of reasons we might skip encoding a particular child - */ - if (myNext.getDef().getElementName().equals("id")) { - myNext = null; - } else if (!myNext.shouldBeEncoded(theContainedResource)) { - myNext = null; - } else if (myNext.getDef() instanceof RuntimeChildNarrativeDefinition) { - if (isSuppressNarratives() || isSummaryMode()) { - myNext = null; - } else if (theContainedResource) { - myNext = null; - } - } else if (myNext.getDef() instanceof RuntimeChildContainedResources) { - if (theContainedResource) { - myNext = null; - } - } - } while (myNext == null); - - myHasNext = true; - return true; + } else if (myNext.getDef() instanceof RuntimeChildContainedResources) { + if (theContainedResource) { + continue; } - - @Override - public CompositeChildElement next() { - if (myHasNext == null) { - if (!hasNext()) { - throw new IllegalStateException(); - } - } - CompositeChildElement retVal = myNext; - myNext = null; - myHasNext = null; - return retVal; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; + } + result.add(myNext); } - }; + return result; + }); } private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) { @@ -1274,6 +1267,37 @@ public abstract class BaseParser implements IParser { return retVal; } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((myDef == null) ? 0 : myDef.hashCode()); + result = prime * result + ((myParent == null) ? 0 : myParent.hashCode()); + result = prime * result + ((myResDef == null) ? 0 : myResDef.hashCode()); + result = prime * result + ((myEncodeContext == null) ? 0 : myEncodeContext.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (obj instanceof CompositeChildElement) { + final CompositeChildElement that = (CompositeChildElement) obj; + return Objects.equals(this.getEnclosingInstance(), that.getEnclosingInstance()) && + Objects.equals(this.myDef, that.myDef) && + Objects.equals(this.myParent, that.myParent) && + Objects.equals(this.myResDef, that.myResDef) && + Objects.equals(this.myEncodeContext, that.myEncodeContext); + } + return false; + } + + private BaseParser getEnclosingInstance() { + return BaseParser.this; + } } protected class EncodeContextPath {