blob: c9af221af08c1eacf74ab63dee4d6089bf419d5f [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 java.util.Map;
import java.util.TreeMap;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
/**
* Abstraction for hidden dex marker intended for the main dex file.
*/
public class Marker {
public static final String VERSION = "version";
public static final String MIN_API = "min-api";
public static final String SHA1 = "sha-1";
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 Tool getTool() {
return tool;
}
public boolean isD8() {
return tool == Tool.D8;
}
public boolean isR8() {
return tool == Tool.R8;
}
public String getVersion() {
return (String) content.get(VERSION);
}
public Marker setVersion(String version) {
internalPut(VERSION, version);
return this;
}
public Long getMinApi() {
return (Long) content.get(MIN_API);
}
public Marker setMinApi(long minApi) {
internalPut(MIN_API, minApi);
return this;
}
public String getSha1() {
return (String) content.get(SHA1);
}
public Marker setSha1(String sha1) {
internalPut(SHA1, sha1);
return this;
}
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;
}
}