/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.internal.managed.reflection;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.jcwhatever.nucleus.mixins.IWrapper;
import com.jcwhatever.nucleus.utils.CollectionUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Stores globally available data about a class type.
*/
class CachedReflectedType implements IWrapper<Class<?>> {
private final Class<?> _clazz;
private final Multimap<Integer, Constructor<?>> _constructors =
MultimapBuilder.hashKeys(3).arrayListValues().build();
private final Multimap<Class<?>, ReflectedField> _fieldsByType =
MultimapBuilder.hashKeys(10).arrayListValues().build();
private final Map<String, ReflectedField> _fieldsByName = new HashMap<>(10);
private final Multimap<String, Method> _methods =
MultimapBuilder.hashKeys(10).arrayListValues().build();
/**
* Constructor.
*
* @param clazz
*/
CachedReflectedType(Class<?> clazz) {
_clazz = clazz;
loadFields();
loadMethods();
loadConstructors();
}
/**
* Get all constructors with the specified number of parameters.
*
* @param count The parameter count.
*/
public <T extends Collection<Constructor<?>>> T constructorsByCount(int count, T output) {
synchronized (_constructors) {
output.addAll(_constructors.get(count));
return output;
}
}
/**
* Get a field by name.
*
* @param name The name of the field.
*
* @return the {@link ReflectedField} or null if not found.
*/
@Nullable
public ReflectedField fieldByName(String name) {
synchronized (_fieldsByName) {
return _fieldsByName.get(name);
}
}
/**
* Get all fields of a specified value type.
*
* @param type The value type class.
*/
public List<ReflectedField> fieldsByType(Class<?> type) {
synchronized (_fieldsByType) {
return CollectionUtils.unmodifiableList(_fieldsByType.get(type));
}
}
/**
* Get all methods with the specified name.
*
* @param name The method name.
*/
public <T extends Collection<Method>> T methodsByName(String name, T output) {
synchronized (_methods) {
output.addAll(_methods.get(name));
return output;
}
}
/**
* Get the encapsulated class.
*/
@Override
public Class<?> getHandle() {
return _clazz;
}
// load all constructors from the encapsulated class.
private void loadConstructors() {
Constructor<?>[] constructors = _clazz.getDeclaredConstructors();
for (Constructor c : constructors) {
c.setAccessible(true);
//noinspection unchecked
_constructors.put(c.getParameterTypes().length, c);
}
}
// load all fields from the encapsulated class.
private void loadFields() {
Field[] fields = _clazz.getDeclaredFields();
for (Field field : fields) {
ReflectedField reflectedField = new ReflectedField(this, field);
_fieldsByType.put(field.getType(), reflectedField);
_fieldsByType.put(Object.class, reflectedField);
_fieldsByName.put(field.getName(), reflectedField);
}
}
// load all methods from the encapsulated class.
private void loadMethods() {
Method[] methods = _clazz.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
_methods.put(method.getName(), method);
}
}
}