From 85fdcff103225150f5a8bc1663aa1b330b2916c8 Mon Sep 17 00:00:00 2001 From: Dave Friedel Date: Wed, 23 Jul 2025 07:29:16 -0400 Subject: [PATCH] Fixed annotation parsing in AstBuilder pulled some hair out on this one --- IronJava.Core/AST/Builders/AstBuilder.cs | 163 ++++++++----- IronJava.Core/AST/Nodes/Expressions.cs | 17 ++ IronJava.Core/AST/Visitors/IJavaVisitor.cs | 2 + IronJava.Core/AST/Visitors/JavaVisitorBase.cs | 2 + IronJava.Core/IronJava.Core.csproj | 2 +- .../Serialization/AstJsonSerializer.cs | 50 ++++ IronJava.Tests/AnnotationParsingTests.cs | 215 ++++++++++++++++++ samples/IronJava.Sample/Program.cs | 99 +++++++- 8 files changed, 492 insertions(+), 58 deletions(-) create mode 100644 IronJava.Tests/AnnotationParsingTests.cs diff --git a/IronJava.Core/AST/Builders/AstBuilder.cs b/IronJava.Core/AST/Builders/AstBuilder.cs index 64030bd..4c143b4 100644 --- a/IronJava.Core/AST/Builders/AstBuilder.cs +++ b/IronJava.Core/AST/Builders/AstBuilder.cs @@ -297,11 +297,24 @@ namespace MarketAlly.IronJava.Core.AST.Builders var annotations = new List(); foreach (var context in contexts) { + // Check if this is directly an annotation context if (context is Java9Parser.AnnotationContext annotationContext) { var annotation = BuildAnnotation(annotationContext); 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; } @@ -312,25 +325,37 @@ namespace MarketAlly.IronJava.Core.AST.Builders TypeReference? type = null; var arguments = new List(); + // Annotations can be NormalAnnotation, MarkerAnnotation, or SingleElementAnnotation if (context.normalAnnotation() != null) { var normalAnn = context.normalAnnotation(); 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(), new List()); + } if (normalAnn.elementValuePairList() != null) { foreach (var pair in normalAnn.elementValuePairList().elementValuePair()) { var name = pair.identifier().GetText(); - var value = BuildExpression(pair.elementValue()); - if (value != null) + var value = BuildElementValue(pair.elementValue()); + + // 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( - GetSourceRange(pair), name, value - )); + var elementText = pair.elementValue().GetText(); + 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(); 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(), new List()); + } // Marker annotations have no arguments } else if (context.singleElementAnnotation() != null) { var singleAnn = context.singleElementAnnotation(); 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(), new List()); + } if (singleAnn.elementValue() != null) { - var value = BuildExpression(singleAnn.elementValue()); - if (value != null) + var value = BuildElementValue(singleAnn.elementValue()); + + // Fallback if BuildElementValue returns null + if (value == null) { - arguments.Add(new AnnotationValueArgument( - location, "value", value - )); + var elementText = singleAnn.elementValue().GetText(); + 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; } + 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(); + + 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 BuildTypeParameters(Java9Parser.TypeParametersContext? context) { if (context == null) return new List(); @@ -414,8 +498,8 @@ namespace MarketAlly.IronJava.Core.AST.Builders Java9Parser.ArrayTypeContext array => BuildArrayType(array), Java9Parser.TypeVariableContext typeVar => BuildTypeVariable(typeVar), Java9Parser.ClassTypeContext classType => BuildClassType(classType), - Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType), Java9Parser.TypeNameContext typeName => BuildTypeName(typeName), + Java9Parser.InterfaceTypeContext interfaceType => BuildInterfaceType(interfaceType), _ => null }; } @@ -980,6 +1064,13 @@ namespace MarketAlly.IronJava.Core.AST.Builders 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 modifiers = BuildModifiers(context.variableModifier()); var annotations = BuildAnnotations(context.variableModifier()); @@ -989,7 +1080,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders var declaratorId = context.variableDeclaratorId(); var name = declaratorId.identifier().GetText(); 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); } @@ -1325,7 +1416,7 @@ namespace MarketAlly.IronJava.Core.AST.Builders if (context.defaultValue() != null) { - defaultValue = BuildExpression(context.defaultValue().elementValue()); + defaultValue = BuildElementValue(context.defaultValue().elementValue()); } return new AnnotationMember(location, name, type, defaultValue); @@ -2590,46 +2681,6 @@ namespace MarketAlly.IronJava.Core.AST.Builders 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(); - - 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 diff --git a/IronJava.Core/AST/Nodes/Expressions.cs b/IronJava.Core/AST/Nodes/Expressions.cs index 43b3bcd..0f3b8d3 100644 --- a/IronJava.Core/AST/Nodes/Expressions.cs +++ b/IronJava.Core/AST/Nodes/Expressions.cs @@ -400,6 +400,23 @@ namespace MarketAlly.IronJava.Core.AST.Nodes public override void Accept(IJavaVisitor visitor) => visitor.VisitArrayInitializer(this); } + /// + /// Represents an annotation used as an expression (in annotation element values). + /// + public class AnnotationExpression : Expression + { + public Annotation Annotation { get; } + + public AnnotationExpression(SourceRange location, Annotation annotation) : base(location) + { + Annotation = annotation; + AddChild(annotation); + } + + public override T Accept(IJavaVisitor visitor) => visitor.VisitAnnotationExpression(this); + public override void Accept(IJavaVisitor visitor) => visitor.VisitAnnotationExpression(this); + } + /// /// Represents a lambda expression. /// diff --git a/IronJava.Core/AST/Visitors/IJavaVisitor.cs b/IronJava.Core/AST/Visitors/IJavaVisitor.cs index 3b5f273..00e13b4 100644 --- a/IronJava.Core/AST/Visitors/IJavaVisitor.cs +++ b/IronJava.Core/AST/Visitors/IJavaVisitor.cs @@ -51,6 +51,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors void VisitNewExpression(NewExpression node); void VisitNewArrayExpression(NewArrayExpression node); void VisitArrayInitializer(ArrayInitializer node); + void VisitAnnotationExpression(AnnotationExpression node); void VisitLambdaExpression(LambdaExpression node); void VisitLambdaParameter(LambdaParameter node); void VisitMethodReferenceExpression(MethodReferenceExpression node); @@ -135,6 +136,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors T VisitNewExpression(NewExpression node); T VisitNewArrayExpression(NewArrayExpression node); T VisitArrayInitializer(ArrayInitializer node); + T VisitAnnotationExpression(AnnotationExpression node); T VisitLambdaExpression(LambdaExpression node); T VisitLambdaParameter(LambdaParameter node); T VisitMethodReferenceExpression(MethodReferenceExpression node); diff --git a/IronJava.Core/AST/Visitors/JavaVisitorBase.cs b/IronJava.Core/AST/Visitors/JavaVisitorBase.cs index 5e7c0c3..d37f1c2 100644 --- a/IronJava.Core/AST/Visitors/JavaVisitorBase.cs +++ b/IronJava.Core/AST/Visitors/JavaVisitorBase.cs @@ -54,6 +54,7 @@ namespace MarketAlly.IronJava.Core.AST.Visitors public virtual void VisitNewExpression(NewExpression node) => DefaultVisit(node); public virtual void VisitNewArrayExpression(NewArrayExpression 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 VisitLambdaParameter(LambdaParameter 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 VisitNewArrayExpression(NewArrayExpression 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 VisitLambdaParameter(LambdaParameter node) => DefaultVisit(node); public virtual T VisitMethodReferenceExpression(MethodReferenceExpression node) => DefaultVisit(node); diff --git a/IronJava.Core/IronJava.Core.csproj b/IronJava.Core/IronJava.Core.csproj index acb634d..820920b 100644 --- a/IronJava.Core/IronJava.Core.csproj +++ b/IronJava.Core/IronJava.Core.csproj @@ -8,7 +8,7 @@ IronJava - 2.1.1 + 2.1.2 David H Friedel Jr MarketAlly IronJava diff --git a/IronJava.Core/Serialization/AstJsonSerializer.cs b/IronJava.Core/Serialization/AstJsonSerializer.cs index 79d6b67..b59fc6b 100644 --- a/IronJava.Core/Serialization/AstJsonSerializer.cs +++ b/IronJava.Core/Serialization/AstJsonSerializer.cs @@ -416,6 +416,22 @@ namespace MarketAlly.IronJava.Core.Serialization return result; } + public override Dictionary VisitAnnotationValueArgument(AnnotationValueArgument node) + { + var result = CreateBaseNode(node); + result["name"] = node.Name; + result["value"] = node.Value.Accept(this); + return result; + } + + public override Dictionary 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 VisitJavaDoc(JavaDoc node) { var result = CreateBaseNode(node); @@ -561,6 +577,13 @@ namespace MarketAlly.IronJava.Core.Serialization return result; } + public override Dictionary VisitAnnotationExpression(AnnotationExpression node) + { + var result = CreateBaseNode(node); + result["annotation"] = node.Annotation.Accept(this); + return result; + } + public override Dictionary VisitMethodReferenceExpression(MethodReferenceExpression node) { var result = CreateBaseNode(node); @@ -747,6 +770,9 @@ namespace MarketAlly.IronJava.Core.Serialization "InstanceOfExpression" => DeserializeInstanceOfExpression(element, location), "NewArrayExpression" => DeserializeNewArrayExpression(element, location), "ArrayInitializer" => DeserializeArrayInitializer(element, location), + "AnnotationExpression" => DeserializeAnnotationExpression(element, location), + "AnnotationValueArgument" => DeserializeAnnotationValueArgument(element, location), + "AnnotationArrayArgument" => DeserializeAnnotationArrayArgument(element, location), "MethodReferenceExpression" => DeserializeMethodReferenceExpression(element, location), "ClassLiteralExpression" => DeserializeClassLiteralExpression(element, location), "LocalVariableStatement" => DeserializeLocalVariableStatement(element, location), @@ -1282,6 +1308,30 @@ namespace MarketAlly.IronJava.Core.Serialization 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(element.GetProperty("values")); + return new AnnotationArrayArgument(location, name, values); + } + private MethodReferenceExpression DeserializeMethodReferenceExpression(JsonElement element, SourceRange location) { var target = Deserialize(element.GetProperty("target")) as Expression ?? throw new JsonException("target is not Expression"); diff --git a/IronJava.Tests/AnnotationParsingTests.cs b/IronJava.Tests/AnnotationParsingTests.cs new file mode 100644 index 0000000..892507a --- /dev/null +++ b/IronJava.Tests/AnnotationParsingTests.cs @@ -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().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(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(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(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().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().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); + } + } +} \ No newline at end of file diff --git a/samples/IronJava.Sample/Program.cs b/samples/IronJava.Sample/Program.cs index 93d6c92..670eff5 100644 --- a/samples/IronJava.Sample/Program.cs +++ b/samples/IronJava.Sample/Program.cs @@ -6,7 +6,7 @@ using MarketAlly.IronJava.Core.AST.Transformation; using MarketAlly.IronJava.Core.AST.Visitors; using MarketAlly.IronJava.Core.Serialization; -namespace MarketAlly.IronJava.Sample +namespace IronJava.Sample { class Program { @@ -19,6 +19,11 @@ namespace MarketAlly.IronJava.Sample TestNestedTypes(); Console.WriteLine("\n===========================\n"); + // Test annotations + TestAnnotations(); + Console.WriteLine("\n===========================\n"); + + // Sample Java code string javaCode = @" package com.example; @@ -296,6 +301,98 @@ public class OuterClass { 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().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().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