|
2 | 2 |
|
3 | 3 | import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery; |
4 | 4 | import com.laytonsmith.PureUtilities.Common.ReflectionUtils; |
| 5 | +import com.laytonsmith.PureUtilities.Common.ReflectionUtils.ReflectionException; |
5 | 6 | import com.laytonsmith.abstraction.enums.EnumConvertor; |
| 7 | +import com.laytonsmith.abstraction.enums.MCVersion; |
6 | 8 | import com.laytonsmith.annotations.abstractionenum; |
7 | | -import com.laytonsmith.core.Prefs; |
| 9 | +import com.laytonsmith.core.LogLevel; |
| 10 | +import com.laytonsmith.core.MSLog; |
| 11 | +import com.laytonsmith.core.MSLog.Tags; |
| 12 | +import com.laytonsmith.core.constructs.Target; |
8 | 13 |
|
9 | | -import java.lang.reflect.InvocationTargetException; |
10 | 14 | import java.lang.reflect.Method; |
11 | | -import java.util.Set; |
12 | 15 |
|
13 | 16 | /** |
14 | 17 | * This class dynamically detects the server version being run, using various checks as needed. |
@@ -67,89 +70,110 @@ public static void setServerType(Implementation.Type type) { |
67 | 70 | } |
68 | 71 | } |
69 | 72 |
|
| 73 | + if(type == Type.TEST || type == Type.SHELL || !useAbstractEnumThread) { |
| 74 | + return; |
| 75 | + } |
70 | 76 | //Fire off our abstractionenum checks in a new Thread |
71 | | - if(type != Type.TEST && type != Type.SHELL && useAbstractEnumThread) { |
72 | | - Thread abstractionenumsThread; |
73 | | - abstractionenumsThread = new Thread(() -> { |
| 77 | + Thread abstractionenumsThread = new Thread(() -> { |
| 78 | + try { |
74 | 79 | try { |
75 | | - try { |
76 | | - //Let the server startup data blindness go by first, so we display any error messages prominently, |
77 | | - //since an Error is a case of very bad code that shouldn't have been released to begin with. |
78 | | - Thread.sleep(15000); |
79 | | - } catch (InterruptedException ex) { |
80 | | - // |
| 80 | + //Let the server startup data blindness go by first, so we display any error messages prominently, |
| 81 | + //since an Error is a case of very bad code that shouldn't have been released to begin with. |
| 82 | + Thread.sleep(15000); |
| 83 | + } catch (InterruptedException ex) { |
| 84 | + // |
| 85 | + } |
| 86 | + for(Class c : ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(abstractionenum.class)) { |
| 87 | + abstractionenum annotation = (abstractionenum) c.getAnnotation(abstractionenum.class); |
| 88 | + if(!EnumConvertor.class.isAssignableFrom(c)) { |
| 89 | + throw new Error("Only classes that extend EnumConvertor may use @abstractionenum. " |
| 90 | + + c.getName() + " does not, yet it uses the annotation."); |
81 | 91 | } |
82 | | - Set<Class<?>> abstractionenums = ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(abstractionenum.class); |
83 | | - for(Class c : abstractionenums) { |
84 | | - abstractionenum annotation = (abstractionenum) c.getAnnotation(abstractionenum.class); |
85 | | - if(EnumConvertor.class.isAssignableFrom(c)) { |
86 | | - EnumConvertor<Enum, Enum> convertor; |
87 | | - try { |
88 | | - //Now, if this is not the current server type, skip it |
89 | | - if(annotation.implementation() != serverType) { |
90 | | - continue; |
91 | | - } |
92 | | - //Next, verify usage of the annotation (it is an error if not used properly) |
93 | | - //All EnumConvertor subclasses should have public static getConvertor methods, let's grab it now |
94 | | - Method m = c.getDeclaredMethod("getConvertor"); |
95 | | - convertor = (EnumConvertor<Enum, Enum>) m.invoke(null); |
96 | | - //Go through and check for a proper mapping both ways, from concrete to abstract, and vice versa. |
97 | | - //At this point, if there is an error, it is only a warning, NOT an error. |
98 | | - Class abstractEnum = annotation.forAbstractEnum(); |
99 | | - Class concreteEnum = annotation.forConcreteEnum(); |
100 | | - checkEnumConvertors(convertor, abstractEnum, concreteEnum, false); |
101 | | - checkEnumConvertors(convertor, concreteEnum, abstractEnum, true); |
102 | | - |
103 | | - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { |
104 | | - throw new Error(ex); |
105 | | - } catch (NoSuchMethodException ex) { |
106 | | - throw new Error(serverType.getBranding() + ": The method with signature public static " + c.getName() + " getConvertor() was not found in " + c.getName() |
107 | | - + " Please add the following code: \n" |
108 | | - + "private static " + c.getName() + " instance;\n" |
109 | | - + "public static " + c.getName() + " getConvertor(){\n" |
110 | | - + "\tif(instance == null){\n" |
111 | | - + "\t\tinstance = new " + c.getName() + "();\n" |
112 | | - + "\t}\n" |
113 | | - + "\treturn instance;\n" |
114 | | - + "}\n" |
115 | | - + "If you do not know what error is, please report this to the developers."); |
116 | | - } |
117 | | - } else { |
118 | | - throw new Error("Only classes that extend EnumConvertor may use @abstractionenum. " + c.getName() + " does not, yet it uses the annotation."); |
119 | | - } |
120 | | - |
| 92 | + //Now, if this is not the current server type, skip it |
| 93 | + if(annotation.implementation() != serverType) { |
| 94 | + continue; |
121 | 95 | } |
122 | | - } catch (Exception e) { |
123 | | - boolean debugMode; |
| 96 | + EnumConvertor<Enum, Enum> convertor; |
124 | 97 | try { |
125 | | - debugMode = Prefs.DebugMode(); |
126 | | - } catch (RuntimeException ex) { |
127 | | - //Set it to true if we fail to load prefs, which can happen |
128 | | - //with a buggy front end. |
129 | | - debugMode = true; |
| 98 | + //Next, verify usage of the annotation (it is an error if not used properly) |
| 99 | + //All EnumConvertor subclasses should have public static getConvertor methods, let's grab it now |
| 100 | + Method m = c.getDeclaredMethod("getConvertor"); |
| 101 | + convertor = (EnumConvertor<Enum, Enum>) m.invoke(null); |
| 102 | + } catch (NoSuchMethodException ex) { |
| 103 | + throw new Error("The method with signature public static " + c.getName() |
| 104 | + + " getConvertor() was not found in " + c.getName() + "." |
| 105 | + + " Please add the following code: \n" |
| 106 | + + "private static " + c.getName() + " instance;\n" |
| 107 | + + "public static " + c.getName() + " getConvertor(){\n" |
| 108 | + + "\tif(instance == null){\n" |
| 109 | + + "\t\tinstance = new " + c.getName() + "();\n" |
| 110 | + + "\t}\n" |
| 111 | + + "\treturn instance;\n" |
| 112 | + + "}\n" |
| 113 | + + "If you do not know what error is, please report this to the developers."); |
130 | 114 | } |
131 | | - if(debugMode) { |
132 | | - //If we're in debug mode, sure, go ahead and print the stack trace, |
133 | | - //but otherwise we don't want to bother the user. |
134 | | - e.printStackTrace(); |
| 115 | + //Go through and check for a proper mapping both ways, from concrete to abstract, and vice versa. |
| 116 | + //At this point, if there is an error, it is only a warning, NOT an error. |
| 117 | + if(MSLog.GetLogger().WillLog(Tags.GENERAL, LogLevel.WARNING)) { |
| 118 | + Class abstractEnum = annotation.forAbstractEnum(); |
| 119 | + Class concreteEnum = annotation.forConcreteEnum(); |
| 120 | + checkAbstractEnumConversion(convertor, abstractEnum, concreteEnum); |
| 121 | + checkConcreteEnumConversion(convertor, concreteEnum, abstractEnum); |
135 | 122 | } |
136 | 123 | } |
137 | | - }, "Abstraction Enum Verification Thread"); |
138 | | - abstractionenumsThread.setPriority(Thread.MIN_PRIORITY); |
139 | | - abstractionenumsThread.setDaemon(true); |
140 | | - abstractionenumsThread.start(); |
141 | | - } |
| 124 | + } catch (Exception e) { |
| 125 | + MSLog.GetLogger().e(Tags.GENERAL, e, Target.UNKNOWN); |
| 126 | + } |
| 127 | + }, "Abstraction Enum Verification Thread"); |
| 128 | + abstractionenumsThread.setPriority(Thread.MIN_PRIORITY); |
| 129 | + abstractionenumsThread.setDaemon(true); |
| 130 | + abstractionenumsThread.start(); |
142 | 131 | } |
143 | 132 |
|
144 | | - private static void checkEnumConvertors(EnumConvertor convertor, Class to, Class from, boolean isToConcrete) { |
145 | | - for(Object enumConst : from.getEnumConstants()) { |
146 | | - ReflectionUtils.set(EnumConvertor.class, convertor, "useError", false); |
147 | | - if(isToConcrete) { |
148 | | - convertor.getConcreteEnum((Enum) enumConst); |
| 133 | + private static void checkAbstractEnumConversion(EnumConvertor convertor, Class<? extends Enum> abstracted, Class<? extends Enum> concrete) { |
| 134 | + for(Enum abstractValue : abstracted.getEnumConstants()) { |
| 135 | + Enum enumConcrete; |
| 136 | + Deprecated deprecated; |
| 137 | + try { |
| 138 | + enumConcrete = ReflectionUtils.invokeMethod(convertor, "getConcreteEnumCustom", abstractValue); |
| 139 | + deprecated = concrete.getField(enumConcrete.name()).getAnnotation(Deprecated.class); |
| 140 | + } catch (NoSuchFieldException | ReflectionException ex) { |
| 141 | + // Log missing concrete values for existing abstract values. |
| 142 | + // These can mean implementation differences, removed values, or we're running an older MC version. |
| 143 | + MSLog.GetLogger().w(Tags.GENERAL, abstracted.getSimpleName() + "." + abstractValue.name() |
| 144 | + + " cannot be converted to " + concrete.getSimpleName(), Target.UNKNOWN); |
| 145 | + continue; |
| 146 | + } |
| 147 | + // Log deprecations of concrete values. |
| 148 | + if(deprecated == null) { |
| 149 | + continue; |
| 150 | + } |
| 151 | + if(deprecated.since().isEmpty()) { |
| 152 | + MSLog.GetLogger().i(Tags.GENERAL, concrete.getSimpleName() + "." + enumConcrete.name() |
| 153 | + + " is deprecated", Target.UNKNOWN); |
| 154 | + } else if(MCVersion.match(deprecated.since().split("\\.")).lte(MCVersion.EARLIEST_SUPPORTED)) { |
| 155 | + MSLog.GetLogger().w(Tags.GENERAL, concrete.getSimpleName() + "." + enumConcrete.name() |
| 156 | + + " is deprecated since " + deprecated.since(), Target.UNKNOWN); |
149 | 157 | } else { |
150 | | - convertor.getAbstractedEnum((Enum) enumConst); |
| 158 | + MSLog.GetLogger().i(Tags.GENERAL, concrete.getSimpleName() + "." + enumConcrete.name() |
| 159 | + + " is deprecated since " + deprecated.since(), Target.UNKNOWN); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + private static void checkConcreteEnumConversion(EnumConvertor convertor, Class<? extends Enum> concrete, Class<? extends Enum> abstracted) { |
| 165 | + for(Enum concreteValue : concrete.getEnumConstants()) { |
| 166 | + try { |
| 167 | + ReflectionUtils.invokeMethod(convertor, "getAbstractedEnumCustom", concreteValue); |
| 168 | + } catch (ReflectionException ex) { |
| 169 | + try { |
| 170 | + // Log missing abstract values for concrete values that are not deprecated. |
| 171 | + if(concrete.getField(concreteValue.name()).getAnnotation(Deprecated.class) == null) { |
| 172 | + MSLog.GetLogger().w(Tags.GENERAL, concrete.getSimpleName() + "." + concreteValue.name() |
| 173 | + + " cannot be converted to " + abstracted.getSimpleName(), Target.UNKNOWN); |
| 174 | + } |
| 175 | + } catch (NoSuchFieldException ignore) {} |
151 | 176 | } |
152 | | - ReflectionUtils.set(EnumConvertor.class, convertor, "useError", true); |
153 | 177 | } |
154 | 178 | } |
155 | 179 |
|
|
0 commit comments