/******************************************************************************* * Copyright (c) 2015 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public * License v3.0 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.Map; import com.google.common.collect.Maps; public class Type implements Serializable { private static final long serialVersionUID = 7862257669347104063L; public enum Primitive { Byte('B'), Character('C'), Short('S'), Integer('I'), Long('J'), Float('F'), Double('D'), Boolean('Z'); private static final Map<Character, Primitive> m_lookup; static { m_lookup = Maps.newTreeMap(); for(Primitive val : values()) m_lookup.put(val.getCode(), val); } public static Primitive get(char code) { return m_lookup.get(code); } private char m_code; private Primitive(char code) { m_code = code; } public char getCode() { return m_code; } } public static String parseFirst(String in) { if(in == null || in.length() <= 0) throw new IllegalArgumentException( "No type to parse, input is empty!"); // read one type from the input char c = in.charAt(0); // first check for void if(c == 'V') return "V"; // then check for primitives Primitive primitive = Primitive.get(c); if(primitive != null) return in.substring(0, 1); // then check for classes if(c == 'L') return readClass(in); // then check for templates if(c == 'T') return readClass(in); // then check for arrays int dim = countArrayDimension(in); if(dim > 0) { String arrayType = Type.parseFirst(in.substring(dim)); return in.substring(0, dim + arrayType.length()); } throw new IllegalArgumentException("don't know how to parse: " + in); } protected String m_name; public Type(String name) { // don't deal with generics // this is just for raw jvm types if(name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) throw new IllegalArgumentException( "don't use with generic types or templates: " + name); m_name = name; } public Type(Type other) { m_name = other.m_name; } public Type(ClassEntry classEntry) { m_name = "L" + classEntry.getClassName() + ";"; } public Type(Type other, ClassNameReplacer replacer) { m_name = other.m_name; if(other.isClass()) { String replacedName = replacer.replace(other.getClassEntry().getClassName()); if(replacedName != null) m_name = "L" + replacedName + ";"; }else if(other.isArray() && other.hasClass()) { String replacedName = replacer.replace(other.getClassEntry().getClassName()); if(replacedName != null) m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; } } @Override public String toString() { return m_name; } public boolean isVoid() { return m_name.length() == 1 && m_name.charAt(0) == 'V'; } public boolean isPrimitive() { return m_name.length() == 1 && Primitive.get(m_name.charAt(0)) != null; } public Primitive getPrimitive() { if(!isPrimitive()) throw new IllegalStateException("not a primitive"); return Primitive.get(m_name.charAt(0)); } public boolean isClass() { return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; } public ClassEntry getClassEntry() { if(isClass()) { String name = m_name.substring(1, m_name.length() - 1); int pos = name.indexOf('<'); if(pos >= 0) // remove the parameters from the class name name = name.substring(0, pos); return new ClassEntry(name); }else if(isArray() && getArrayType().isClass()) return getArrayType().getClassEntry(); else throw new IllegalStateException("type doesn't have a class"); } public boolean isArray() { return m_name.charAt(0) == '['; } public int getArrayDimension() { if(!isArray()) throw new IllegalStateException("not an array"); return countArrayDimension(m_name); } public Type getArrayType() { if(!isArray()) throw new IllegalStateException("not an array"); return new Type(m_name.substring(getArrayDimension(), m_name.length())); } private static String getArrayPrefix(int dimension) { StringBuilder buf = new StringBuilder(); for(int i = 0; i < dimension; i++) buf.append("["); return buf.toString(); } public boolean hasClass() { return isClass() || isArray() && getArrayType().hasClass(); } @Override public boolean equals(Object other) { if(other instanceof Type) return equals((Type)other); return false; } public boolean equals(Type other) { return m_name.equals(other.m_name); } @Override public int hashCode() { return m_name.hashCode(); } private static int countArrayDimension(String in) { int i = 0; for(; i < in.length() && in.charAt(i) == '['; i++) ; return i; } private static String readClass(String in) { // read all the characters in the buffer until we hit a ';' // include the parameters too StringBuilder buf = new StringBuilder(); int depth = 0; for(int i = 0; i < in.length(); i++) { char c = in.charAt(i); buf.append(c); if(c == '<') depth++; else if(c == '>') depth--; else if(depth == 0 && c == ';') return buf.toString(); } return null; } }