mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
OOPS
This commit is contained in:
1 parent
58cac3667c
commit
96c243f6bf
1 file changed
+277
+277
@@ -0,0 +1,277 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter.field;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.ScriptDocument;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodCallInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.ScriptTypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Represents an enum constant declaration with its constructor call.
|
||||
* Combines a FieldInfo (the constant itself) with a MethodCallInfo (the constructor call validation).
|
||||
* This allows us to reuse existing method call validation logic for enum constructor matching.
|
||||
*/
|
||||
public class EnumConstantInfo {
|
||||
|
||||
private final FieldInfo fieldInfo;
|
||||
private final MethodCallInfo constructorCall; // Can be null if no args provided
|
||||
private final ScriptTypeInfo enumType;
|
||||
|
||||
private EnumConstantInfo(FieldInfo fieldInfo, MethodCallInfo constructorCall, ScriptTypeInfo enumType) {
|
||||
this.fieldInfo = fieldInfo;
|
||||
this.constructorCall = constructorCall;
|
||||
this.enumType = enumType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse enum constants from an enum body.
|
||||
* Returns a list of EnumConstantInfo objects, each representing one constant.
|
||||
*
|
||||
* @param enumType The enum type being parsed
|
||||
* @param bodyText The text content of the enum body
|
||||
* @param bodyOffset The absolute offset where the enum body starts
|
||||
* @param keywordPattern Pattern to detect Java keywords (to skip them)
|
||||
* @return List of parsed enum constants
|
||||
*/
|
||||
public static List<EnumConstantInfo> parseEnumConstants(
|
||||
ScriptTypeInfo enumType,
|
||||
String bodyText,
|
||||
int bodyOffset,
|
||||
Pattern keywordPattern) {
|
||||
|
||||
List<EnumConstantInfo> constants = new ArrayList<>();
|
||||
|
||||
// Find where enum constants end (first semicolon not in parens, or first method/field declaration)
|
||||
int constantsEnd = findEnumConstantsEnd(bodyText);
|
||||
if (constantsEnd <= 0) {
|
||||
// No semicolon found - entire body might be constants
|
||||
constantsEnd = bodyText.length();
|
||||
}
|
||||
|
||||
// Pattern to match enum constants: NAME or NAME() or NAME(args)
|
||||
Pattern constantPattern = Pattern.compile(
|
||||
"([A-Za-z_][a-zA-Z0-9_]*)\\s*(\\(([^)]*)\\))?");
|
||||
|
||||
Matcher m = constantPattern.matcher(bodyText);
|
||||
int lastEnd = 0;
|
||||
|
||||
while (m.find()) {
|
||||
// Skip if we're past the constants section
|
||||
if (m.start() >= constantsEnd) {
|
||||
break;
|
||||
}
|
||||
|
||||
int absPos = bodyOffset + m.start();
|
||||
if (ScriptDocument.INSTANCE.isExcluded(absPos)) {
|
||||
lastEnd = m.end();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is at a valid position (after comma or at start)
|
||||
String beforeMatch = bodyText.substring(lastEnd, m.start()).trim();
|
||||
if (!beforeMatch.isEmpty() && !beforeMatch.equals(",")) {
|
||||
// Not a valid enum constant position - might be inside parens
|
||||
continue;
|
||||
}
|
||||
|
||||
String constantName = m.group(1);
|
||||
String argsClause = m.group(2); // includes parens if present
|
||||
String args = m.group(3); // just the args without parens
|
||||
|
||||
// Skip keywords
|
||||
if (keywordPattern.matcher(constantName).matches()) {
|
||||
lastEnd = m.end();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create FieldInfo for the enum constant
|
||||
FieldInfo fieldInfo = FieldInfo.enumConstant(
|
||||
constantName,
|
||||
enumType,
|
||||
absPos,
|
||||
args,
|
||||
enumType
|
||||
);
|
||||
|
||||
// Create MethodCallInfo for constructor validation if args present
|
||||
MethodCallInfo constructorCall = null;
|
||||
if (argsClause != null && !argsClause.isEmpty()) {
|
||||
int openParenPos = bodyOffset + m.start(2);
|
||||
int closeParenPos = bodyOffset + m.end(2) - 1;
|
||||
|
||||
constructorCall = createConstructorCall(
|
||||
enumType,
|
||||
constantName,
|
||||
absPos,
|
||||
openParenPos,
|
||||
closeParenPos
|
||||
);
|
||||
} else if (enumType.hasConstructors()) {
|
||||
// No args provided, but enum has constructors - validate against no-arg constructor
|
||||
int openParenPos = absPos + constantName.length();
|
||||
int closeParenPos = openParenPos;
|
||||
|
||||
constructorCall = createConstructorCall(
|
||||
enumType,
|
||||
constantName,
|
||||
absPos,
|
||||
openParenPos,
|
||||
closeParenPos
|
||||
);
|
||||
}
|
||||
|
||||
EnumConstantInfo constantInfo = new EnumConstantInfo(fieldInfo, constructorCall, enumType);
|
||||
constants.add(constantInfo);
|
||||
|
||||
lastEnd = m.end();
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MethodCallInfo for an enum constant's constructor call.
|
||||
* Validates the arguments against available constructors.
|
||||
*/
|
||||
private static MethodCallInfo createConstructorCall(
|
||||
ScriptTypeInfo enumType,
|
||||
String constantName,
|
||||
int constantStart,
|
||||
int openParenPos,
|
||||
int closeParenPos) {
|
||||
|
||||
List<MethodInfo> constructors = enumType.getConstructors();
|
||||
|
||||
// Parse arguments - pass absolute positions (bodyOffset accounts for the offset into the document)
|
||||
List<MethodCallInfo.Argument> arguments = ScriptDocument.INSTANCE.parseMethodArguments(
|
||||
openParenPos + 1,
|
||||
closeParenPos,
|
||||
null
|
||||
);
|
||||
|
||||
// Find matching constructor
|
||||
MethodInfo matchedConstructor = null;
|
||||
List<MethodInfo> candidates = new ArrayList<>();
|
||||
|
||||
for (MethodInfo constructor : constructors) {
|
||||
candidates.add(constructor);
|
||||
if (constructor.getParameterCount() == arguments.size()) {
|
||||
matchedConstructor = constructor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create MethodCallInfo
|
||||
MethodCallInfo callInfo = new MethodCallInfo(
|
||||
constantName, // Use constant name as "method" name
|
||||
constantStart,
|
||||
openParenPos,
|
||||
openParenPos,
|
||||
closeParenPos,
|
||||
arguments,
|
||||
enumType, // Receiver is the enum type
|
||||
matchedConstructor
|
||||
);
|
||||
|
||||
|
||||
callInfo.setConstructor(true);
|
||||
callInfo.validate();
|
||||
return callInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find where enum constants section ends.
|
||||
* Returns the position of the first semicolon that's not inside parentheses,
|
||||
* or the position of the first method/field declaration.
|
||||
*/
|
||||
private static int findEnumConstantsEnd(String bodyText) {
|
||||
int parenDepth = 0;
|
||||
boolean foundConstant = false;
|
||||
|
||||
for (int i = 0; i < bodyText.length(); i++) {
|
||||
char c = bodyText.charAt(i);
|
||||
|
||||
if (c == '(') {
|
||||
parenDepth++;
|
||||
} else if (c == ')') {
|
||||
parenDepth--;
|
||||
} else if (c == ';' && parenDepth == 0) {
|
||||
return i;
|
||||
} else if (c == '{' && parenDepth == 0) {
|
||||
// Start of method body - constants end before this
|
||||
return findStatementStart(bodyText, i);
|
||||
}
|
||||
|
||||
// Track if we've found at least one identifier (potential constant)
|
||||
if (Character.isJavaIdentifierStart(c)) {
|
||||
foundConstant = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found constants but no semicolon, return the full length
|
||||
return foundConstant ? bodyText.length() : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the start of a statement by going backwards from a position.
|
||||
*/
|
||||
private static int findStatementStart(String text, int fromPos) {
|
||||
int pos = fromPos - 1;
|
||||
int parenDepth = 0;
|
||||
|
||||
while (pos >= 0) {
|
||||
char c = text.charAt(pos);
|
||||
if (c == ')')
|
||||
parenDepth++;
|
||||
else if (c == '(')
|
||||
parenDepth--;
|
||||
else if ((c == ',' || c == ';' || c == '{' || c == '}') && parenDepth == 0) {
|
||||
return pos + 1;
|
||||
}
|
||||
pos--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ==================== GETTERS ====================
|
||||
|
||||
public FieldInfo getFieldInfo() {
|
||||
return fieldInfo;
|
||||
}
|
||||
|
||||
public MethodCallInfo getConstructorCall() {
|
||||
return constructorCall;
|
||||
}
|
||||
|
||||
public ScriptTypeInfo getEnumType() {
|
||||
return enumType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return fieldInfo.getName();
|
||||
}
|
||||
|
||||
public int getDeclarationOffset() {
|
||||
return fieldInfo.getDeclarationOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this enum constant has any errors (constructor mismatch, etc).
|
||||
*/
|
||||
public boolean hasError() {
|
||||
return (constructorCall != null && constructorCall.hasError());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message if any.
|
||||
*/
|
||||
public String getErrorMessage() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user