blob: 4762fd45acfc16724be0f26cd1a6636b9bf71918 [file] [log] [blame]
// Copyright (c) 2017, the Rex project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Map;
import java.util.TreeMap;
/**
* Abstraction for hidden dex marker intended for the main dex file.
*/
public class Marker {
public enum Tool {D8, R8}
private static final String kPrefix = "~~";
private static final String kD8prefix = kPrefix + Tool.D8 + "{";
private static final String kR8prefix = kPrefix + Tool.R8 + "{";
private final TreeMap<String, Object> content;
private final Tool tool;
public Marker(Tool tool) {
this.tool = tool;
this.content = new TreeMap<>();
}
private Marker(Tool tool, JSONObject object) {
this.tool = tool;
content = new TreeMap<>();
// This loop is necessary to make the type checker to shut up.
for (Object e : object.entrySet()) {
Map.Entry entry = (Map.Entry) e;
content.put(String.valueOf(entry.getKey()), entry.getValue());
}
}
public Marker put(String key, int value) {
// value is converted to Long ensuring equals works with the parsed json string.
return internalPut(key, new Long(value));
}
public Marker put(String key, String value) {
return internalPut(key, value);
}
private Marker internalPut(String key, Object value) {
assert (key != null) && (value != null);
assert !content.containsKey(key);
content.put(key, value);
return this;
}
@Override
public String toString() {
// The JSONObject does not support a predictable sorted serialization of the object.
// Therefore, a TreeMap is used and iteration is over the keySet.
StringBuffer sb = new StringBuffer(kPrefix + tool);
boolean first = true;
sb.append('{');
for (String key : content.keySet()) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(JSONObject.toString(key, content.get(key)));
}
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Marker) {
Marker other = (Marker) obj;
return (tool == other.tool) && content.equals(other.content);
}
return false;
}
@Override
public int hashCode() {
return tool.hashCode() + 3 * content.hashCode();
}
// Try to parse str as a marker.
// Returns null if parsing fails.
public static Marker parse(String str) {
if (str.startsWith(kD8prefix)) {
return internalParse(Tool.D8, str.substring(kD8prefix.length() - 1));
}
if (str.startsWith(kR8prefix)) {
return internalParse(Tool.R8, str.substring(kR8prefix.length() - 1));
}
return null;
}
private static Marker internalParse(Tool tool, String str) {
try {
Object result = new JSONParser().parse(str);
if (result instanceof JSONObject) {
return new Marker(tool, (JSONObject) result);
}
} catch (ParseException e) {
// Fall through.
}
return null;
}
}