Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a TypeAdapter to deserialize Multipiece from json and cleanup #66

Merged
merged 3 commits into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/main/java/analysis/GoblintAnalysis.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,7 @@ private CompletableFuture<Collection<AnalysisResult>> getComposedAnalysisResults
*/

private Collection<AnalysisResult> convertMessagesFromJson(List<GoblintMessagesResult> response) {
return gobpieConfiguration.explodeGroupWarnings()
? response.stream().map(GoblintMessagesResult::convertExplode).flatMap(List::stream).toList()
: response.stream().map(GoblintMessagesResult::convertNonExplode).flatMap(List::stream).toList();
return response.stream().map(msg -> msg.convert(gobpieConfiguration.explodeGroupWarnings())).flatMap(List::stream).toList();
}

private Collection<AnalysisResult> convertFunctionsFromJson(List<GoblintFunctionsResult> response) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/api/json/GoblintMessageJsonHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public GsonBuilder getDefaultGsonBuilder() {
.registerTypeAdapterFactory(new TupleTypeAdapters.TwoTypeAdapterFactory())
.registerTypeAdapterFactory(new EnumTypeAdapter.Factory())
.registerTypeAdapterFactory(new GoblintMessageTypeAdapter.Factory(this))
.registerTypeAdapter(GoblintMessagesResult.Tag.class, new GoblintTagInterfaceAdapter());
.registerTypeAdapter(GoblintMessagesResult.Tag.class, new GoblintTagInterfaceAdapter())
.registerTypeAdapter(GoblintMessagesResult.MultiPiece.class, new GoblintMultiPieceInterfaceAdapter());
}

}
29 changes: 29 additions & 0 deletions src/main/java/api/json/GoblintMultiPieceInterfaceAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package api.json;

import api.messages.GoblintMessagesResult;
import com.google.gson.*;

import java.lang.reflect.Type;

/**
* The Class GoblintMultiPieceInterfaceAdapter.
* <p>
* Implements the JsonDeserializer to deserialize json to GoblintResult objects.
* In particular to differentiate between the Group and Piece (Single) classes.
*
* @author Karoliine Holter
* @since 0.0.4
*/


public class GoblintMultiPieceInterfaceAdapter implements JsonDeserializer<Object> {
@Override
public Object deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("group_text"))
return jsonDeserializationContext.deserialize(jsonObject, GoblintMessagesResult.Group.class);
if (jsonObject.has("text"))
return jsonDeserializationContext.deserialize(jsonObject, GoblintMessagesResult.Piece.class);
return null;
}
}
166 changes: 79 additions & 87 deletions src/main/java/api/messages/GoblintMessagesResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class GoblintMessagesResult {
private final String type = getClass().getName();
private final List<Tag> tags = new ArrayList<>();
private String severity;
private Multipiece multipiece;
private MultiPiece multipiece;

public interface Tag {
String toString();
Expand All @@ -51,73 +51,96 @@ public String toString() {
}
}

static class Multipiece {
private GoblintLocation loc;
public interface MultiPiece {
List<AnalysisResult> convert(List<Tag> tags, String severity, boolean explode);
}

public static class Piece implements MultiPiece {
private String text;
private String group_text;
private final List<Piece> pieces = new ArrayList<>();
private GoblintLocation loc;

static class Piece {
private String text;
private GoblintLocation loc;
/**
* Converts the Single (Piece type of) Goblint messages from the
* GoblintMessagesResult type to AnalysisResult that are needed for MagPieBridge.
*
* @param tags the tags of the warning given by Goblint
* @param severity the severity of the warning given by Goblint
* @param explode is not used here and is only used for group warnings
* @return A collection of AnalysisResult objects.
*/
public List<AnalysisResult> convert(List<Tag> tags, String severity, boolean explode) {
GoblintPosition pos = getLocation(loc);
String msg = joinTags(tags) + " " + text;
GoblintMessagesAnalysisResult result = new GoblintMessagesAnalysisResult(pos, msg, severity);
return new ArrayList<>(List.of(result));
karoliineh marked this conversation as resolved.
Show resolved Hide resolved
}
}

public String getType() {
return type;
}

public List<AnalysisResult> convertSingle() {
GoblintMessagesAnalysisResult result = createGoblintAnalysisResult();
return new ArrayList<>(List.of(result));
}
public static class Group implements MultiPiece {
private String group_text;
private GoblintLocation group_loc;
private final List<Piece> pieces = new ArrayList<>();

public List<AnalysisResult> convertGroupToSeparateWarnings() {
List<GoblintMessagesAnalysisResult> resultsWithoutRelated =
multipiece.pieces.stream().map(piece -> createGoblintAnalysisResult(piece, true)).toList();
// Add related warnings to all the pieces in the group
List<GoblintMessagesAnalysisResult> resultsWithRelated = new ArrayList<>();
for (GoblintMessagesAnalysisResult result : resultsWithoutRelated) {
resultsWithRelated.add(
new GoblintMessagesAnalysisResult(
result.position(),
result.group_text() + "\n" + result.text(),
result.severityStr(),
resultsWithoutRelated.stream()
.filter(res -> res != result)
.map(res -> Pair.make(res.position(), res.text()))
.toList()));
/**
* Converts the Group Goblint messages from the
* GoblintMessagesResult type to AnalysisResult that are needed for MagPieBridge.
*
* @param tags the tags of the warning given by Goblint
* @param severity the severity of the warning given by Goblint
* @param explode the group warnings are exploded to have one IDE warning for each piece in the group if true,
* if false, only one warning per one Goblint warning is shown in the IDE
* @return A collection of AnalysisResult objects.
*/
public List<AnalysisResult> convert(List<Tag> tags, String severity, boolean explode) {
return explode
? convertGroupExplode(tags, severity)
: convertGroup(tags, severity);
}
return new ArrayList<>(resultsWithRelated);
}

public List<AnalysisResult> convertGroup() {
List<Pair<Position, String>> relatedFromPieces =
multipiece.pieces.stream()
.map(piece -> createGoblintAnalysisResult(piece, false))
.map(result -> Pair.make(result.position(), result.text()))
.toList();
GoblintMessagesAnalysisResult result = createGoblintAnalysisResult(multipiece, relatedFromPieces);
return new ArrayList<>(List.of(result));
}
public List<AnalysisResult> convertGroupExplode(List<Tag> tags, String severity) {
String groupText = joinTags(tags) + " Group: " + group_text;
List<GoblintMessagesAnalysisResult> resultsWithoutRelated =
pieces.stream().map(piece -> new GoblintMessagesAnalysisResult(getLocation(piece.loc), groupText, piece.text, severity)).toList();
// Add related warnings to all the pieces in the group
List<GoblintMessagesAnalysisResult> resultsWithRelated = new ArrayList<>();
for (GoblintMessagesAnalysisResult result : resultsWithoutRelated) {
resultsWithRelated.add(
new GoblintMessagesAnalysisResult(
result.position(),
result.group_text() + "\n" + result.text(),
result.severityStr(),
resultsWithoutRelated.stream()
.filter(res -> res != result)
.map(res -> Pair.make(res.position(), res.text()))
.toList()));
}
return new ArrayList<>(resultsWithRelated);
}

public List<AnalysisResult> convertExplode() {
if (multipiece.group_text == null) {
return convertSingle();
} else {
return convertGroupToSeparateWarnings();
public List<AnalysisResult> convertGroup(List<Tag> tags, String severity) {
// Convert all pieces to pairs of position and text to be used as related warnings
List<Pair<Position, String>> relatedFromPieces =
pieces.stream().map(piece -> Pair.make((Position) getLocation(piece.loc), piece.text)).toList();
// Use the group location for the warning if defined, or a random one from one of the pieces otherwise
GoblintPosition pos =
group_loc != null
? group_loc.toPosition()
: pieces.stream()
.filter(piece -> piece.loc != null)
.findFirst()
.map(piece -> piece.loc.toPosition())
.orElse(getLocation(group_loc));
GoblintMessagesAnalysisResult result =
new GoblintMessagesAnalysisResult(pos, joinTags(tags) + " " + group_text, severity, relatedFromPieces);
return new ArrayList<>(List.of(result));
}
}

public List<AnalysisResult> convertNonExplode() {
if (multipiece.group_text == null) {
return convertSingle();
} else {
return convertGroup();
}
private static String joinTags(List<Tag> tags) {
return tags.stream().map(Tag::toString).collect(Collectors.joining(""));
}

public GoblintPosition getLocation(GoblintLocation loc) {
private static GoblintPosition getLocation(GoblintLocation loc) {
try {
return loc == null
? new GoblintPosition(1, 1, 1, new File("").toURI().toURL())
Expand All @@ -127,39 +150,8 @@ public GoblintPosition getLocation(GoblintLocation loc) {
}
}

public GoblintPosition getRandomLocation(Multipiece multipiece) {
for (GoblintMessagesResult.Multipiece.Piece piece : multipiece.pieces) {
if (piece.loc != null) return getLocation(piece.loc);
}
return getLocation(multipiece.loc);
public List<AnalysisResult> convert(boolean explode) {
return multipiece.convert(tags, severity, explode);
}

public GoblintMessagesAnalysisResult createGoblintAnalysisResult() {
GoblintPosition pos = getLocation(multipiece.loc);
String msg = tags.stream().map(Tag::toString).collect(Collectors.joining("")) + " " + multipiece.text;
return new GoblintMessagesAnalysisResult(pos, msg, severity);

}

public GoblintMessagesAnalysisResult createGoblintAnalysisResult(Multipiece.Piece piece, boolean addGroupText) {
GoblintPosition pos = getLocation(piece.loc);
return new GoblintMessagesAnalysisResult(
pos,
addGroupText
? tags.stream().map(Tag::toString).collect(Collectors.joining("")) + " Group: " + multipiece.group_text
: "",
piece.text,
severity);
}

public GoblintMessagesAnalysisResult createGoblintAnalysisResult(Multipiece multipiece, List<Pair<Position, String>> related) {
GoblintPosition pos = multipiece.loc != null
? getLocation(multipiece.loc)
: getRandomLocation(multipiece);
return new GoblintMessagesAnalysisResult(
pos,
tags.stream().map(Tag::toString).collect(Collectors.joining("")) + " " + this.multipiece.group_text,
severity,
related);
}
}