Fixed annotation parsing in AstBuilder

pulled some hair out on this one
This commit is contained in:
2025-07-23 07:29:16 -04:00
parent 1e15e90d32
commit 85fdcff103
8 changed files with 492 additions and 58 deletions

View File

@@ -297,11 +297,24 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var annotations = new List<Annotation>(); var annotations = new List<Annotation>();
foreach (var context in contexts) foreach (var context in contexts)
{ {
// Check if this is directly an annotation context
if (context is Java9Parser.AnnotationContext annotationContext) if (context is Java9Parser.AnnotationContext annotationContext)
{ {
var annotation = BuildAnnotation(annotationContext); var annotation = BuildAnnotation(annotationContext);
if (annotation != null) annotations.Add(annotation); if (annotation != null) annotations.Add(annotation);
} }
// Check if this is a modifier that contains an annotation
else if (context is ParserRuleContext ruleContext && ruleContext.ChildCount > 0)
{
// The first child of a modifier rule that represents an annotation
// will be an AnnotationContext
var firstChild = ruleContext.GetChild(0);
if (firstChild is Java9Parser.AnnotationContext childAnnotation)
{
var annotation = BuildAnnotation(childAnnotation);
if (annotation != null) annotations.Add(annotation);
}
}
} }
return annotations; return annotations;
} }
@@ -312,25 +325,37 @@ namespace MarketAlly.IronJava.Core.AST.Builders
TypeReference? type = null; TypeReference? type = null;
var arguments = new List<AnnotationArgument>(); var arguments = new List<AnnotationArgument>();
// Annotations can be NormalAnnotation, MarkerAnnotation, or SingleElementAnnotation // Annotations can be NormalAnnotation, MarkerAnnotation, or SingleElementAnnotation
if (context.normalAnnotation() != null) if (context.normalAnnotation() != null)
{ {
var normalAnn = context.normalAnnotation(); var normalAnn = context.normalAnnotation();
type = BuildTypeReference(normalAnn.typeName()); type = BuildTypeReference(normalAnn.typeName());
if (type == null) return null; // Don't return early - try to build the type from the name
if (type == null)
{
var typeName = normalAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(normalAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
if (normalAnn.elementValuePairList() != null) if (normalAnn.elementValuePairList() != null)
{ {
foreach (var pair in normalAnn.elementValuePairList().elementValuePair()) foreach (var pair in normalAnn.elementValuePairList().elementValuePair())
{ {
var name = pair.identifier().GetText(); var name = pair.identifier().GetText();
var value = BuildExpression(pair.elementValue()); var value = BuildElementValue(pair.elementValue());
if (value != null)
// Debug - always add the argument even if value is null
// If value is null, create a string literal as fallback
if (value == null)
{ {
arguments.Add(new AnnotationValueArgument( var elementText = pair.elementValue().GetText();
GetSourceRange(pair), name, value value = new LiteralExpression(GetSourceRange(pair.elementValue()), elementText, LiteralKind.String);
));
} }
arguments.Add(new AnnotationValueArgument(
GetSourceRange(pair), name, value
));
} }
} }
} }
@@ -338,30 +363,89 @@ namespace MarketAlly.IronJava.Core.AST.Builders
{ {
var markerAnn = context.markerAnnotation(); var markerAnn = context.markerAnnotation();
type = BuildTypeReference(markerAnn.typeName()); type = BuildTypeReference(markerAnn.typeName());
if (type == null) return null; if (type == null)
{
var typeName = markerAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(markerAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
// Marker annotations have no arguments // Marker annotations have no arguments
} }
else if (context.singleElementAnnotation() != null) else if (context.singleElementAnnotation() != null)
{ {
var singleAnn = context.singleElementAnnotation(); var singleAnn = context.singleElementAnnotation();
type = BuildTypeReference(singleAnn.typeName()); type = BuildTypeReference(singleAnn.typeName());
if (type == null) return null; if (type == null)
{
var typeName = singleAnn.typeName().GetText();
type = new ClassOrInterfaceType(GetSourceRange(singleAnn.typeName()), typeName, null, new List<TypeArgument>(), new List<Annotation>());
}
if (singleAnn.elementValue() != null) if (singleAnn.elementValue() != null)
{ {
var value = BuildExpression(singleAnn.elementValue()); var value = BuildElementValue(singleAnn.elementValue());
if (value != null)
// Fallback if BuildElementValue returns null
if (value == null)
{ {
arguments.Add(new AnnotationValueArgument( var elementText = singleAnn.elementValue().GetText();
location, "value", value value = new LiteralExpression(GetSourceRange(singleAnn.elementValue()), elementText, LiteralKind.String);
));
} }
arguments.Add(new AnnotationValueArgument(
location, "value", value
));
} }
} }
return type != null ? new Annotation(location, type, arguments) : null; return type != null ? new Annotation(location, type, arguments) : null;
} }
private Expression? BuildElementValue(Java9Parser.ElementValueContext context)
{
if (context.conditionalExpression() != null)
{
var expr = BuildExpression(context.conditionalExpression());
// If BuildExpression returns null, try to parse it as a string literal
if (expr == null)
{
var text = context.conditionalExpression().GetText();
if (text.StartsWith('"') && text.EndsWith('"'))
{
// Remove quotes and create a string literal
var value = text.Substring(1, text.Length - 2);
expr = new LiteralExpression(GetSourceRange(context), value, LiteralKind.String);
}
}
return expr;
}
else if (context.annotation() != null)
{
var annotation = BuildAnnotation(context.annotation());
if (annotation != null)
{
return new AnnotationExpression(GetSourceRange(context), annotation);
}
}
else if (context.elementValueArrayInitializer() != null)
{
var arrayInit = context.elementValueArrayInitializer();
var elements = new List<Expression>();
if (arrayInit.elementValueList() != null)
{
foreach (var elementValue in arrayInit.elementValueList().elementValue())
{
var element = BuildElementValue(elementValue);
if (element != null) elements.Add(element);
}
}
return new ArrayInitializer(GetSourceRange(arrayInit), elements);
}
return null;
}
private List<TypeParameter> BuildTypeParameters(Java9Parser.TypeParametersContext? context) private List<TypeParameter> BuildTypeParameters(Java9Parser.TypeParametersContext? context)
{ {
if (context == null) return new List<TypeParameter>(); if (context == null) return new List<TypeParameter>();
@@ -414,8 +498,8 @@ namespace MarketAlly.IronJava.Core.AST.Builders
Java9Parser.ArrayTypeContext array => BuildArrayType(array), Java9Parser.ArrayTypeContext array => BuildArrayType(array),
Java9Parser.TypeVariableContext typeVar => BuildTypeVariable(typeVar), Java9Parser.TypeVariableContext typeVar => BuildTypeVariable(typeVar),
Java9Parser.ClassTypeContext classType => BuildClassType(classType), Java9Parser.ClassTypeContext classType => BuildClassType(classType),
Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType),
Java9Parser.TypeNameContext typeName => BuildTypeName(typeName), Java9Parser.TypeNameContext typeName => BuildTypeName(typeName),
Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType),
_ => null _ => null
}; };
} }
@@ -980,6 +1064,13 @@ namespace MarketAlly.IronJava.Core.AST.Builders
private Parameter? BuildLastParameter(Java9Parser.LastFormalParameterContext context) private Parameter? BuildLastParameter(Java9Parser.LastFormalParameterContext context)
{ {
// Check if this is just a regular parameter (not varargs)
if (context.formalParameter() != null)
{
return BuildParameter(context.formalParameter());
}
// Otherwise, it's a varargs parameter
var location = GetSourceRange(context); var location = GetSourceRange(context);
var modifiers = BuildModifiers(context.variableModifier()); var modifiers = BuildModifiers(context.variableModifier());
var annotations = BuildAnnotations(context.variableModifier()); var annotations = BuildAnnotations(context.variableModifier());
@@ -989,7 +1080,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var declaratorId = context.variableDeclaratorId(); var declaratorId = context.variableDeclaratorId();
var name = declaratorId.identifier().GetText(); var name = declaratorId.identifier().GetText();
var isFinal = modifiers.HasFlag(Modifiers.Final); var isFinal = modifiers.HasFlag(Modifiers.Final);
var isVarArgs = context.GetChild(context.ChildCount - 2)?.GetText() == "..."; var isVarArgs = true; // This branch is only for varargs
return new Parameter(location, type, name, isVarArgs, isFinal, annotations); return new Parameter(location, type, name, isVarArgs, isFinal, annotations);
} }
@@ -1325,7 +1416,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders
if (context.defaultValue() != null) if (context.defaultValue() != null)
{ {
defaultValue = BuildExpression(context.defaultValue().elementValue()); defaultValue = BuildElementValue(context.defaultValue().elementValue());
} }
return new AnnotationMember(location, name, type, defaultValue); return new AnnotationMember(location, name, type, defaultValue);
@@ -2590,46 +2681,6 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return parameters; return parameters;
} }
private Expression? BuildElementValue(Java9Parser.ElementValueContext context)
{
if (context.conditionalExpression() != null)
{
return BuildConditionalExpression(context.conditionalExpression());
}
else if (context.elementValueArrayInitializer() != null)
{
return BuildElementValueArrayInitializer(context.elementValueArrayInitializer());
}
else if (context.annotation() != null)
{
// Annotation as expression - wrap it
var annotation = BuildAnnotation(context.annotation());
if (annotation != null)
{
// Create a special expression to hold the annotation
return new IdentifierExpression(annotation.Location, "@" + annotation.Type.ToString());
}
}
return null;
}
private ArrayInitializer? BuildElementValueArrayInitializer(Java9Parser.ElementValueArrayInitializerContext context)
{
var location = GetSourceRange(context);
var elements = new List<Expression>();
if (context.elementValueList() != null)
{
foreach (var value in context.elementValueList().elementValue())
{
var expr = BuildElementValue(value);
if (expr != null) elements.Add(expr);
}
}
return new ArrayInitializer(location, elements);
}
// Statement building methods // Statement building methods

View File

@@ -400,6 +400,23 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public override void Accept(IJavaVisitor visitor) => visitor.VisitArrayInitializer(this); public override void Accept(IJavaVisitor visitor) => visitor.VisitArrayInitializer(this);
} }
/// <summary>
/// Represents an annotation used as an expression (in annotation element values).
/// </summary>
public class AnnotationExpression : Expression
{
public Annotation Annotation { get; }
public AnnotationExpression(SourceRange location, Annotation annotation) : base(location)
{
Annotation = annotation;
AddChild(annotation);
}
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitAnnotationExpression(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitAnnotationExpression(this);
}
/// <summary> /// <summary>
/// Represents a lambda expression. /// Represents a lambda expression.
/// </summary> /// </summary>

View File

@@ -51,6 +51,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
void VisitNewExpression(NewExpression node); void VisitNewExpression(NewExpression node);
void VisitNewArrayExpression(NewArrayExpression node); void VisitNewArrayExpression(NewArrayExpression node);
void VisitArrayInitializer(ArrayInitializer node); void VisitArrayInitializer(ArrayInitializer node);
void VisitAnnotationExpression(AnnotationExpression node);
void VisitLambdaExpression(LambdaExpression node); void VisitLambdaExpression(LambdaExpression node);
void VisitLambdaParameter(LambdaParameter node); void VisitLambdaParameter(LambdaParameter node);
void VisitMethodReferenceExpression(MethodReferenceExpression node); void VisitMethodReferenceExpression(MethodReferenceExpression node);
@@ -135,6 +136,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
T VisitNewExpression(NewExpression node); T VisitNewExpression(NewExpression node);
T VisitNewArrayExpression(NewArrayExpression node); T VisitNewArrayExpression(NewArrayExpression node);
T VisitArrayInitializer(ArrayInitializer node); T VisitArrayInitializer(ArrayInitializer node);
T VisitAnnotationExpression(AnnotationExpression node);
T VisitLambdaExpression(LambdaExpression node); T VisitLambdaExpression(LambdaExpression node);
T VisitLambdaParameter(LambdaParameter node); T VisitLambdaParameter(LambdaParameter node);
T VisitMethodReferenceExpression(MethodReferenceExpression node); T VisitMethodReferenceExpression(MethodReferenceExpression node);

View File

@@ -54,6 +54,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
public virtual void VisitNewExpression(NewExpression node) => DefaultVisit(node); public virtual void VisitNewExpression(NewExpression node) => DefaultVisit(node);
public virtual void VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node); public virtual void VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node);
public virtual void VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node); public virtual void VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node);
public virtual void VisitAnnotationExpression(AnnotationExpression node) => DefaultVisit(node);
public virtual void VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node); public virtual void VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node);
public virtual void VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node); public virtual void VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node);
public virtual void VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node); public virtual void VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node);
@@ -133,6 +134,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors
public virtual T VisitNewExpression(NewExpression node) => DefaultVisit(node); public virtual T VisitNewExpression(NewExpression node) => DefaultVisit(node);
public virtual T VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node); public virtual T VisitNewArrayExpression(NewArrayExpression node) => DefaultVisit(node);
public virtual T VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node); public virtual T VisitArrayInitializer(ArrayInitializer node) => DefaultVisit(node);
public virtual T VisitAnnotationExpression(AnnotationExpression node) => DefaultVisit(node);
public virtual T VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node); public virtual T VisitLambdaExpression(LambdaExpression node) => DefaultVisit(node);
public virtual T VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node); public virtual T VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node);
public virtual T VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node); public virtual T VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node);

View File

@@ -8,7 +8,7 @@
<!-- NuGet Package Metadata --> <!-- NuGet Package Metadata -->
<PackageId>IronJava</PackageId> <PackageId>IronJava</PackageId>
<Version>2.1.1</Version> <Version>2.1.2</Version>
<Authors>David H Friedel Jr</Authors> <Authors>David H Friedel Jr</Authors>
<Company>MarketAlly</Company> <Company>MarketAlly</Company>
<Title>IronJava</Title> <Title>IronJava</Title>

View File

@@ -416,6 +416,22 @@ namespace MarketAlly.IronJava.Core.Serialization
return result; return result;
} }
public override Dictionary<string, object?> VisitAnnotationValueArgument(AnnotationValueArgument node)
{
var result = CreateBaseNode(node);
result["name"] = node.Name;
result["value"] = node.Value.Accept(this);
return result;
}
public override Dictionary<string, object?> VisitAnnotationArrayArgument(AnnotationArrayArgument node)
{
var result = CreateBaseNode(node);
result["name"] = node.Name;
result["values"] = node.Values.Select(v => v.Accept(this)).ToList();
return result;
}
public override Dictionary<string, object?> VisitJavaDoc(JavaDoc node) public override Dictionary<string, object?> VisitJavaDoc(JavaDoc node)
{ {
var result = CreateBaseNode(node); var result = CreateBaseNode(node);
@@ -561,6 +577,13 @@ namespace MarketAlly.IronJava.Core.Serialization
return result; return result;
} }
public override Dictionary<string, object?> VisitAnnotationExpression(AnnotationExpression node)
{
var result = CreateBaseNode(node);
result["annotation"] = node.Annotation.Accept(this);
return result;
}
public override Dictionary<string, object?> VisitMethodReferenceExpression(MethodReferenceExpression node) public override Dictionary<string, object?> VisitMethodReferenceExpression(MethodReferenceExpression node)
{ {
var result = CreateBaseNode(node); var result = CreateBaseNode(node);
@@ -747,6 +770,9 @@ namespace MarketAlly.IronJava.Core.Serialization
"InstanceOfExpression" => DeserializeInstanceOfExpression(element, location), "InstanceOfExpression" => DeserializeInstanceOfExpression(element, location),
"NewArrayExpression" => DeserializeNewArrayExpression(element, location), "NewArrayExpression" => DeserializeNewArrayExpression(element, location),
"ArrayInitializer" => DeserializeArrayInitializer(element, location), "ArrayInitializer" => DeserializeArrayInitializer(element, location),
"AnnotationExpression" => DeserializeAnnotationExpression(element, location),
"AnnotationValueArgument" => DeserializeAnnotationValueArgument(element, location),
"AnnotationArrayArgument" => DeserializeAnnotationArrayArgument(element, location),
"MethodReferenceExpression" => DeserializeMethodReferenceExpression(element, location), "MethodReferenceExpression" => DeserializeMethodReferenceExpression(element, location),
"ClassLiteralExpression" => DeserializeClassLiteralExpression(element, location), "ClassLiteralExpression" => DeserializeClassLiteralExpression(element, location),
"LocalVariableStatement" => DeserializeLocalVariableStatement(element, location), "LocalVariableStatement" => DeserializeLocalVariableStatement(element, location),
@@ -1282,6 +1308,30 @@ namespace MarketAlly.IronJava.Core.Serialization
return new ArrayInitializer(location, elements); return new ArrayInitializer(location, elements);
} }
private AnnotationExpression DeserializeAnnotationExpression(JsonElement element, SourceRange location)
{
var annotation = Deserialize(element.GetProperty("annotation")) as Annotation ?? throw new JsonException("annotation is not Annotation");
return new AnnotationExpression(location, annotation);
}
private AnnotationValueArgument DeserializeAnnotationValueArgument(JsonElement element, SourceRange location)
{
var name = element.TryGetProperty("name", out var nameEl) && nameEl.ValueKind != JsonValueKind.Null
? nameEl.GetString()
: null;
var value = Deserialize(element.GetProperty("value")) as Expression ?? throw new JsonException("value is not Expression");
return new AnnotationValueArgument(location, name, value);
}
private AnnotationArrayArgument DeserializeAnnotationArrayArgument(JsonElement element, SourceRange location)
{
var name = element.TryGetProperty("name", out var nameEl) && nameEl.ValueKind != JsonValueKind.Null
? nameEl.GetString()
: null;
var values = DeserializeList<Expression>(element.GetProperty("values"));
return new AnnotationArrayArgument(location, name, values);
}
private MethodReferenceExpression DeserializeMethodReferenceExpression(JsonElement element, SourceRange location) private MethodReferenceExpression DeserializeMethodReferenceExpression(JsonElement element, SourceRange location)
{ {
var target = Deserialize(element.GetProperty("target")) as Expression ?? throw new JsonException("target is not Expression"); var target = Deserialize(element.GetProperty("target")) as Expression ?? throw new JsonException("target is not Expression");

View File

@@ -0,0 +1,215 @@
using System.Linq;
using MarketAlly.IronJava.Core;
using MarketAlly.IronJava.Core.AST.Nodes;
using MarketAlly.IronJava.Core.AST.Query;
using Xunit;
namespace MarketAlly.IronJava.Tests
{
public class AnnotationParsingTests
{
[Fact]
public void TestClassAnnotations()
{
var javaCode = @"
@Entity
@Table(name = ""users"")
public class User {
private String name;
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var userClass = result.Ast.Types.OfType<ClassDeclaration>().FirstOrDefault();
Assert.NotNull(userClass);
Assert.Equal("User", userClass.Name);
// Check annotations
Assert.Equal(2, userClass.Annotations.Count);
var entityAnnotation = userClass.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "Entity");
Assert.NotNull(entityAnnotation);
Assert.Empty(entityAnnotation.Arguments);
var tableAnnotation = userClass.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "Table");
Assert.NotNull(tableAnnotation);
Assert.Single(tableAnnotation.Arguments);
var nameArg = tableAnnotation.Arguments[0] as AnnotationValueArgument;
Assert.NotNull(nameArg);
Assert.Equal("name", nameArg.Name);
}
[Fact]
public void TestMethodAnnotations()
{
var javaCode = @"
public class UserService {
@GetMapping(""/users/{id}"")
@ResponseBody
public User getUser(@PathVariable Long id) {
return null;
}
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var method = AstQuery.FindFirst<MethodDeclaration>(result.Ast);
Assert.NotNull(method);
Assert.Equal("getUser", method.Name);
// Check method annotations
Assert.Equal(2, method.Annotations.Count);
var getMappingAnnotation = method.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "GetMapping");
Assert.NotNull(getMappingAnnotation);
Assert.Single(getMappingAnnotation.Arguments);
var responseBodyAnnotation = method.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "ResponseBody");
Assert.NotNull(responseBodyAnnotation);
Assert.Empty(responseBodyAnnotation.Arguments);
}
[Fact]
public void TestFieldAnnotations()
{
var javaCode = @"
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var fields = AstQuery.FindAll<FieldDeclaration>(result.Ast).ToList();
Assert.Equal(2, fields.Count);
// Check id field annotations
var idField = fields.FirstOrDefault(f => f.Variables[0].Name == "id");
Assert.NotNull(idField);
Assert.Equal(2, idField.Annotations.Count);
var idAnnotation = idField.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "Id");
Assert.NotNull(idAnnotation);
var generatedValueAnnotation = idField.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "GeneratedValue");
Assert.NotNull(generatedValueAnnotation);
Assert.Single(generatedValueAnnotation.Arguments);
// Check name field annotations
var nameField = fields.FirstOrDefault(f => f.Variables[0].Name == "name");
Assert.NotNull(nameField);
Assert.Single(nameField.Annotations);
var columnAnnotation = nameField.Annotations[0];
Assert.Equal("Column", ((ClassOrInterfaceType)columnAnnotation.Type).Name);
Assert.Equal(2, columnAnnotation.Arguments.Count);
}
[Fact]
public void TestParameterAnnotations()
{
var javaCode = @"
public class UserController {
public void updateUser(@RequestBody User user, @PathVariable Long id) {
}
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var method = AstQuery.FindFirst<MethodDeclaration>(result.Ast);
Assert.NotNull(method);
Assert.Equal(2, method.Parameters.Count);
// Check first parameter annotations
var userParam = method.Parameters[0];
Assert.Equal("user", userParam.Name);
Assert.Single(userParam.Annotations);
Assert.Equal("RequestBody", ((ClassOrInterfaceType)userParam.Annotations[0].Type).Name);
// Check second parameter annotations
var idParam = method.Parameters[1];
Assert.Equal("id", idParam.Name);
Assert.Single(idParam.Annotations);
Assert.Equal("PathVariable", ((ClassOrInterfaceType)idParam.Annotations[0].Type).Name);
}
[Fact]
public void TestNestedAnnotations()
{
var javaCode = @"
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String value() default ""default"";
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var annotationDecl = result.Ast.Types.OfType<AnnotationDeclaration>().FirstOrDefault();
Assert.NotNull(annotationDecl);
Assert.Equal("MyAnnotation", annotationDecl.Name);
// Check meta-annotations
Assert.Equal(2, annotationDecl.Annotations.Count);
var targetAnnotation = annotationDecl.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "Target");
Assert.NotNull(targetAnnotation);
Assert.Single(targetAnnotation.Arguments);
var retentionAnnotation = annotationDecl.Annotations.FirstOrDefault(a => ((ClassOrInterfaceType)a.Type).Name == "Retention");
Assert.NotNull(retentionAnnotation);
Assert.Single(retentionAnnotation.Arguments);
}
[Fact]
public void TestAnnotationArrayValues()
{
var javaCode = @"
@SuppressWarnings({""unchecked"", ""rawtypes""})
public class Test {
}
";
var result = JavaParser.Parse(javaCode);
Assert.True(result.Success);
Assert.NotNull(result.Ast);
var testClass = result.Ast.Types.OfType<ClassDeclaration>().FirstOrDefault();
Assert.NotNull(testClass);
Assert.Single(testClass.Annotations);
var suppressWarningsAnnotation = testClass.Annotations[0];
Assert.Equal("SuppressWarnings", ((ClassOrInterfaceType)suppressWarningsAnnotation.Type).Name);
Assert.Single(suppressWarningsAnnotation.Arguments);
var valueArg = suppressWarningsAnnotation.Arguments[0] as AnnotationValueArgument;
Assert.NotNull(valueArg);
Assert.Equal("value", valueArg.Name);
// The value should be an ArrayInitializer expression
var arrayInit = valueArg.Value as ArrayInitializer;
Assert.NotNull(arrayInit);
Assert.Equal(2, arrayInit.Elements.Count);
}
}
}

View File

@@ -6,7 +6,7 @@ using MarketAlly.IronJava.Core.AST.Transformation;
using MarketAlly.IronJava.Core.AST.Visitors; using MarketAlly.IronJava.Core.AST.Visitors;
using MarketAlly.IronJava.Core.Serialization; using MarketAlly.IronJava.Core.Serialization;
namespace MarketAlly.IronJava.Sample namespace IronJava.Sample
{ {
class Program class Program
{ {
@@ -19,6 +19,11 @@ namespace MarketAlly.IronJava.Sample
TestNestedTypes(); TestNestedTypes();
Console.WriteLine("\n===========================\n"); Console.WriteLine("\n===========================\n");
// Test annotations
TestAnnotations();
Console.WriteLine("\n===========================\n");
// Sample Java code // Sample Java code
string javaCode = @" string javaCode = @"
package com.example; package com.example;
@@ -296,6 +301,98 @@ public class OuterClass {
Console.WriteLine("✗ Parse failed!"); Console.WriteLine("✗ Parse failed!");
} }
} }
static void TestAnnotations()
{
Console.WriteLine("Testing Annotation Parsing");
Console.WriteLine("--------------------------");
var javaCode = @"
@Entity
@Table(name = ""users"")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@GetMapping(""/users/{id}"")
@ResponseBody
public User getUser(@PathVariable Long id) {
return null;
}
}
";
var result = JavaParser.Parse(javaCode);
if (result.Success)
{
Console.WriteLine("✓ Parse successful!");
var userClass = result.Ast!.Types[0] as ClassDeclaration;
if (userClass != null)
{
Console.WriteLine($"\nClass: {userClass.Name}");
Console.WriteLine($" Class annotations: {userClass.Annotations.Count}");
foreach (var annotation in userClass.Annotations)
{
var typeName = annotation.Type is ClassOrInterfaceType cit ? cit.Name : annotation.Type.ToString();
Console.WriteLine($" - @{typeName} with {annotation.Arguments.Count} arguments");
}
// Check fields
var fields = userClass.Members.OfType<FieldDeclaration>().ToList();
Console.WriteLine($"\n Fields: {fields.Count}");
foreach (var field in fields)
{
Console.WriteLine($" - {field.Variables[0].Name}: {field.Annotations.Count} annotations");
foreach (var annotation in field.Annotations)
{
var typeName = annotation.Type is ClassOrInterfaceType cit ? cit.Name : annotation.Type.ToString();
Console.WriteLine($" - @{typeName}");
}
}
// Check methods
var methods = userClass.Members.OfType<MethodDeclaration>().ToList();
Console.WriteLine($"\n Methods: {methods.Count}");
foreach (var method in methods)
{
Console.WriteLine($" - {method.Name}: {method.Annotations.Count} annotations");
foreach (var annotation in method.Annotations)
{
var typeName = annotation.Type is ClassOrInterfaceType cit ? cit.Name : annotation.Type.ToString();
Console.WriteLine($" - @{typeName}");
}
// Check parameters
foreach (var param in method.Parameters)
{
if (param.Annotations.Count > 0)
{
Console.WriteLine($" Parameter {param.Name}: {param.Annotations.Count} annotations");
foreach (var annotation in param.Annotations)
{
var typeName = annotation.Type is ClassOrInterfaceType cit ? cit.Name : annotation.Type.ToString();
Console.WriteLine($" - @{typeName}");
}
}
}
}
}
}
else
{
Console.WriteLine("✗ Parse failed!");
foreach (var error in result.Errors)
{
Console.WriteLine($" Error: {error.Message} at {error.Line}:{error.Column}");
}
}
}
} }
// Custom visitor for code analysis // Custom visitor for code analysis