Skip to content

Commit

Permalink
Ensure thread safe for attribute currentNode
Browse files Browse the repository at this point in the history
  • Loading branch information
prakanth97 committed Jan 3, 2024
1 parent e7a9b64 commit 3872a93
Showing 1 changed file with 48 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public class XmlParser {
}

private XMLStreamReader xmlStreamReader;
private static BMap<BString, Object> currentNode;
public static final String PARSE_ERROR = "failed to parse xml";
public static final String PARSE_ERROR_PREFIX = PARSE_ERROR + ": ";

Expand Down Expand Up @@ -152,7 +151,7 @@ public Object parse(XmlParserData xmlParserData) {
handleXMLStreamException(e);
}

return currentNode;
return xmlParserData.currentNode;
}

private boolean parseXmlElements(int next, XmlParserData xmlParserData) throws XMLStreamException {
Expand Down Expand Up @@ -188,7 +187,7 @@ public void parseRecordRest(String startElementName, XmlParserData xmlParserData
if (next == END_ELEMENT) {
QName endElement = xmlStreamReader.getName();
if (endElement.getLocalPart().equals(startElementName)) {
validateRequiredFields(currentNode, xmlParserData);
validateRequiredFields(xmlParserData);
xmlParserData.fieldHierarchy.pop();
xmlParserData.restTypes.pop();
xmlParserData.attributeHierarchy.pop();
Expand Down Expand Up @@ -217,7 +216,7 @@ private void parseRootElement(XMLStreamReader xmlStreamReader,
}

RecordType rootRecord = xmlParserData.rootRecord;
currentNode = ValueCreator.createRecordValue(rootRecord);
xmlParserData.currentNode = ValueCreator.createRecordValue(rootRecord);
QualifiedName elementQName = getElementName(xmlStreamReader);
xmlParserData.rootElement =
DataUtils.validateAndGetXmlNameFromRecordAnnotation(rootRecord, rootRecord.getName(), elementQName);
Expand Down Expand Up @@ -263,17 +262,17 @@ private void readText(XMLStreamReader xmlStreamReader,
throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_TYPE, fieldType, PredefinedTypes.TYPE_STRING);
}

if (currentNode.containsKey(bFieldName)) {
if (xmlParserData.currentNode.containsKey(bFieldName)) {
if (!DataUtils.isArrayValueAssignable(fieldType.getTag())) {
throw DiagnosticLog.error(DiagnosticErrorCode.FOUND_ARRAY_FOR_NON_ARRAY_TYPE, fieldType, fieldName);
}

int arraySize = ((ArrayType) fieldType).getSize();
if (arraySize != -1 && arraySize <= ((BArray) currentNode.get(bFieldName)).getLength()) {
if (arraySize != -1 && arraySize <= ((BArray) xmlParserData.currentNode.get(bFieldName)).getLength()) {
return;
}

((BArray) currentNode.get(bFieldName)).append(convertStringToRestExpType(bText, fieldType));
((BArray) xmlParserData.currentNode.get(bFieldName)).append(convertStringToRestExpType(bText, fieldType));
return;
}

Expand All @@ -283,7 +282,7 @@ private void readText(XMLStreamReader xmlStreamReader,
&& ((ArrayType) fieldType).getElementType().getTag() == TypeTags.RECORD_TYPE_TAG) {
handleContentFieldInRecordType((RecordType) ((ArrayType) fieldType).getElementType(), bText, xmlParserData);
} else {
currentNode.put(bFieldName, convertStringToRestExpType(bText, fieldType));
xmlParserData.currentNode.put(bFieldName, convertStringToRestExpType(bText, fieldType));
}
}

Expand All @@ -305,9 +304,9 @@ private void handleContentFieldInRecordType(RecordType recordType, BString text,
popStacks(xmlParserData);
for (String key : recordType.getFields().keySet()) {
if (key.contains(Constants.CONTENT)) {
currentNode.put(StringUtils.fromString(key),
xmlParserData.currentNode.put(StringUtils.fromString(key),
convertStringToExpType(text, recordType.getFields().get(key).getFieldType()));
currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
xmlParserData.currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
return;
}
}
Expand All @@ -317,8 +316,9 @@ private void handleContentFieldInRecordType(RecordType recordType, BString text,
return;
}

currentNode.put(StringUtils.fromString(Constants.CONTENT), convertStringToRestExpType(text, restType));
currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
xmlParserData.currentNode.put(StringUtils.fromString(Constants.CONTENT),
convertStringToRestExpType(text, restType));
xmlParserData.currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
}

private Object convertStringToExpType(BString value, Type expType) {
Expand Down Expand Up @@ -352,8 +352,8 @@ private Object convertStringToRestExpType(BString value, Type expType) {
}

private Object buildDocument(XmlParserData xmlParserData) {
validateRequiredFields(currentNode, xmlParserData);
return currentNode;
validateRequiredFields(xmlParserData);
return xmlParserData.currentNode;
}

@SuppressWarnings("unchecked")
Expand All @@ -369,12 +369,13 @@ private void endElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParser
return;
}

validateRequiredFields(currentNode, xmlParserData);
currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
validateRequiredFields(xmlParserData);
xmlParserData.currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
popStacks(xmlParserData);
}

private void validateRequiredFields(BMap<BString, Object> currentMapValue, XmlParserData xmlParserData) {
private void validateRequiredFields(XmlParserData xmlParserData) {
BMap<BString, Object> currentMapValue = xmlParserData.currentNode;
Map<QualifiedName, Field> fields = xmlParserData.fieldHierarchy.peek();
for (QualifiedName key : fields.keySet()) {
// Validate required array size
Expand Down Expand Up @@ -410,11 +411,12 @@ private void readElement(XMLStreamReader xmlStreamReader, XmlParserData xmlParse
xmlParserData.currentField = currentField;
if (xmlParserData.currentField == null) {
if (xmlParserData.restTypes.peek() != null) {
currentNode = handleRestField(xmlParserData);
xmlParserData.currentNode = handleRestField(xmlParserData);
}
return;
}

BMap<BString, Object> currentNode = xmlParserData.currentNode;
String fieldName = currentField.getFieldName();
Object temp = currentNode.get(StringUtils.fromString(fieldName));
BString bFieldName = StringUtils.fromString(fieldName);
Expand Down Expand Up @@ -456,7 +458,7 @@ private void updateNextRecord(XMLStreamReader xmlStreamReader, XmlParserData xml
xmlParserData.siblings = new LinkedHashMap<>();
xmlParserData.recordTypeStack.push(xmlParserData.rootRecord);
xmlParserData.rootRecord = recordType;
currentNode = updateNextValue(recordType, fieldName, fieldType, xmlParserData);
xmlParserData.currentNode = updateNextValue(recordType, fieldName, fieldType, xmlParserData);
QName qName = xmlStreamReader.getName();
DataUtils.validateTypeNamespace(qName.getPrefix(), qName.getNamespaceURI(), recordType);
handleAttributes(xmlStreamReader, xmlParserData);
Expand All @@ -466,6 +468,7 @@ private BMap<BString, Object> updateNextValue(RecordType rootRecord, String fiel
XmlParserData xmlParserData) {
BMap<BString, Object> nextValue = ValueCreator.createRecordValue(rootRecord);
updateExpectedTypeStacks(rootRecord, xmlParserData);
BMap<BString, Object> currentNode = xmlParserData.currentNode;
Object temp = currentNode.get(StringUtils.fromString(fieldName));
if (temp instanceof BArray) {
int arraySize = ((ArrayType) fieldType).getSize();
Expand Down Expand Up @@ -498,7 +501,7 @@ private BMap<BString, Object> handleRestField(XmlParserData xmlParserData) {
QualifiedName restStartPoint = xmlParserData.parents.isEmpty() ?
xmlParserData.rootElement : getLastElementInSiblings(xmlParserData.parents.peek());
xmlParserData.restFieldsPoints.push(restStartPoint);
xmlParserData.nodesStack.push(currentNode);
xmlParserData.nodesStack.push(xmlParserData.currentNode);
return (BMap<BString, Object>) parseRestField(xmlParserData);
}

Expand Down Expand Up @@ -554,12 +557,13 @@ private BString readElementRest(XMLStreamReader xmlStreamReader, XmlParserData x
xmlParserData.siblings = new LinkedHashMap<>();
xmlParserData.siblings.put(elemQName, false);
BMap<BString, Object> temp =
(BMap<BString, Object>) currentNode.get(StringUtils.fromString(lastElement.getLocalPart()));
(BMap<BString, Object>) xmlParserData.currentNode.get(
StringUtils.fromString(lastElement.getLocalPart()));
BMap<BString, Object> next =
ValueCreator.createMapValue(DataUtils.getMapTypeFromConstraintType(restType));
temp.put(currentFieldName, next);
xmlParserData.nodesStack.add(currentNode);
currentNode = temp;
xmlParserData.nodesStack.add(xmlParserData.currentNode);
xmlParserData.currentNode = temp;
handleAttributesRest(xmlStreamReader, restType, next);
return currentFieldName;
}
Expand All @@ -568,25 +572,25 @@ private BString readElementRest(XMLStreamReader xmlStreamReader, XmlParserData x
xmlParserData.siblings.put(elemQName, false);
if (restType.getTag() == TypeTags.ARRAY_TAG) {
BArray tempArray = ValueCreator.createArrayValue(DataUtils.getArrayTypeFromElementType(restType));
currentNode.put(currentFieldName, tempArray);
xmlParserData.currentNode.put(currentFieldName, tempArray);
} else {
BMap<BString, Object> next =
ValueCreator.createMapValue(DataUtils.getMapTypeFromConstraintType(restType));
currentNode.put(currentFieldName, next);
xmlParserData.currentNode.put(currentFieldName, next);
handleAttributesRest(xmlStreamReader, restType, next);
}
return currentFieldName;
}

xmlParserData.parents.push(xmlParserData.siblings);
xmlParserData.siblings = new LinkedHashMap<>();
Object currentElement = currentNode.get(currentFieldName);
xmlParserData.nodesStack.add(currentNode);
Object currentElement = xmlParserData.currentNode.get(currentFieldName);
xmlParserData.nodesStack.add(xmlParserData.currentNode);

if (currentElement instanceof BArray) {
int elemTypeTag = ((BArray) currentElement).getElementType().getTag();
if (elemTypeTag == TypeTags.ANYDATA_TAG || elemTypeTag == TypeTags.JSON_TAG) {
currentNode = updateNextArrayMemberForRestType((BArray) currentElement, restType);
xmlParserData.currentNode = updateNextArrayMemberForRestType((BArray) currentElement, restType);
}
return currentFieldName;
}
Expand All @@ -597,11 +601,11 @@ private BString readElementRest(XMLStreamReader xmlStreamReader, XmlParserData x
}
BArray tempArray = ValueCreator.createArrayValue(DataUtils.getArrayTypeFromElementType(restType));
tempArray.append(currentElement);
currentNode.put(currentFieldName, tempArray);
xmlParserData.currentNode.put(currentFieldName, tempArray);

int elemTypeTag = tempArray.getElementType().getTag();
if (elemTypeTag == TypeTags.ANYDATA_TAG || elemTypeTag == TypeTags.JSON_TAG) {
currentNode = updateNextArrayMemberForRestType(tempArray, restType);
xmlParserData.currentNode = updateNextArrayMemberForRestType(tempArray, restType);
}
return currentFieldName;
}
Expand All @@ -624,7 +628,7 @@ private void endElementRest(XMLStreamReader xmlStreamReader, XmlParserData xmlPa
return;
}

currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
xmlParserData.currentNode = (BMap<BString, Object>) xmlParserData.nodesStack.pop();
xmlParserData.siblings = xmlParserData.parents.pop();
if (xmlParserData.siblings.containsKey(elemQName) && xmlParserData.restFieldsPoints.remove(elemQName)) {
xmlParserData.fieldHierarchy.pop();
Expand Down Expand Up @@ -658,19 +662,19 @@ private void readTextRest(XMLStreamReader xmlStreamReader,
throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_TYPE, restType, PredefinedTypes.TYPE_STRING);
}

Object currentElement = currentNode.get(currentFieldName);
Object currentElement = xmlParserData.currentNode.get(currentFieldName);
BMap<BString, Object> parent = (BMap<BString, Object>) xmlParserData.nodesStack.peek();
Object result = convertStringToRestExpType(bText, restType);

if (currentElement == null && !currentNode.isEmpty()) { // Add text to the #content field
currentNode.put(StringUtils.fromString(Constants.CONTENT), result);
currentNode = parent;
if (currentElement == null && !xmlParserData.currentNode.isEmpty()) { // Add text to the #content field
xmlParserData.currentNode.put(StringUtils.fromString(Constants.CONTENT), result);
xmlParserData.currentNode = parent;

Check warning on line 671 in native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java

View check run for this annotation

Codecov / codecov/patch

native/src/main/java/io/ballerina/stdlib/data/xmldata/xml/XmlParser.java#L670-L671

Added lines #L670 - L671 were not covered by tests
} else if (currentElement instanceof BArray) {
((BArray) currentElement).append(result);
} else if (currentElement instanceof BMap && !((BMap<BString, Object>) currentElement).isEmpty()) {
((BMap<BString, Object>) currentElement).put(StringUtils.fromString(Constants.CONTENT), result);
} else {
currentNode.put(currentFieldName, result);
xmlParserData.currentNode.put(currentFieldName, result);
}
}

Expand Down Expand Up @@ -720,7 +724,7 @@ private Map<QualifiedName, Field> getAllAttributesInRecordType(RecordType record
for (BString annotationKey : annotations.getKeys()) {
String keyStr = annotationKey.getValue();
if (keyStr.contains(Constants.FIELD) && DataUtils.isAttributeField(annotationKey, annotations)) {
String attributeName = keyStr.split("\\$field\\$\\.")[1].replaceAll("\\\\", "");
String attributeName = keyStr.split(Constants.FIELD_REGEX)[1].replaceAll("\\\\", "");
Map<BString, Object> fieldAnnotation = (Map<BString, Object>) annotations.get(annotationKey);
QualifiedName fieldQName = DataUtils.getFieldNameFromRecord(fieldAnnotation, attributeName);
fieldQName.setLocalPart(getModifiedName(fieldAnnotation, attributeName));
Expand Down Expand Up @@ -754,7 +758,7 @@ private void handleAttributes(XMLStreamReader xmlStreamReader, XmlParserData xml
}

try {
currentNode.put(StringUtils.fromString(field.getFieldName()), convertStringToExpType(
xmlParserData.currentNode.put(StringUtils.fromString(field.getFieldName()), convertStringToExpType(
StringUtils.fromString(xmlStreamReader.getAttributeValue(i)), field.getFieldType()));
} catch (Exception e) {
// Ignore: Expected type will mismatch when element and attribute having same name.
Expand Down Expand Up @@ -785,7 +789,7 @@ private Optional<Object> handleRecordRestType(XmlParserData xmlParserData, XMLSt
switch (restType.getTag()) {
case TypeTags.RECORD_TYPE_TAG -> {
RecordType recordType = (RecordType) restType;
currentNode = updateNextValue(recordType, fieldName, restType, xmlParserData);
xmlParserData.currentNode = updateNextValue(recordType, fieldName, restType, xmlParserData);
handleAttributes(xmlStreamReader, xmlParserData);
parseRecordRest(fieldName, xmlParserData);
xmlParserData.siblings.clear();
Expand All @@ -796,11 +800,12 @@ private Optional<Object> handleRecordRestType(XmlParserData xmlParserData, XMLSt
Type elemType = TypeUtils.getReferredType(arrayType.getElementType());
if (elemType.getTag() == TypeTags.RECORD_TYPE_TAG) {
// Create an array value since expected type is an array.
if (!currentNode.containsKey(StringUtils.fromString(fieldName))) {
currentNode.put(StringUtils.fromString(fieldName),
if (!xmlParserData.currentNode.containsKey(StringUtils.fromString(fieldName))) {
xmlParserData.currentNode.put(StringUtils.fromString(fieldName),
ValueCreator.createArrayValue(DataUtils.getArrayTypeFromElementType(elemType)));
}
currentNode = updateNextValue((RecordType) elemType, fieldName, arrayType, xmlParserData);
xmlParserData.currentNode =
updateNextValue((RecordType) elemType, fieldName, arrayType, xmlParserData);
handleAttributes(xmlStreamReader, xmlParserData);
parseRecordRest(fieldName, xmlParserData);
xmlParserData.siblings.clear();
Expand Down Expand Up @@ -843,5 +848,6 @@ public static class XmlParserData {
private QualifiedName rootElement;
private final Stack<LinkedHashMap<QualifiedName, Boolean>> parents = new Stack<>();
private LinkedHashMap<QualifiedName, Boolean> siblings = new LinkedHashMap<>();
private BMap<BString, Object> currentNode;
}
}

0 comments on commit 3872a93

Please sign in to comment.