fix problem with Element childMap getting out of sync

This commit is contained in:
Grahame Grieve 2023-09-21 12:40:30 +10:00
parent 3801f2e702
commit 7c2f403d55
2 changed files with 174 additions and 67 deletions

View File

@ -54,6 +54,8 @@ import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.ElementDecoration;
import org.hl7.fhir.utilities.ElementDecoration.DecorationType;
import org.hl7.fhir.utilities.NamedItemList;
import org.hl7.fhir.utilities.NamedItemList.NamedItem;
import org.hl7.fhir.utilities.SourceLocation;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@ -69,7 +71,7 @@ import org.hl7.fhir.utilities.xhtml.XhtmlNode;
* @author Grahame Grieve
*
*/
public class Element extends Base {
public class Element extends Base implements NamedItem {
private static final HashSet<String> extensionList = new HashSet<>(Arrays.asList("extension", "modifierExtension"));
public enum SpecialElement {
@ -109,7 +111,7 @@ public class Element extends Base {
private String type;
private String value;
private int index = -1;
private List<Element> children;
private NamedItemList<Element> children;
private Property property;
private Property elementProperty; // this is used when special is set to true - it tracks the underlying element property which is used in a few places
private int line;
@ -123,7 +125,6 @@ public class Element extends Base {
private List<ValidationMessage> messages;
private boolean prohibited;
private boolean required;
private Map<String, List<Element>> childMap;
private int descendentCount;
private int instanceId;
private boolean isNull;
@ -149,7 +150,7 @@ public class Element extends Base {
this.name = name;
this.property = property;
if (property.isResource()) {
children = new ArrayList<>();
children = new NamedItemList<>();
}
}
@ -190,9 +191,9 @@ public class Element extends Base {
return !(children == null || children.isEmpty());
}
public List<Element> getChildren() {
public NamedItemList<Element> getChildren() {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
return children;
}
@ -233,21 +234,7 @@ public class Element extends Base {
}
public List<Element> getChildrenByName(String name) {
List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l != null) {
res.addAll(l);
}
} else {
if (hasChildren()) {
for (Element child : children)
if (name.equals(child.getName()))
res.add(child);
}
}
return res;
return children.getByName(name);
}
public void numberChildren() {
@ -308,7 +295,7 @@ public class Element extends Base {
public void setChildValue(String name, String value) {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
for (Element child : children) {
if (name.equals(child.getName())) {
if (!child.isPrimitive())
@ -316,7 +303,7 @@ public class Element extends Base {
child.setValue(value);
}
}
childMap = null;
try {
setProperty(name.hashCode(), name, new StringType(value));
} catch (FHIRException e) {
@ -327,8 +314,7 @@ public class Element extends Base {
public List<Element> getChildren(String name) {
List<Element> res = new ArrayList<Element>();
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
res.addAll(l);
}
@ -367,8 +353,7 @@ public class Element extends Base {
List<Base> result = new ArrayList<Base>();
if (children != null) {
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
result.addAll(l);
}
@ -389,27 +374,6 @@ public class Element extends Base {
return result.toArray(new Base[result.size()]);
}
private void populateChildMap() {
if (childMap == null) {
childMap = new HashMap<>();
for (Element child : children) {
String n;
if (child.getProperty().getName().endsWith("[x]")) {
n = child.getProperty().getName();
n = n.substring(0, n.length()-3);
} else {
n = child.getName();
}
List<Element> l = childMap.get(n);
if (l == null) {
l = new ArrayList<Element>();
childMap.put(n,l);
}
l.add(child);
}
}
}
@Override
protected void listChildren(List<org.hl7.fhir.r5.model.Property> childProps) {
if (children != null) {
@ -446,9 +410,8 @@ public class Element extends Base {
throw new FHIRException("Cannot set property "+name+" on "+this.name+" - value is not a primitive type ("+value.fhirType()+") or an ElementModel type");
}
childMap = null;
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
Element childForValue = null;
// look through existing children
@ -505,7 +468,7 @@ public class Element extends Base {
}
if (ve.children != null) {
if (childForValue.children == null)
childForValue.children = new ArrayList<Element>();
childForValue.children = new NamedItemList<Element>();
else
childForValue.children.clear();
childForValue.children.addAll(ve.children);
@ -533,7 +496,7 @@ public class Element extends Base {
public Element makeElement(String name) throws FHIRException {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
// look through existing children
for (Element child : children) {
@ -572,7 +535,7 @@ public class Element extends Base {
public Element forceElement(String name) throws FHIRException {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
// look through existing children
for (Element child : children) {
@ -669,7 +632,6 @@ public class Element extends Base {
for (Element e : children) {
e.clearDecorations();
}
childMap = null;
}
public void markValidation(StructureDefinition profile, ElementDefinition definition) {
@ -694,9 +656,8 @@ public class Element extends Base {
if (children == null)
return null;
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
if (l == null) {
List<Element> l = children.getByName(name);
if (l == null || l.size() == 0) {
// try the other way (in case of complicated naming rules)
} else if (l.size() > 1) {
throw new Error("Attempt to read a single element when there is more than one present ("+name+")");
@ -724,8 +685,7 @@ public class Element extends Base {
public void getNamedChildren(String name, List<Element> list) {
if (children != null)
if (children.size() > 20) {
populateChildMap();
List<Element> l = childMap.get(name);
List<Element> l = children.getByName(name);
if (l != null) {
list.addAll(l);
}
@ -918,8 +878,7 @@ public class Element extends Base {
remove.add(child);
}
children.removeAll(remove);
Collections.sort(children, new ElementSortComparator(this, this.property));
childMap = null;
children.sort(new ElementSortComparator(this, this.property));
}
}
@ -1125,7 +1084,6 @@ public class Element extends Base {
public void clear() {
comments = null;
children.clear();
childMap = null;
property = null;
elementProperty = null;
xhtml = null;
@ -1157,7 +1115,6 @@ public class Element extends Base {
public void removeChild(String name) {
children.removeIf(n -> name.equals(n.getName()));
childMap = null;
}
public boolean isProhibited() {
@ -1331,7 +1288,7 @@ public class Element extends Base {
public Element addElement(String name) {
if (children == null)
children = new ArrayList<Element>();
children = new NamedItemList<Element>();
for (Property p : property.getChildProperties(this.name, type)) {
if (p.getName().equals(name)) {
@ -1367,13 +1324,13 @@ public class Element extends Base {
}
dest.value = value;
if (children != null) {
dest.children = new ArrayList<>();
dest.children = new NamedItemList<>();
for (Element child : children) {
dest.children.add((Element) child.copy());
}
} else {
dest.children = null;
}
}
dest.line = line;
dest.col = col;
dest.xhtml = xhtml;
@ -1383,7 +1340,6 @@ public class Element extends Base {
dest.messages = null;
dest.prohibited = prohibited;
dest.required = required;
dest.childMap = null;
dest.descendentCount = descendentCount;
dest.instanceId = instanceId;
dest.isNull = isNull;
@ -1499,4 +1455,15 @@ public class Element extends Base {
ext.addElement("url").setValue("value");
ext.addElement("valueString").setValue(translation);
}
@Override
public String getListName() {
if (getProperty().getName().endsWith("[x]")) {
String n = getProperty().getName();
return n.substring(0, n.length()-3);
} else {
return getName();
}
}
}

View File

@ -0,0 +1,140 @@
package org.hl7.fhir.utilities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class NamedItemList<T extends org.hl7.fhir.utilities.NamedItemList.NamedItem> implements Collection<T> {
public interface NamedItem {
public String getListName();
}
private static final int SIZE_CUTOFF_MAP = 10;
private List<T> list = new ArrayList<>();
private Map<String, List<T>> map = null;
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean add(T e) {
map = null;
return list.add(e);
}
public void add(int index, T e) {
list.add(index, e);
map = null;
}
@Override
public boolean remove(Object o) {
map = null;
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
map = null;
return list.addAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
map = null;
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
map = null;
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
map = null;
}
public List<T> getByName(String name) {
List<T> res = new ArrayList<>();
if (size() > SIZE_CUTOFF_MAP) {
if (map == null) {
buildMap();
}
List<T> l = map.get(name);
if (l != null) {
res.addAll(l);
}
} else {
for (T child : list) {
if (name.equals(child.getListName())) {
res.add(child);
}
}
}
return res;
}
public T get(int c) {
return list.get(c);
}
private void buildMap() {
map = new HashMap<>();
for (T child : list) {
String n = child.getListName();
List<T> l = map.get(n);
if (l == null) {
l = new ArrayList<>();
map.put(n,l);
}
l.add(child);
}
}
public void sort(Comparator<? super T> sorter) {
Collections.sort(list, sorter);
}
}