/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Moderne Source Available License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://docs.moderne.io/licensing/moderne-source-available-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {JsonVisitor} from "./visitor";
import {asRef, RpcCodecs, RpcReceiveQueue, RpcSendQueue} from "../rpc";
import {Json} from "./tree";
import {updateIfChanged} from "../util";
class JsonSender extends JsonVisitor {
protected async preVisit(j: Json, q: RpcSendQueue): Promise {
await q.getAndSend(j, j2 => j2.id);
await q.getAndSend(j, j2 => asRef(j2.prefix),
async space => await this.visitSpace(space, q));
await q.getAndSend(j, j2 => j2.markers);
return j;
}
protected async visitDocument(document: Json.Document, q: RpcSendQueue): Promise {
await q.getAndSend(document, d => d.sourcePath);
await q.getAndSend(document, d => d.charsetName);
await q.getAndSend(document, d => d.charsetBomMarked);
await q.getAndSend(document, d => d.checksum);
await q.getAndSend(document, d => d.fileAttributes);
await q.getAndSend(document, d => d.value,
async j => await this.visit(j, q));
await q.getAndSend(document, d => asRef(d.eof),
async space => await this.visitSpace(space, q));
return document;
}
protected async visitArray(array: Json.Array, q: RpcSendQueue): Promise {
await q.getAndSendList(array, a => a.values, j => j.element.id,
async j => await this.visitRightPadded(j, q));
return array;
}
protected async visitEmpty(empty: Json.Empty): Promise {
return empty;
}
protected async visitIdentifier(identifier: Json.Identifier, q: RpcSendQueue): Promise {
await q.getAndSend(identifier, id => id.name);
return identifier;
}
protected async visitLiteral(literal: Json.Literal, q: RpcSendQueue): Promise {
await q.getAndSend(literal, lit => lit.source);
await q.getAndSend(literal, lit => lit.value);
return literal;
}
protected async visitMember(member: Json.Member, q: RpcSendQueue): Promise {
await q.getAndSend(member, m => m.key, async j => await this.visitRightPadded(j, q));
await q.getAndSend(member, m => m.value, async j => await this.visit(j, q));
return member;
}
protected async visitObject(obj: Json.Object, q: RpcSendQueue): Promise {
await q.getAndSendList(obj, o => o.members, j => j.element.id,
async j => await this.visitRightPadded(j, q));
return obj;
}
public async visitSpace(space: Json.Space, q: RpcSendQueue): Promise {
await q.getAndSendList(space, s => s.comments, c => c.text + c.suffix, async c => {
await q.getAndSend(c, c2 => c2.multiline);
await q.getAndSend(c, c2 => c2.text);
await q.getAndSend(c, c2 => c2.suffix);
await q.getAndSend(c, c2 => c2.markers);
});
await q.getAndSend(space, s => s.whitespace);
return space;
}
public async visitRightPadded(right: Json.RightPadded, q: RpcSendQueue): Promise | undefined> {
await q.getAndSend(right, r => r.element, j => this.visit(j, q));
await q.getAndSend(right, r => asRef(r.after), async space => await this.visitSpace(space, q));
await q.getAndSend(right, r => r.markers);
return right;
}
}
// noinspection ES6MissingAwait
class JsonReceiver extends JsonVisitor {
protected async preVisit(j: Json, q: RpcReceiveQueue): Promise {
return updateIfChanged(j, {
id: await q.receive(j.id),
prefix: await q.receive(j.prefix, async space => await this.visitSpace(space, q)),
markers: await q.receive(j.markers),
});
}
protected async visitDocument(document: Json.Document, q: RpcReceiveQueue): Promise {
return updateIfChanged(document, {
sourcePath: await q.receive(document.sourcePath),
charsetName: await q.receive(document.charsetName),
charsetBomMarked: await q.receive(document.charsetBomMarked),
checksum: await q.receive(document.checksum),
fileAttributes: await q.receive(document.fileAttributes),
value: await q.receive(document.value, async j => await this.visit(j, q)!),
eof: await q.receive(document.eof, async space => await this.visitSpace(space, q)),
});
}
protected async visitArray(array: Json.Array, q: RpcReceiveQueue): Promise {
return updateIfChanged(array, {
values: await q.receiveListDefined(array.values,
async j => await this.visitRightPadded(j, q)!)!,
});
}
protected async visitEmpty(empty: Json.Empty): Promise {
return empty;
}
protected async visitIdentifier(identifier: Json.Identifier, q: RpcReceiveQueue): Promise {
return updateIfChanged(identifier, {
name: await q.receive(identifier.name),
});
}
protected async visitLiteral(literal: Json.Literal, q: RpcReceiveQueue): Promise {
return updateIfChanged(literal, {
source: await q.receive(literal.source),
value: await q.receive(literal.value),
});
}
protected async visitMember(member: Json.Member, q: RpcReceiveQueue): Promise {
return updateIfChanged(member, {
key: await q.receive(member.key,
async j => await this.visitRightPadded(j, q)!)!,
value: await q.receive(member.value,
async j => await this.visit(j, q)!),
});
}
protected async visitObject(obj: Json.Object, q: RpcReceiveQueue): Promise {
return updateIfChanged(obj, {
members: await q.receiveListDefined(obj.members,
async j => await this.visitRightPadded(j, q)),
});
}
public async visitSpace(space: Json.Space, q: RpcReceiveQueue): Promise {
return updateIfChanged(space, {
comments: await q.receiveListDefined(space.comments, async c => {
return updateIfChanged(c, {
multiline: await q.receive(c.multiline),
text: await q.receive(c.text),
suffix: await q.receive(c.suffix),
markers: await q.receive(c.markers),
});
}),
whitespace: await q.receive(space.whitespace),
});
}
public async visitRightPadded(right: Json.RightPadded, p: RpcReceiveQueue): Promise | undefined> {
if (!right) {
throw new Error("TreeDataReceiveQueue should have instantiated an empty padding")
}
return updateIfChanged(right, {
element: await p.receive(right.element, async j => await this.visit(j, p)!) as T,
after: await p.receive(right.after, async space => await this.visitSpace(space, p)),
markers: await p.receiveMarkers(right.markers),
});
}
}
const receiver = new JsonReceiver();
const sender = new JsonSender();
// Register codec for all Java AST node types
for (const kind of Object.values(Json.Kind)) {
if (kind === Json.Kind.Space) {
RpcCodecs.registerCodec(kind, {
async rpcReceive(before: Json.Space, q: RpcReceiveQueue): Promise {
return (await receiver.visitSpace(before, q))!;
},
async rpcSend(after: Json.Space, q: RpcSendQueue): Promise {
await sender.visitSpace(after, q);
}
}, Json.Kind.Document);
} else if (kind === Json.Kind.RightPadded) {
RpcCodecs.registerCodec(kind, {
async rpcReceive(before: Json.RightPadded, q: RpcReceiveQueue): Promise> {
return (await receiver.visitRightPadded(before, q))!;
},
async rpcSend(after: Json.RightPadded, q: RpcSendQueue): Promise {
await sender.visitRightPadded(after, q);
}
}, Json.Kind.Document);
} else {
RpcCodecs.registerCodec(kind as string, {
async rpcReceive(before: Json, q: RpcReceiveQueue): Promise {
return (await receiver.visit(before, q))!;
},
async rpcSend(after: Json, q: RpcSendQueue): Promise {
await sender.visit(after, q);
}
}, Json.Kind.Document);
}
}