/* * Minecraft Forge * Copyright (c) 2016-2018. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml.common.discovery.json; import java.util.List; import java.util.Map; import java.util.function.Function; import org.objectweb.asm.Type; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.loading.moddiscovery.ModAnnotation.EnumHolder; //Package private, modders shouldn't access this. Do it through ASMDataTable. class ASMInfo { String name; String[] interfaces; List annotations; private Map byID; public Annotation getSubAnnotation(int id) { if (byID == null) { byID = Maps.newHashMap(); annotations.forEach(a -> { if (a.id != null) byID.put(a.id, a); }); } return byID.get(id); } @Override public String toString() { return MoreObjects.toStringHelper("") .add("name", name) .add("itf", interfaces) .add("ann", annotations) .toString(); } public enum TargetType { CLASS, FIELD, METHOD, SUBTYPE }; public enum ValueType { BOOL(Boolean::valueOf, v -> {boolean[] ret = new boolean[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Boolean.parseBoolean(v[x]); return ret; }), BYTE(Byte::valueOf, v -> {byte[] ret = new byte[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Byte.parseByte(v[x]); return ret; }), CHAR(x -> x.charAt(0), v -> {char[] ret = new char[v.length]; for (int x = 0; x < v.length; x++) ret[x] = v[x].charAt(0); return ret; }), SHORT(Short::valueOf, v -> {short[] ret = new short[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Short.parseShort(v[x]); return ret; }), INT(Integer::valueOf, v -> {int[] ret = new int[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Integer.parseInt(v[x]); return ret; }), LONG(Long::valueOf, v -> {long[] ret = new long[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Long.parseLong(v[x]); return ret; }), FLOAT(Float::valueOf, v -> {float[] ret = new float[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Float.parseFloat(v[x]); return ret; }), DOUBLE(Double::valueOf, v -> {double[] ret = new double[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Double.parseDouble(v[x]); return ret; }), STRING(x -> x, x -> x), CLASS(Type::getType, v -> {Type[] ret = new Type[v.length]; for (int x = 0; x < v.length; x++) ret[x] = Type.getType(v[x]); return ret; }), ENUM(ValueType::getEnumHolder, v -> {List ret = Lists.newArrayList(); for (int x = 0; x < v.length; x++) ret.add(ValueType.getEnumHolder(v[x])); return ret; }), ANNOTATION(null, null), NULL(x -> null, x -> null); public final Function single; public final Function array; private ValueType(Function single, Function array) { this.single = single; this.array = array; } private static EnumHolder getEnumHolder(String value) { int idx = value.lastIndexOf('/'); if (idx <= 1) throw new IllegalArgumentException("Can not create a EnumHolder for value: " + value); String field = value.substring(idx + 1); value = value.substring(0, idx); idx = value.indexOf(';'); //Legacy internal name. value = value.substring(1, idx); return new EnumHolder(value, field); } }; static class Annotation { TargetType type; String name; String target; Integer id; ValueHolder value; Map values; private Map _values; public Map getValues(ASMInfo pool) { if (_values == null) { _values = Maps.newHashMap(); if (values != null) values.forEach((k, v) -> _values.put(k, v.get(pool))); else _values.put("value", value == null ? null : value.get(pool)); } return _values; } @Override public String toString() { return MoreObjects.toStringHelper("") .add("type", type) .add("name", name) .add("target", target) .add("id", id) .add("value", value) .toString(); } } static class ValueHolder { ValueType type; String value; String[] values; private Object _value; private ValueType getType() { return type == null ? ValueType.STRING : type; } public Object get(ASMInfo pool) { if (_value == null) { if (values != null) { if (type == ValueType.ANNOTATION) { List> list = Lists.newArrayList(); _value = list; for (String s : values) { Annotation sub = pool.getSubAnnotation(Integer.parseInt(s)); if (sub == null) FMLLog.log.error("Invalid Sub-Annotation in Annotation JSON: " + s); else list.add(sub.getValues(pool)); } } else _value = getType().array.apply(values); } else { if (type == ValueType.ANNOTATION) { Annotation sub = pool.getSubAnnotation(Integer.parseInt(value)); if (sub == null) FMLLog.log.error("Invalid Sub-Annotation in Annotation JSON: " + value); else _value = sub.getValues(pool); } else _value = getType().single.apply(value); } } return _value; } } }