Java tutorial
/******************************************************************************* * Copyright (c) 2016 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Pieter Penninckx - initial implementation * Bruno Medeiros - refactor, modifications *******************************************************************************/ package com.github.rustdt.tooling; import java.io.IOException; import java.io.Reader; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.stream.JsonReader; import melnorme.lang.tooling.common.ToolSourceMessage; import melnorme.lang.tooling.toolchain.ops.ToolMessageData; import melnorme.lang.tooling.toolchain.ops.ToolMessageParser; import melnorme.lang.utils.gson.GsonHelper; import melnorme.lang.utils.gson.JsonParserX; import melnorme.lang.utils.gson.JsonParserX.JsonSyntaxExceptionX; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.core.CommonException; import melnorme.utilbox.misc.StringUtil; import melnorme.utilbox.status.Severity; public class RustJsonMessageParser { protected final GsonHelper helper = new GsonHelper(); protected final ToolMessageParser messageParseHelper = new ToolMessageParser(); public ToolSourceMessage parseSourceMessage(ToolMessageData msgdata) throws CommonException { if (msgdata.pathString.startsWith("<") && msgdata.pathString.endsWith(">")) { // That path doesn't exist, it's a macro expansion. // Furthermore, on Windows it's not even a valid Path, so createMessage would fail // So we set it to empty: msgdata.pathString = ""; } return messageParseHelper.createMessage(msgdata); } public ArrayList2<RustMainMessage> parseStructuredMessages(Reader reader) throws CommonException { // Only Json from now on. // JsonReader creates an internal buffer of its reader. // There is no way we can know how much it has parsed. JsonReader jsonReader = JsonParserX.newReader(reader, true); return parseStructuredMessages(jsonReader); } public ArrayList2<RustMainMessage> parseStructuredMessages(JsonReader jsonReader) throws CommonException { ArrayList2<RustMainMessage> rustMessages = new ArrayList2<>(); while (true) { try { RustMainMessage rustMessage = parseStructuredMessage(jsonReader); if (rustMessage == null) { jsonReader.close(); break; } rustMessages.add(rustMessage); } catch (IOException ioe) { throw new CommonException("Unexpected IO Exception: ", ioe); } } return rustMessages; } protected RustMainMessage parseStructuredMessage(JsonReader jsonReader) throws CommonException, IOException { if (JsonParserX.isEndOfInput(jsonReader)) { return null; } try { JsonElement element = new JsonParserX().parse(jsonReader); return parseTopLevelRustMessage(element); } catch (JsonSyntaxExceptionX e) { throw new CommonException("JSON syntax error in output message: ", e); } } /** Add all compiler errors (if any) to the list of build messages in the order in which they appear. * * @param messsagesElement The JsonElement to transform into messages. * @throws CommonException when messageLine is not in the expected format. */ protected RustMainMessage parseTopLevelRustMessage(JsonElement messsagesElement) throws CommonException { JsonObject messages = helper.asObject(messsagesElement); String messageText = helper.getString(messages, "message"); String severityLevel = helper.getOptionalString(messages, "level"); if (severityLevel == null) { severityLevel = Severity.WARNING.getLabel(); } String errorCode = getErrorCode(messages); ArrayList2<String> notes = parseNotes(messages.get("children")); JsonArray spansArray = helper.getArray(messages, "spans"); ArrayList2<RustMessage> spans = new ArrayList2<>(); for (JsonElement spanElement : spansArray) { JsonObject spanObject = helper.asObject(spanElement); RustMessage message = parseToolMessageFromSpanObject(spanObject, severityLevel, false, ""); spans.add(message); } ToolMessageData messageData = toolMessageWithoutSpan(messageText, severityLevel); ToolSourceMessage sourceMessage = parseSourceMessage(messageData); return new RustMainMessage(sourceMessage, errorCode, notes, spans); } protected RustMessage parseToolMessageFromSpanObject(JsonObject spanObject, String severityLevel, boolean overridePrimaryToTrue, String macroDeclarationName) throws CommonException { ToolMessageData subMessage = toolMessageWithRange(spanObject); boolean isPrimary = true; if (!overridePrimaryToTrue) { isPrimary = helper.getBooleanOr(spanObject, "is_primary", false); } subMessage.sourceBeforeMessageText = ""; // This does not seem to be used (?). TODO if (isPrimary) { subMessage.messageTypeString = severityLevel; } else { subMessage.messageTypeString = Severity.INFO.getLabel(); } String label = helper.getOptionalString(spanObject, "label"); subMessage.messageText = StringUtil.nullAsEmpty(label); RustMessage expansionMsg = null; RustMessage defSiteMsg = null; JsonObject expansion = helper.getOptionalObject(spanObject, "expansion"); if (expansion != null) { String macroDeclarationNameInExpansion = helper.getOptionalString(expansion, "macro_decl_name"); if (macroDeclarationNameInExpansion != null) { macroDeclarationName = macroDeclarationNameInExpansion; } JsonObject expansionSpan = helper.getObject(expansion, "span"); expansionMsg = parseToolMessageFromSpanObject(expansionSpan, severityLevel, isPrimary, macroDeclarationName); JsonObject expansionDefSiteSpan = helper.getOptionalObject(expansion, "def_site_span"); if (expansionDefSiteSpan != null) { defSiteMsg = parseToolMessageFromSpanObject(expansionDefSiteSpan, severityLevel, isPrimary, ""); } } return new RustSubMessage(parseSourceMessage(subMessage), isPrimary, expansionMsg, defSiteMsg); } protected ToolMessageData toolMessageWithRange(JsonObject span) throws CommonException { ToolMessageData subMessage = new ToolMessageData(); subMessage.lineString = Integer.toString(helper.getIntegerOr(span, "line_start", -1)); subMessage.endLineString = Integer.toString(helper.getIntegerOr(span, "line_end", -1)); subMessage.columnString = Integer.toString(helper.getIntegerOr(span, "column_start", -1)); subMessage.endColumnString = Integer.toString(helper.getIntegerOr(span, "column_end", -1)); subMessage.pathString = helper.getStringOr(span, "file_name", ""); return subMessage; } protected ToolMessageData toolMessageWithoutSpan(String message, String level) { ToolMessageData subMessage = new ToolMessageData(); subMessage.lineString = "1"; subMessage.endLineString = "1"; subMessage.columnString = "1"; subMessage.endColumnString = "1"; subMessage.pathString = ""; subMessage.messageText = message; subMessage.sourceBeforeMessageText = ""; subMessage.messageTypeString = level; return subMessage; } protected ArrayList2<String> parseNotes(JsonElement children) throws CommonException { ArrayList2<String> notes = new ArrayList2<>(); if (children == null || !children.isJsonArray()) { return notes; } JsonArray childrenArray = children.getAsJsonArray(); if (childrenArray.size() == 0) { return notes; } for (JsonElement child : childrenArray) { if (child.isJsonObject()) { String childMessageString = helper.getOptionalString(child.getAsJsonObject(), "message"); if (childMessageString != null && !childMessageString.isEmpty()) { notes.add(childMessageString); } } } return notes; } protected String getErrorCode(JsonObject messageLine) throws CommonException { JsonObject codeObject = helper.getOptionalObject(messageLine, "code"); if (codeObject != null) { String errorCode = helper.getOptionalString(codeObject, "code"); if (errorCode != null) { return errorCode; } } return ""; } }