addCharacter method
void
addCharacter(
- String character
)
override
Implementation
@override
void addCharacter(String character) {
// Debug logging disabled to reduce noise
// print(
// 'MAP[$propertyPath] State: $_state | Char: |$character| | KeyBuffer: |$_keyBuffer| | ChildDone: ${_activeChildDelegate?.isDone}',
// );
if (_state == MapParserState.readingKey) {
if (character == '"') {
_state = MapParserState.waitingForValue;
return;
} else {
_keyBuffer += character;
return;
}
}
if (_state == MapParserState.readingValue) {
// Store the delegate reference before calling addCharacter
// because onComplete callback might clear it
final childDelegate = _activeChildDelegate;
childDelegate?.addCharacter(character);
final childIsDone = childDelegate?.isDone ?? false;
if (childIsDone) {
_state = MapParserState.waitingForCommaOrEnd;
_activeChildDelegate = null;
// Only reprocess if the child is NOT a list or map
// (lists and maps consume their own closing brackets)
final childType = childDelegate.runtimeType.toString();
if (childType == 'ListPropertyDelegate' ||
childType == 'MapPropertyDelegate') {
return; // Don't reprocess - child consumed the closing bracket
}
// For other types (numbers, strings, etc), reprocess the delimiter
} else {
return;
}
}
if (_state == MapParserState.waitingForValue) {
if (character == " " || character == ":") return;
// Add this key to our list of keys
_keys.add(_keyBuffer);
// FIRST: Determine the type and create the PropertyStream
// This ensures the controller exists before the delegate tries to use it
final childPath = newPath(_keyBuffer);
final Type streamType;
if (character == '"') {
streamType = String;
} else if (character == '{') {
streamType = Map;
} else if (character == '[') {
streamType = List;
} else if (character == 't' || character == 'f') {
streamType = bool;
} else if (character == 'n') {
streamType = Null;
} else {
streamType = num;
}
// Create the property stream (which creates the controller)
parserController.getPropertyStream(childPath, streamType);
// THEN: Create child delegate with a closure that checks if it's still active
PropertyDelegate? childDelegate;
childDelegate = createDelegate(
character,
propertyPath: childPath,
jsonStreamParserController: parserController,
onComplete: () {
// Only notify parent if this child is still the active one
if (_activeChildDelegate == childDelegate) {
onChildComplete();
}
},
);
_activeChildDelegate = childDelegate;
_activeChildDelegate!.addCharacter(character);
_state = MapParserState.readingValue;
return;
}
if (_firstCharacter && character == '{') {
_firstCharacter = false;
return;
}
if (_state == MapParserState.waitingForCommaOrEnd) {
// Skip whitespace
if (character == ' ' ||
character == '\t' ||
character == '\n' ||
character == '\r') {
return;
}
if (character == ',') {
_state = MapParserState.waitingForKey;
_keyBuffer = "";
return;
} else if (character == '}') {
_completeMap();
return;
}
}
if (_state == MapParserState.waitingForKey) {
// Skip whitespace
if (character == ' ' ||
character == '\t' ||
character == '\n' ||
character == '\r') {
return;
}
if (character == '"') {
_state = MapParserState.readingKey;
return;
}
if (character == "}") {
_completeMap();
return;
}
}
return;
}