Further improve upon Madus work with automatic editor.

This commit is contained in:
Olof Larsson 2016-04-14 13:02:06 +02:00
parent 1b73f9bea0
commit 712a1ab0f4
No known key found for this signature in database
GPG Key ID: BBEF14F97DA52474
18 changed files with 261 additions and 153 deletions

View File

@ -231,7 +231,7 @@ public class MassiveCore extends MassivePlugin
IntegrationVault.get()
);
// Some commands rely on the MConf being present.
// NOTE: Some commands rely on the MConf being loaded at construction.
this.activate(
// Command
CmdMassiveCore.get(),

View File

@ -6,7 +6,7 @@ import java.util.Map;
import org.bukkit.permissions.Permissible;
import com.massivecraft.massivecore.command.editor.EditorField;
import com.massivecraft.massivecore.command.editor.annotation.EditorType;
import com.massivecraft.massivecore.store.Entity;
import com.massivecraft.massivecore.util.MUtil;
import com.massivecraft.massivecore.util.PermUtil;
@ -113,7 +113,7 @@ public class MassiveCoreMConf extends Entity<MassiveCoreMConf>
public volatile long millisBetweenRemotePollWithoutPusher = TimeUnit.MILLIS_PER_SECOND * 10;
public volatile long millisBetweenRemotePollWithPusher = TimeUnit.MILLIS_PER_MINUTE * 1;
@EditorField(singletonName = "iOn")
@EditorType(fieldName = "iOn")
public boolean warnOnLocalAlter = false;
}

View File

@ -10,42 +10,42 @@ public enum MassiveCorePerm
// ENUM
// -------------------------------------------- //
BASECOMMAND("basecommand"),
TEST("test"),
ID("id"),
VERSION("version"),
HEARSOUND("hearsound"),
STORE("store"),
STORE_STATS("store.stats"),
STORE_LISTCOLLS("store.listcolls"),
STORE_COPYDB("store.copydb"),
USYS("usys"),
USYS_MULTIVERSE("usys.multiverse"),
USYS_MULTIVERSE_LIST("usys.multiverse.list"),
USYS_MULTIVERSE_SHOW("usys.multiverse.show"),
USYS_MULTIVERSE_NEW("usys.multiverse.new"),
USYS_MULTIVERSE_DEL("usys.multiverse.del"),
USYS_UNIVERSE("usys.universe"),
USYS_UNIVERSE_NEW("usys.universe.new"),
USYS_UNIVERSE_DEL("usys.universe.del"),
USYS_UNIVERSE_CLEAR("usys.universe.clear"),
USYS_WORLD("usys.world"),
USYS_ASPECT("usys.aspect"),
USYS_ASPECT_LIST("usys.aspect.list"),
USYS_ASPECT_SHOW("usys.aspect.show"),
USYS_ASPECT_USE("usys.aspect.use"),
BUFFER("buffer"),
BUFFER_PRINT("buffer.print"),
BUFFER_CLEAR("buffer.clear"),
BUFFER_SET("buffer.set"),
BUFFER_ADD("buffer.add"),
BUFFER_WHITESPACE("buffer.whitespace"),
CMDURL("cmdurl"),
CONFIG("config"),
NOTPDELAY("notpdelay"),
VARIABLE_BOOK("variable.book"),
VARIABLE_BUFFER("variable.buffer"),
CLICK("click"),
BASECOMMAND,
TEST,
ID,
VERSION,
HEARSOUND,
STORE,
STORE_STATS,
STORE_LISTCOLLS,
STORE_COPYDB,
USYS,
USYS_MULTIVERSE,
USYS_MULTIVERSE_LIST,
USYS_MULTIVERSE_SHOW,
USYS_MULTIVERSE_NEW,
USYS_MULTIVERSE_DEL,
USYS_UNIVERSE,
USYS_UNIVERSE_NEW,
USYS_UNIVERSE_DEL,
USYS_UNIVERSE_CLEAR,
USYS_WORLD,
USYS_ASPECT,
USYS_ASPECT_LIST,
USYS_ASPECT_SHOW,
USYS_ASPECT_USE,
BUFFER,
BUFFER_PRINT,
BUFFER_CLEAR,
BUFFER_SET,
BUFFER_ADD,
BUFFER_WHITESPACE,
CMDURL,
CONFIG,
NOTPDELAY,
VARIABLE_BOOK,
VARIABLE_BUFFER,
CLICK,
// END OF LIST
;
@ -60,9 +60,9 @@ public enum MassiveCorePerm
// CONSTRUCT
// -------------------------------------------- //
MassiveCorePerm(final String permissionNode)
MassiveCorePerm()
{
this.node = "massivecore."+permissionNode;
this.node = "massivecore." + this.name().toLowerCase().replace('_', '.');
}
// -------------------------------------------- //

View File

@ -1,7 +1,8 @@
package com.massivecraft.massivecore.command.editor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.massivecraft.massivecore.command.MassiveCommand;
public class CommandEditReflection<O, V> extends CommandEditAbstract<O, V>
{
@ -12,29 +13,18 @@ public class CommandEditReflection<O, V> extends CommandEditAbstract<O, V>
public CommandEditReflection(EditSettings<O> settings, Property<O, V> property, Class<V> clazz)
{
super(settings, property, null);
// TODO: What about super classes?
// TODO: While we not often use super classes they could in theory also be meant to be editable.
// TODO: Something to consider coding in for the future.
for (Field field : clazz.getDeclaredFields())
{
if ( ! this.isOkay(field)) continue;
Property<O, ?> propertyReflection = PropertyReflection.get(field);
this.addChild(propertyReflection.createEditCommand(settings));
if ( ! PropertyReflection.isVisible(field)) continue;
Property<O, ?> fieldProperty = PropertyReflection.get(field);
MassiveCommand fieldCommand = fieldProperty.createEditCommand(settings);
this.addChild(fieldCommand);
}
}
// -------------------------------------------- //
// OKAY
// -------------------------------------------- //
public boolean isOkay(Field field)
{
if ( ! this.isModifiersOkay(field.getModifiers())) return false;
return true;
}
public boolean isModifiersOkay(int modifiers)
{
if (Modifier.isStatic(modifiers)) return false;
if (Modifier.isVolatile(modifiers)) return false;
return true;
}
}

View File

@ -1,5 +1,6 @@
package com.massivecraft.massivecore.command.editor;
import com.massivecraft.massivecore.command.requirement.RequirementHasPerm;
import com.massivecraft.massivecore.command.type.RegistryType;
import com.massivecraft.massivecore.command.type.Type;
import com.massivecraft.massivecore.command.type.TypeSingleton;
@ -18,7 +19,7 @@ public class CommandEditSingleton<O> extends CommandEditReflection<O, O>
@SuppressWarnings("unchecked")
public CommandEditSingleton(O object, Type<O> typeObject, String permission)
{
super(EditSettingsSingleton.get(object, typeObject, permission), new PropertyThis<>(typeObject), (Class<O>) object.getClass());
super(createEditSettings(object, typeObject, permission), new PropertyThis<>(typeObject), (Class<O>) object.getClass());
String name = typeObject.getName(object);
this.setAliases(name);
this.setDesc("edit " + name);
@ -28,6 +29,18 @@ public class CommandEditSingleton<O> extends CommandEditReflection<O, O>
// UTIL
// -------------------------------------------- //
private static <O> EditSettings<O> createEditSettings(O object, Type<O> typeObject, String permission)
{
EditSettings<O> ret = new EditSettings<>(typeObject);
PropertyUsed<O> usedProperty = new PropertyUsed<O>(ret, object);
ret.setUsedProperty(usedProperty);
ret.addPropertyRequirements(RequirementHasPerm.get(permission));
return ret;
}
@SuppressWarnings("unchecked")
private static <O> Type<O> getType(O object)
{
@ -39,6 +52,7 @@ public class CommandEditSingleton<O> extends CommandEditReflection<O, O>
{
RegistryType.register((Class<O>) object.getClass(), ret);
}
return ret;
}

View File

@ -45,13 +45,20 @@ public class EditSettings<O>
return new EditSettings<CommandSender>(TypeSender.get(), new PropertyThis<CommandSender>(TypeSender.get()));
}
// The requirements to edit used object.
// Requirements to edit the used object.
private List<Requirement> usedRequirements = new ArrayList<Requirement>();
public List<Requirement> getUsedRequirements() { return this.usedRequirements; }
public void setUsedRequirements(List<Requirement> requirements) { this.usedRequirements = requirements; }
public void addUsedRequirements(Collection<Requirement> requirements) { this.usedRequirements.addAll(requirements); }
public void addUsedRequirements(Requirement... requirements) { this.addUsedRequirements(Arrays.asList(requirements)); }
// Requirements to edit properties. Common stuff shared by all properties.
private List<Requirement> propertyRequirements = new ArrayList<Requirement>();
public List<Requirement> getPropertyRequirements() { return this.propertyRequirements; }
public void setPropertyRequirements(List<Requirement> requirements) { this.propertyRequirements = requirements; }
public void addPropertyRequirements(Collection<Requirement> requirements) { this.propertyRequirements.addAll(requirements); }
public void addPropertyRequirements(Requirement... requirements) { this.addPropertyRequirements(Arrays.asList(requirements)); }
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //

View File

@ -1,59 +0,0 @@
package com.massivecraft.massivecore.command.editor;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.Permission;
import com.massivecraft.massivecore.command.type.Type;
import com.massivecraft.massivecore.util.PermUtil;
public class EditSettingsSingleton<O> extends EditSettings<O>
{
// -------------------------------------------- //
// FIELDS
// -------------------------------------------- //
private final String permission;
public String getPermission() { return this.permission; }
// -------------------------------------------- //
// INSTANCE & CONSTRUCT
// -------------------------------------------- //
public static <O> EditSettingsSingleton<O> get(O object, Type<O> typeObject, String permission)
{
return new EditSettingsSingleton<O>(object, typeObject, permission);
}
public EditSettingsSingleton(final O object, Type<O> typeObject, String permission)
{
super(typeObject);
this.permission = permission;
PropertyUsed<O> usedProperty = new PropertyUsed<O>(this) {
@Override
public O getRaw(CommandSender sender)
{
return object;
}
@Override
public void setRaw(CommandSender sender, O used)
{
}
};
this.setUsedProperty(usedProperty);
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
@Override
public Permission getPropertyPermission(Property<O, ?> property)
{
return PermUtil.get(false, permission);
}
}

View File

@ -161,7 +161,13 @@ public abstract class Property<O, V> implements Named
public CommandEditAbstract<O, V> createEditCommand(EditSettings<O> settings)
{
CommandEditAbstract<O, V> ret = this.getValueType().createEditCommand(settings, this);
// Add general requirements.
ret.addRequirements(settings.getPropertyRequirements());
// Add specific requirements.
ret.addRequirements(this.getRequirements());
return ret;
}

View File

@ -3,6 +3,10 @@ package com.massivecraft.massivecore.command.editor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.massivecraft.massivecore.command.editor.annotation.EditorEditable;
import com.massivecraft.massivecore.command.editor.annotation.EditorInheritable;
import com.massivecraft.massivecore.command.editor.annotation.EditorNullable;
import com.massivecraft.massivecore.command.editor.annotation.EditorVisible;
import com.massivecraft.massivecore.command.type.RegistryType;
import com.massivecraft.massivecore.command.type.Type;
import com.massivecraft.massivecore.util.ReflectionUtil;
@ -35,26 +39,14 @@ public class PropertyReflection<O, V> extends Property<O, V>
public PropertyReflection(Type<O> typeObject, Type<V> typeValue, Field field)
{
super(typeObject, typeValue);
ReflectionUtil.makeAccessible(field);
this.field = field;
this.setInheritable(isInheritable(field));
this.setEditable(isEditable(field));
this.setNullable(isNullable(field));
this.setName(field.getName());
this.field = field;
}
private static boolean isEditable(Field field)
{
return ! Modifier.isFinal(field.getModifiers());
}
private static boolean isNullable(Field field)
{
if (field.getType().isPrimitive()) return false;
EditorField setting = field.getAnnotation(EditorField.class);
if (setting != null && ! setting.nullable()) return false;
return true;
}
// -------------------------------------------- //
@ -73,4 +65,71 @@ public class PropertyReflection<O, V> extends Property<O, V>
ReflectionUtil.setField(this.getField(), object, value);
}
// -------------------------------------------- //
// PROPERTY SETTINGS CALCULATION
// -------------------------------------------- //
public static boolean isVisible(Field field)
{
// Create
boolean ret = true;
// Fill > Standard
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) ret = false;
// Fill > Annotation
EditorVisible annotation = field.getAnnotation(EditorVisible.class);
if (annotation != null) ret = annotation.value();
// Return
return ret;
}
public static boolean isInheritable(Field field)
{
// Create
boolean ret = true;
// Fill > Annotation
EditorInheritable annotation = field.getAnnotation(EditorInheritable.class);
if (annotation != null) ret = annotation.value();
// Return
return ret;
}
public static boolean isEditable(Field field)
{
// Create
boolean ret = true;
// Fill > Standard
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers)) ret = false;
// Fill > Annotation
EditorEditable annotation = field.getAnnotation(EditorEditable.class);
if (annotation != null) ret = annotation.value();
// Return
return ret;
}
public static boolean isNullable(Field field)
{
// Primitive
if (field.getType().isPrimitive()) return false;
// Create
boolean ret = true;
// Fill > Annotation
EditorNullable annotation = field.getAnnotation(EditorNullable.class);
if (annotation != null) ret = annotation.value();
// Return
return ret;
}
}

View File

@ -32,7 +32,7 @@ public class PropertyThisSenderEntity<O extends SenderEntity<O>> extends Propert
@Override
public O getRaw(CommandSender object)
{
return coll.get(object);
return this.getColl().get(object);
}
@Override

View File

@ -4,16 +4,44 @@ import org.bukkit.command.CommandSender;
import com.massivecraft.massivecore.command.type.sender.TypeSender;
public abstract class PropertyUsed<V> extends Property<CommandSender, V>
public class PropertyUsed<V> extends Property<CommandSender, V>
{
// -------------------------------------------- //
// CONSTRUCT
// -------------------------------------------- //
public PropertyUsed(EditSettings<V> settings)
public PropertyUsed(EditSettings<V> settings, V used)
{
super(TypeSender.get(), settings.getObjectType(), "used " + settings.getObjectType().getName());
this.addRequirements(settings.getUsedRequirements());
this.used = used;
}
public PropertyUsed(EditSettings<V> settings)
{
this(settings, null);
}
// -------------------------------------------- //
// OVERRIDE
// -------------------------------------------- //
// We provide a default implementation with "the used" stored internally.
// This makes sense for a few cases.
// Such as when we edit the same instance all the time, such as a configuration.
// Most of the time these methods will however be overridden.
private V used = null;
@Override
public V getRaw(CommandSender sender)
{
return this.used;
}
@Override
public void setRaw(CommandSender sender, V used)
{
this.used = used;
}
}

View File

@ -1,4 +1,4 @@
package com.massivecraft.massivecore.command.editor;
package com.massivecraft.massivecore.command.editor.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@ -7,10 +7,7 @@ import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EditorField
public @interface EditorEditable
{
boolean nullable() default true;
Class<?> type() default void.class;
String singletonName() default "i";
boolean value();
}

View File

@ -0,0 +1,13 @@
package com.massivecraft.massivecore.command.editor.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EditorInheritable
{
boolean value();
}

View File

@ -0,0 +1,13 @@
package com.massivecraft.massivecore.command.editor.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EditorNullable
{
boolean value();
}

View File

@ -0,0 +1,17 @@
package com.massivecraft.massivecore.command.editor.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EditorType
{
// This is the type class.
Class<?> value() default void.class;
// The name of the singleton instance field to use.
String fieldName() default "i";
}

View File

@ -0,0 +1,13 @@
package com.massivecraft.massivecore.command.editor.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EditorVisible
{
boolean value();
}

View File

@ -41,6 +41,9 @@ public class CmdMassiveCore extends MassiveCommand
public CmdMassiveCore()
{
// Tweak
this.cmdMassiveCoreConfig.setAliases("config");
// Children
this.addChild(this.cmdMassiveCoreUsys);
this.addChild(this.cmdMassiveCoreMStore);

View File

@ -37,7 +37,7 @@ import com.massivecraft.massivecore.SoundEffect;
import com.massivecraft.massivecore.collections.ExceptionSet;
import com.massivecraft.massivecore.collections.MassiveMap;
import com.massivecraft.massivecore.collections.WorldExceptionSet;
import com.massivecraft.massivecore.command.editor.EditorField;
import com.massivecraft.massivecore.command.editor.annotation.EditorType;
import com.massivecraft.massivecore.command.type.combined.TypeEntry;
import com.massivecraft.massivecore.command.type.combined.TypePotionEffectWrap;
import com.massivecraft.massivecore.command.type.combined.TypeSoundEffect;
@ -93,12 +93,12 @@ public class RegistryType
public static Type<?> getType(Field field)
{
EditorField setting = field.getAnnotation(EditorField.class);
if (setting != null)
EditorType annotation = field.getAnnotation(EditorType.class);
if (annotation != null)
{
Class<?> clazz = setting.type();
Class<?> clazz = annotation.value();
if (clazz == void.class) clazz = getType(field.getGenericType()).getClass();
return ReflectionUtil.getField(clazz, setting.singletonName(), null);
return ReflectionUtil.getField(clazz, annotation.fieldName(), null);
}
return getType(field.getGenericType());
@ -112,32 +112,39 @@ public class RegistryType
if (type == null) throw new IllegalStateException(reflectType + " is not registered.");
return type;
}
if (reflectType instanceof ParameterizedType)
{
ParameterizedType paramType = (ParameterizedType) reflectType;
Class<?> parent = (Class<?>) paramType.getRawType();
if (Map.class.isAssignableFrom(parent))
{
TypeEntry<?, ?> typeEntry = TypeEntry.get(getType(paramType.getActualTypeArguments()[0]), getType(paramType.getActualTypeArguments()[1]));
return TypeMap.get(typeEntry);
}
if (List.class.isAssignableFrom(parent))
{
return TypeList.get(getType(paramType.getActualTypeArguments()[0]));
}
if (Set.class.isAssignableFrom(parent))
{
return TypeSet.get(getType(paramType.getActualTypeArguments()[0]));
}
if (Entry.class.isAssignableFrom(parent))
{
return TypeEntry.get(getType(paramType.getActualTypeArguments()[0]), getType(paramType.getActualTypeArguments()[1]));
}
if (ExceptionSet.class.isAssignableFrom(parent))
{
return TypeExceptionSet.get(getType(paramType.getActualTypeArguments()[0]));
}
}
throw new IllegalArgumentException("Unknown type: " + reflectType);
}