/*
* Copyright (C) 2010-2016 JPEXS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jpexs.helpers;
import com.jpexs.decompiler.flash.types.annotations.Internal;
import com.jpexs.decompiler.flash.types.annotations.SWFField;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author JPEXS
*/
public class ReflectionTools {
private static final Logger logger = Logger.getLogger(ReflectionTools.class.getName());
public static <E> Map<E, String> getConstNamesMap(Class<?> classWithConsts, Class<E> constsType, String matchRegexp) {
Map<E, String> ret = new HashMap<>();
Field[] fs = classWithConsts.getDeclaredFields();
Pattern p = Pattern.compile(matchRegexp);
for (Field f : fs) {
Matcher m = p.matcher(f.getName());
if (m.matches()) {
try {
String name = m.groupCount() > 0 ? m.group(1) : m.group(0);
@SuppressWarnings("unchecked")
E val = (E) f.get(constsType);
StringBuilder identName = new StringBuilder();
boolean cap = false;
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (c == '_') {
cap = true;
continue;
}
if (cap) {
identName.append(c);
cap = false;
} else {
identName.append(Character.toLowerCase(c));
}
}
ret.put(val, identName.toString());
} catch (IllegalArgumentException | IllegalAccessException ex) {
//ignore
}
}
}
return ret;
}
public static Object getValue(Object obj, Field field) throws IllegalArgumentException, IllegalAccessException {
Object value = field.get(obj);
return value;
}
public static Object getValue(Object obj, Field field, int index) throws IllegalArgumentException, IllegalAccessException {
if (index == -1) {
return getValue(obj, field);
}
if (getFieldSubSize(obj, field) <= index) {
return null;
}
Object value = field.get(obj);
if (List.class.isAssignableFrom(field.getType())) {
return ((List) value).get(index);
}
if (field.getType().isArray()) {
return Array.get(value, index);
}
return value;
}
public static boolean needsIndex(Field field) {
if (List.class.isAssignableFrom(field.getType())) {
return true;
} else if (field.getType().isArray()) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
public static int getFieldSubSize(Object obj, Field field) {
Object val;
try {
val = field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException ex) {
return 0;
}
if (List.class.isAssignableFrom(field.getType())) {
return ((List) val).size();
} else if (field.getType().isArray()) {
return Array.getLength(val);
}
return 0;
}
public static void setValue(Object obj, Field field, Object newValue) throws IllegalArgumentException, IllegalAccessException {
field.set(obj, newValue);
}
@SuppressWarnings("unchecked")
public static void setValue(Object obj, Field field, int index, Object newValue) throws IllegalArgumentException, IllegalAccessException {
if (index == -1) {
setValue(obj, field, newValue);
return;
}
Object value = field.get(obj);
if (needsIndex(field) && index >= getFieldSubSize(obj, field)) { //outofbounds, ignore
return;
}
if (List.class.isAssignableFrom(field.getType())) {
((List) value).set(index, newValue);
} else if (field.getType().isArray()) {
Array.set(value, index, newValue);
} else {
field.set(obj, newValue);
}
}
public static boolean canInstantiate(Class cls) {
if (cls.isInterface()) {
return false;
}
if (Modifier.isAbstract(cls.getModifiers())) {
return false;
}
return true;
}
public static boolean canInstantiateDefaultConstructor(Class<?> cls) {
if (!canInstantiate(cls)) {
return false;
}
try {
cls.getConstructor();
} catch (NoSuchMethodException | SecurityException ex) {
return false;
}
return true;
}
public static boolean canAddToField(Object object, Field field) {
if (List.class.isAssignableFrom(field.getType())) {
ParameterizedType listType = (ParameterizedType) field.getGenericType();
Class<?> parameterClass = (Class<?>) listType.getActualTypeArguments()[0];
return canInstantiate(parameterClass);
}
if (field.getType().isArray()) {
Object arrValue;
try {
arrValue = field.get(object);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
Class componentClass = arrValue.getClass().getComponentType();
if (componentClass.isPrimitive()) {
return true;
}
return canInstantiate(componentClass);
}
return false;
}
public static Object newInstanceOf(Class cls) throws InstantiationException, IllegalAccessException {
if (cls == Integer.class || cls == int.class) {
return 0;
} else if (cls == Float.class || cls == float.class) {
return 0.0f;
} else if (cls == Double.class || cls == double.class) {
return (double) 0;
} else if (cls == Long.class || cls == long.class) {
return 0L;
}
if (cls.isInterface()) {
return null;
}
if (Modifier.isAbstract(cls.getModifiers())) {
return null;
}
return cls.newInstance();
}
@SuppressWarnings("unchecked")
public static boolean addToList(Object object, Field field, int index, Class<?> cls) {
if (!List.class.isAssignableFrom(field.getType())) {
return false;
}
List list;
try {
list = (List) field.get(object);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
ParameterizedType listType = (ParameterizedType) field.getGenericType();
Class<?> parameterClass = (Class<?>) listType.getActualTypeArguments()[0];
try {
Object val = newInstanceOf(cls == null ? parameterClass : cls);
if (val == null) {
return false;
}
list.add(index, val);
} catch (InstantiationException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
return true;
}
public static Class<?> getFieldSubType(Object object, Field field) {
if (field.getType().isArray()) {
Object arrValue;
try {
arrValue = field.get(object);
return arrValue.getClass().getComponentType();
} catch (IllegalArgumentException | IllegalAccessException ex) {
return null;
}
}
if (List.class.isAssignableFrom(field.getType())) {
ParameterizedType listType = (ParameterizedType) field.getGenericType();
return (Class<?>) listType.getActualTypeArguments()[0];
}
return null;
}
public static boolean addToArray(Object object, Field field, int index, boolean notnull, Class<?> cls) {
if (!field.getType().isArray()) {
return false;
}
Object arrValue;
try {
arrValue = field.get(object);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
Class componentClass = arrValue.getClass().getComponentType();
Object val = null;
if (!componentClass.isPrimitive()) {
try {
val = newInstanceOf(cls == null ? componentClass : cls);
} catch (InstantiationException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
if (val == null) {
return false;
}
}
int originalSize = Array.getLength(arrValue);
Object copy = Array.newInstance(componentClass, originalSize + 1);
//Copy items before
for (int i = 0; i < index; i++) {
Array.set(copy, i, Array.get(arrValue, i));
}
if (val != null) {
Array.set(copy, index, val);
}
//Copy items after
for (int i = index; i < originalSize; i++) {
Array.set(copy, i + 1, Array.get(arrValue, i));
}
try {
field.set(object, copy);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
return true;
}
public static boolean addToField(Object object, Field field, int index, boolean notnull, Class<?> cls) {
if (List.class.isAssignableFrom(field.getType())) {
return addToList(object, field, index, cls);
}
if (field.getType().isArray()) {
return addToArray(object, field, index, notnull, cls);
}
return false;
}
public static boolean removeFromField(Object object, Field field, int index) {
if (List.class.isAssignableFrom(field.getType())) {
return removeFromList(object, field, index);
}
if (field.getType().isArray()) {
return removeFromArray(object, field, index);
}
return false;
}
public static boolean removeFromList(Object object, Field field, int index) {
List list;
try {
list = (List) field.get(object);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
if (index < 0 || index >= list.size()) {
return false;
}
list.remove(index);
return true;
}
public static boolean removeFromArray(Object object, Field field, int index) {
Object arrValue;
try {
arrValue = field.get(object);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
Class componentClass = arrValue.getClass().getComponentType();
int originalSize = Array.getLength(arrValue);
Object copy = Array.newInstance(componentClass, originalSize - 1);
int pos = 0;
//copy all before index
for (int i = 0; i < index; i++) {
Array.set(copy, pos, Array.get(arrValue, i));
pos++;
}
//copy all after index
for (int i = index + 1; i < originalSize; i++) {
Array.set(copy, pos, Array.get(arrValue, i));
pos++;
}
try {
field.set(object, copy);
} catch (IllegalArgumentException | IllegalAccessException ex) {
logger.log(Level.SEVERE, null, ex);
return false;
}
return true;
}
public static List<Field> getSwfFields(Class cls) {
List<Field> result = new ArrayList<>();
Field[] fields = cls.getFields();
for (Field f : fields) {
if (Modifier.isStatic(f.getModifiers())) {
continue;
}
Internal inter = f.getAnnotation(Internal.class);
if (inter != null) {
continue;
}
result.add(f);
}
fields = cls.getDeclaredFields();
// Add private fields marked with SWFField annotation
for (Field f : fields) {
if (Modifier.isStatic(f.getModifiers())) {
continue;
}
if (!Modifier.isPrivate(f.getModifiers())) {
continue;
}
SWFField swfField = f.getAnnotation(SWFField.class);
if (swfField != null) {
result.add(f);
}
}
return result;
}
}