com.android.ide.common.blame.MessageJsonSerializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdk-common Show documentation
Show all versions of sdk-common Show documentation
sdk-common library used by other Android tools libraries.
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package com.android.ide.common.blame;
import com.google.common.base.Optional;
import com.google.common.collect.BiMap;
import com.google.common.collect.EnumHashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.File;
import java.io.IOException;
/**
* Class to handle json serialization and deserialization of messages.
*
* Reads json objects of the form:
*
*
* {
* "kind":"ERROR",
* "text":"errorText",
* "original":"unparsed error text: Error in some intermediate file",
* "sources": [{
* "file":"/path/to/source.java",
* "position":{
* "startLine":1,
* "startColumn":2,
* "startOffset":3,
* "endLine":4,
* "endColumn":5,
* "endOffset":6
* }
* }]
* }
*
* All fields, other than text, may be omitted. They have the following defaults:
*
*
* Property Default Notes
* kind (ERROR, WARNING,
* INFO, UNKNOWN) UNKNOWN
* text Empty String Should not be omitted.
* file (Absolute) {} [unknown] See {@link SourceFileJsonTypeAdapter}
* position UNKNOWN
* startLine,
* startColumn,
* startOffset -1 [unknown] 0-based
* endLine,
* endColumn,
* endOffset startLine, startColumn,
* startOffset
*
*
* Notes
*
* - Offset need not be included, if needed by the consumer of the message it can
* be derived from the file, line and column.
* - If line is included and column is not the message will be considered to apply
* to the whole line.
* - A message can have multiple sources.
*
* It also can read legacy serialized objects of the form:
*
* {
* "kind":"ERROR",
* "text":"errorText",
* "sourcePath": "/path/to/source.java",
* "position":{
* "startLine":1,
* "startColumn":2,
* "startOffset":3,
* "endLine":4,
* "endColumn":5,
* "endOffset":6
* }
* }
*
* These serializers are implemented using the lower-level TypeAdapter gson API which gives much
* more control and allow changes to be made without breaking backward compatibility.
*/
public class MessageJsonSerializer extends TypeAdapter {
private static final String KIND = "kind";
private static final String TEXT = "text";
private static final String SOURCE_FILE_POSITIONS = "sources";
private static final String RAW_MESSAGE = "original";
private static final String TOOL_NAME = "tool";
private static final String LEGACY_SOURCE_PATH = "sourcePath";
private static final String LEGACY_POSITION = "position";
private static final BiMap KIND_STRING_ENUM_MAP;
static {
EnumHashBiMap map = EnumHashBiMap.create(Message.Kind.class);
map.put(Message.Kind.ERROR, "error");
map.put(Message.Kind.WARNING, "warning");
map.put(Message.Kind.INFO, "info");
map.put(Message.Kind.STATISTICS, "statistics");
map.put(Message.Kind.UNKNOWN, "unknown");
map.put(Message.Kind.SIMPLE, "simple");
KIND_STRING_ENUM_MAP = Maps.unmodifiableBiMap(map);
}
private final SourceFilePositionJsonSerializer mSourceFilePositionTypeAdapter;
private final SourcePositionJsonTypeAdapter mSourcePositionTypeAdapter;
public MessageJsonSerializer() {
mSourceFilePositionTypeAdapter = new SourceFilePositionJsonSerializer();
mSourcePositionTypeAdapter = mSourceFilePositionTypeAdapter.getSourcePositionTypeAdapter();
}
@Override
public void write(JsonWriter out, Message message) throws IOException {
out.beginObject()
.name(KIND).value(KIND_STRING_ENUM_MAP.get(message.getKind()))
.name(TEXT).value(message.getText())
.name(SOURCE_FILE_POSITIONS).beginArray();
for (SourceFilePosition position : message.getSourceFilePositions()) {
mSourceFilePositionTypeAdapter.write(out, position);
}
out.endArray();
if (!message.getRawMessage().equals(message.getText())) {
out.name(RAW_MESSAGE).value(message.getRawMessage());
}
if (message.getToolName().isPresent()) {
out.name(TOOL_NAME).value(message.getToolName().get());
}
out.endObject();
}
@Override
public Message read(JsonReader in) throws IOException {
in.beginObject();
Message.Kind kind = Message.Kind.UNKNOWN;
String text = "";
String rawMessage = null;
Optional toolName = Optional.absent();
ImmutableList.Builder positions =
new ImmutableList.Builder();
SourceFile legacyFile = SourceFile.UNKNOWN;
SourcePosition legacyPosition = SourcePosition.UNKNOWN;
while (in.hasNext()) {
String name = in.nextName();
if (name.equals(KIND)) {
//noinspection StringToUpperCaseOrToLowerCaseWithoutLocale
Message.Kind theKind = KIND_STRING_ENUM_MAP.inverse()
.get(in.nextString().toLowerCase());
kind = (theKind != null) ? theKind : Message.Kind.UNKNOWN;
} else if (name.equals(TEXT)) {
text = in.nextString();
} else if (name.equals(RAW_MESSAGE)) {
rawMessage = in.nextString();
} else if (name.equals(TOOL_NAME)) {
toolName = Optional.of(in.nextString());
} else if (name.equals(SOURCE_FILE_POSITIONS)) {
switch (in.peek()) {
case BEGIN_ARRAY:
in.beginArray();
while(in.hasNext()) {
positions.add(mSourceFilePositionTypeAdapter.read(in));
}
in.endArray();
break;
case BEGIN_OBJECT:
positions.add(mSourceFilePositionTypeAdapter.read(in));
break;
default:
in.skipValue();
break;
}
} else if (name.equals(LEGACY_SOURCE_PATH)) {
legacyFile = new SourceFile(new File(in.nextString()));
} else if (name.equals(LEGACY_POSITION)) {
legacyPosition = mSourcePositionTypeAdapter.read(in);
} else {
in.skipValue();
}
}
in.endObject();
if (legacyFile != SourceFile.UNKNOWN || legacyPosition != SourcePosition.UNKNOWN) {
positions.add(new SourceFilePosition(legacyFile, legacyPosition));
}
if (rawMessage == null) {
rawMessage = text;
}
ImmutableList sourceFilePositions = positions.build();
if (!sourceFilePositions.isEmpty()) {
return new Message(kind, text, rawMessage, toolName, sourceFilePositions);
} else {
return new Message(kind, text, rawMessage, toolName, ImmutableList.of(SourceFilePosition.UNKNOWN));
}
}
public static void registerTypeAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(SourceFile.class, new SourceFileJsonTypeAdapter());
builder.registerTypeAdapter(SourcePosition.class, new SourcePositionJsonTypeAdapter());
builder.registerTypeAdapter(SourceFilePosition.class,
new SourceFilePositionJsonSerializer());
builder.registerTypeAdapter(Message.class, new MessageJsonSerializer());
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy