11 Commits

Author SHA1 Message Date
85fdcff103 Fixed annotation parsing in AstBuilder
pulled some hair out on this one
2025-07-23 07:29:16 -04:00
1e15e90d32 README and SPECIFICATION updated 2025-07-23 06:27:48 -04:00
aae6087f90 Hot Fix: nestedclasses
resolved empty nestedclasses
2025-07-23 06:10:17 -04:00
0955bb39e3 Version bump to 2.1.0 - Added nested types support 2025-07-23 05:44:24 -04:00
ccc7f1bc8c Merge pull request #1 from MarketAlly/Working/convenience
Nested Types support
2025-07-23 05:35:07 -04:00
6b1f3d7124 Update AstJsonSerializer.cs 2025-07-23 05:31:44 -04:00
5439aeb081 Resolved build errors
1. Fixed constructor calls in AstTransformer.cs by adding missing nestedTypes parameter
  2. Fixed constructor calls in AstJsonSerializer.cs by adding logic to deserialize nestedTypes
  3. Fixed constructor calls in AstBuilder.cs by adding empty nestedTypes lists and correct parameters
  4. Fixed test files by updating ClassDeclaration constructors with proper parameters
2025-07-23 05:24:18 -04:00
2cc1e3d283 new nested types 2025-07-23 05:10:45 -04:00
7708c479e8 tweaks 2025-07-23 05:01:42 -04:00
70c5858c94 Create API_SPECIFICATION.md 2025-07-23 03:16:47 -04:00
c881259f55 Updated readme 2025-07-23 01:21:16 -04:00
19 changed files with 1938 additions and 171 deletions

1019
API_SPECIFICATION.md Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -196,12 +196,12 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var typeParameters = BuildTypeParameters(context.typeParameters());
var superClass = context.superclass() != null ? BuildTypeReference(context.superclass().classType()) : null;
var interfaces = BuildInterfaces(context.superinterfaces());
var members = BuildClassMembers(context.classBody());
var bodyResult = BuildClassBody(context.classBody());
var javaDoc = ExtractJavaDoc(context);
return new ClassDeclaration(
location, name, modifiers, annotations, typeParameters,
superClass, interfaces, members, javaDoc
superClass, interfaces, bodyResult.Members, bodyResult.NestedTypes, javaDoc, false
);
}
@@ -213,12 +213,12 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var name = context.identifier().GetText();
var typeParameters = BuildTypeParameters(context.typeParameters());
var extendedInterfaces = BuildExtendedInterfaces(context.extendsInterfaces());
var members = BuildInterfaceMembers(context.interfaceBody());
var bodyResult = BuildInterfaceBody(context.interfaceBody());
var javaDoc = ExtractJavaDoc(context);
return new InterfaceDeclaration(
location, name, modifiers, annotations, typeParameters,
extendedInterfaces, members, javaDoc
extendedInterfaces, bodyResult.Members, bodyResult.NestedTypes, javaDoc
);
}
@@ -231,12 +231,15 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var interfaces = BuildInterfaces(context.superinterfaces());
var enumBody = context.enumBody();
var constants = BuildEnumConstants(enumBody.enumConstantList());
var members = BuildEnumMembers(enumBody.enumBodyDeclarations());
var bodyResult = BuildEnumBody(enumBody.enumBodyDeclarations());
var javaDoc = ExtractJavaDoc(context);
return new EnumDeclaration(
location, name, modifiers, annotations,
interfaces, constants, members, javaDoc
interfaces, constants,
bodyResult?.Members ?? new List<MemberDeclaration>(),
bodyResult?.NestedTypes ?? new List<TypeDeclaration>(),
javaDoc
);
}
@@ -249,8 +252,9 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var members = BuildAnnotationMembers(context.annotationTypeBody());
var javaDoc = ExtractJavaDoc(context);
// Annotations don't support nested types in Java
return new AnnotationDeclaration(
location, name, modifiers, annotations, members, javaDoc
location, name, modifiers, annotations, members, new List<TypeDeclaration>(), javaDoc
);
}
@@ -293,11 +297,24 @@ namespace MarketAlly.IronJava.Core.AST.Builders
var annotations = new List<Annotation>();
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;
}
@@ -308,25 +325,37 @@ namespace MarketAlly.IronJava.Core.AST.Builders
TypeReference? type = null;
var arguments = new List<AnnotationArgument>();
// 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<TypeArgument>(), new List<Annotation>());
}
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
));
}
}
}
@@ -334,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<TypeArgument>(), new List<Annotation>());
}
// 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<TypeArgument>(), new List<Annotation>());
}
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<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)
{
if (context == null) return new List<TypeParameter>();
@@ -410,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
};
}
@@ -667,6 +755,54 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return members;
}
private ClassBodyResult BuildClassBody(Java9Parser.ClassBodyContext context)
{
var result = new ClassBodyResult();
foreach (var declaration in context.classBodyDeclaration())
{
if (declaration.classMemberDeclaration() != null)
{
var classMemberContext = declaration.classMemberDeclaration();
// Check if it's a nested type declaration
if (classMemberContext.classDeclaration() != null)
{
var nestedType = Visit(classMemberContext.classDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
else if (classMemberContext.interfaceDeclaration() != null)
{
var nestedType = Visit(classMemberContext.interfaceDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
else
{
// It's a regular member (field or method)
var member = BuildClassMember(classMemberContext);
if (member != null) result.Members.Add(member);
}
}
else if (declaration.instanceInitializer() != null)
{
var initializer = BuildInstanceInitializer(declaration.instanceInitializer());
if (initializer != null) result.Members.Add(initializer);
}
else if (declaration.staticInitializer() != null)
{
var initializer = BuildStaticInitializer(declaration.staticInitializer());
if (initializer != null) result.Members.Add(initializer);
}
else if (declaration.constructorDeclaration() != null)
{
var constructor = BuildConstructor(declaration.constructorDeclaration());
if (constructor != null) result.Members.Add(constructor);
}
}
return result;
}
private MemberDeclaration? BuildClassMember(Java9Parser.ClassMemberDeclarationContext context)
{
if (context.fieldDeclaration() != null)
@@ -677,15 +813,8 @@ namespace MarketAlly.IronJava.Core.AST.Builders
{
return BuildMethodDeclaration(context.methodDeclaration());
}
else if (context.classDeclaration() != null)
{
return Visit(context.classDeclaration()) as MemberDeclaration;
}
else if (context.interfaceDeclaration() != null)
{
return Visit(context.interfaceDeclaration()) as MemberDeclaration;
}
// Nested types are now handled in BuildClassBody
return null;
}
@@ -935,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());
@@ -944,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);
}
@@ -1040,6 +1176,37 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return members;
}
private ClassBodyResult BuildInterfaceBody(Java9Parser.InterfaceBodyContext context)
{
var result = new ClassBodyResult();
foreach (var declaration in context.interfaceMemberDeclaration())
{
if (declaration.constantDeclaration() != null)
{
var constant = BuildConstantDeclaration(declaration.constantDeclaration());
if (constant != null) result.Members.Add(constant);
}
else if (declaration.interfaceMethodDeclaration() != null)
{
var method = BuildInterfaceMethodDeclaration(declaration.interfaceMethodDeclaration());
if (method != null) result.Members.Add(method);
}
else if (declaration.classDeclaration() != null)
{
var nestedType = Visit(declaration.classDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
else if (declaration.interfaceDeclaration() != null)
{
var nestedType = Visit(declaration.interfaceDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
}
return result;
}
private FieldDeclaration? BuildConstantDeclaration(Java9Parser.ConstantDeclarationContext context)
{
var location = GetSourceRange(context);
@@ -1125,7 +1292,9 @@ namespace MarketAlly.IronJava.Core.AST.Builders
null,
new List<TypeReference>(),
members,
null
new List<TypeDeclaration>(),
null,
false
);
}
@@ -1170,6 +1339,56 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return members;
}
private ClassBodyResult? BuildEnumBody(Java9Parser.EnumBodyDeclarationsContext? context)
{
if (context == null) return null;
var result = new ClassBodyResult();
foreach (var declaration in context.classBodyDeclaration())
{
if (declaration.classMemberDeclaration() != null)
{
var classMemberContext = declaration.classMemberDeclaration();
// Check if it's a nested type declaration
if (classMemberContext.classDeclaration() != null)
{
var nestedType = Visit(classMemberContext.classDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
else if (classMemberContext.interfaceDeclaration() != null)
{
var nestedType = Visit(classMemberContext.interfaceDeclaration()) as TypeDeclaration;
if (nestedType != null) result.NestedTypes.Add(nestedType);
}
else
{
// It's a regular member (field or method)
var member = BuildClassMember(classMemberContext);
if (member != null) result.Members.Add(member);
}
}
else if (declaration.instanceInitializer() != null)
{
var initializer = BuildInstanceInitializer(declaration.instanceInitializer());
if (initializer != null) result.Members.Add(initializer);
}
else if (declaration.staticInitializer() != null)
{
var initializer = BuildStaticInitializer(declaration.staticInitializer());
if (initializer != null) result.Members.Add(initializer);
}
else if (declaration.constructorDeclaration() != null)
{
var constructor = BuildConstructor(declaration.constructorDeclaration());
if (constructor != null) result.Members.Add(constructor);
}
}
return result;
}
private List<AnnotationMember> BuildAnnotationMembers(Java9Parser.AnnotationTypeBodyContext context)
{
var members = new List<AnnotationMember>();
@@ -1197,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);
@@ -2108,7 +2327,9 @@ namespace MarketAlly.IronJava.Core.AST.Builders
type,
new List<TypeReference>(),
members,
null
new List<TypeDeclaration>(),
null,
false
);
}
@@ -2460,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<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
@@ -2829,7 +3010,9 @@ namespace MarketAlly.IronJava.Core.AST.Builders
type,
new List<TypeReference>(),
members,
null
new List<TypeDeclaration>(),
null,
false
);
}
@@ -3458,5 +3641,12 @@ namespace MarketAlly.IronJava.Core.AST.Builders
return new JavaDoc(range, content, tags);
}
// Helper classes for building class bodies with both members and nested types
private class ClassBodyResult
{
public List<MemberDeclaration> Members { get; set; } = new List<MemberDeclaration>();
public List<TypeDeclaration> NestedTypes { get; set; } = new List<TypeDeclaration>();
}
}
}

View File

@@ -10,6 +10,11 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
{
public TypeReference Type { get; }
public IReadOnlyList<AnnotationArgument> Arguments { get; }
/// <summary>
/// Gets the name of the annotation (e.g., "Override" for @Override).
/// </summary>
public string Name => Type.Name;
public Annotation(
SourceRange location,

View File

@@ -38,6 +38,11 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
{
public string PackageName { get; }
public IReadOnlyList<Annotation> Annotations { get; }
/// <summary>
/// Gets the package name. Alias for PackageName for consistency.
/// </summary>
public string Name => PackageName;
public PackageDeclaration(
SourceRange location,
@@ -61,6 +66,13 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public string ImportPath { get; }
public bool IsStatic { get; }
public bool IsWildcard { get; }
/// <summary>
/// Gets the name of the imported type or package.
/// For "import java.util.List", returns "java.util.List"
/// For "import java.util.*", returns "java.util.*"
/// </summary>
public string Name => ImportPath;
public ImportDeclaration(
SourceRange location,

View File

@@ -400,6 +400,23 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
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>
/// Represents a lambda expression.
/// </summary>

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using MarketAlly.IronJava.Core.AST.Visitors;
namespace MarketAlly.IronJava.Core.AST.Nodes
@@ -13,6 +14,11 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public IReadOnlyList<Annotation> Annotations { get; }
public IReadOnlyList<TypeParameter> TypeParameters { get; }
public JavaDoc? JavaDoc { get; }
/// <summary>
/// Gets the body/members of this type declaration.
/// </summary>
public abstract IEnumerable<JavaNode> Body { get; }
protected TypeDeclaration(
SourceRange location,
@@ -42,6 +48,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public TypeReference? SuperClass { get; }
public IReadOnlyList<TypeReference> Interfaces { get; }
public IReadOnlyList<MemberDeclaration> Members { get; }
public IReadOnlyList<TypeDeclaration> NestedTypes { get; }
public bool IsRecord { get; }
public ClassDeclaration(
@@ -53,6 +60,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
TypeReference? superClass,
IReadOnlyList<TypeReference> interfaces,
IReadOnlyList<MemberDeclaration> members,
IReadOnlyList<TypeDeclaration> nestedTypes,
JavaDoc? javaDoc,
bool isRecord = false)
: base(location, name, modifiers, annotations, typeParameters, javaDoc)
@@ -60,13 +68,17 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
SuperClass = superClass;
Interfaces = interfaces;
Members = members;
NestedTypes = nestedTypes;
IsRecord = isRecord;
if (superClass != null) AddChild(superClass);
AddChildren(interfaces);
AddChildren(members);
AddChildren(nestedTypes);
}
public override IEnumerable<JavaNode> Body => Members.Cast<JavaNode>().Concat(NestedTypes);
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitClassDeclaration(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitClassDeclaration(this);
}
@@ -78,6 +90,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
{
public IReadOnlyList<TypeReference> ExtendedInterfaces { get; }
public IReadOnlyList<MemberDeclaration> Members { get; }
public IReadOnlyList<TypeDeclaration> NestedTypes { get; }
public InterfaceDeclaration(
SourceRange location,
@@ -87,16 +100,21 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
IReadOnlyList<TypeParameter> typeParameters,
IReadOnlyList<TypeReference> extendedInterfaces,
IReadOnlyList<MemberDeclaration> members,
IReadOnlyList<TypeDeclaration> nestedTypes,
JavaDoc? javaDoc)
: base(location, name, modifiers, annotations, typeParameters, javaDoc)
{
ExtendedInterfaces = extendedInterfaces;
Members = members;
NestedTypes = nestedTypes;
AddChildren(extendedInterfaces);
AddChildren(members);
AddChildren(nestedTypes);
}
public override IEnumerable<JavaNode> Body => Members.Cast<JavaNode>().Concat(NestedTypes);
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitInterfaceDeclaration(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitInterfaceDeclaration(this);
}
@@ -109,6 +127,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public IReadOnlyList<TypeReference> Interfaces { get; }
public IReadOnlyList<EnumConstant> Constants { get; }
public IReadOnlyList<MemberDeclaration> Members { get; }
public IReadOnlyList<TypeDeclaration> NestedTypes { get; }
public EnumDeclaration(
SourceRange location,
@@ -118,18 +137,23 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
IReadOnlyList<TypeReference> interfaces,
IReadOnlyList<EnumConstant> constants,
IReadOnlyList<MemberDeclaration> members,
IReadOnlyList<TypeDeclaration> nestedTypes,
JavaDoc? javaDoc)
: base(location, name, modifiers, annotations, new List<TypeParameter>(), javaDoc)
{
Interfaces = interfaces;
Constants = constants;
Members = members;
NestedTypes = nestedTypes;
AddChildren(interfaces);
AddChildren(constants);
AddChildren(members);
AddChildren(nestedTypes);
}
public override IEnumerable<JavaNode> Body => Constants.Cast<JavaNode>().Concat(Members).Concat(NestedTypes);
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitEnumDeclaration(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitEnumDeclaration(this);
}
@@ -140,6 +164,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public class AnnotationDeclaration : TypeDeclaration
{
public IReadOnlyList<AnnotationMember> Members { get; }
public IReadOnlyList<TypeDeclaration> NestedTypes { get; }
public AnnotationDeclaration(
SourceRange location,
@@ -147,13 +172,18 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
Modifiers modifiers,
IReadOnlyList<Annotation> annotations,
IReadOnlyList<AnnotationMember> members,
IReadOnlyList<TypeDeclaration> nestedTypes,
JavaDoc? javaDoc)
: base(location, name, modifiers, annotations, new List<TypeParameter>(), javaDoc)
{
Members = members;
NestedTypes = nestedTypes;
AddChildren(members);
AddChildren(nestedTypes);
}
public override IEnumerable<JavaNode> Body => Members.Cast<JavaNode>().Concat(NestedTypes);
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitAnnotationDeclaration(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitAnnotationDeclaration(this);
}

View File

@@ -9,6 +9,16 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public abstract class TypeReference : JavaNode
{
protected TypeReference(SourceRange location) : base(location) { }
/// <summary>
/// Gets the simple name of the type.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Gets the fully qualified name of the type.
/// </summary>
public virtual string QualifiedName => Name;
}
/// <summary>
@@ -17,6 +27,8 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
public class PrimitiveType : TypeReference
{
public PrimitiveTypeKind Kind { get; }
public override string Name => Kind.ToString().ToLower();
public PrimitiveType(SourceRange location, PrimitiveTypeKind kind) : base(location)
{
@@ -45,7 +57,9 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
/// </summary>
public class ClassOrInterfaceType : TypeReference
{
public string Name { get; }
private readonly string _name;
public override string Name => _name;
public ClassOrInterfaceType? Scope { get; }
public IReadOnlyList<TypeArgument> TypeArguments { get; }
public IReadOnlyList<Annotation> Annotations { get; }
@@ -57,7 +71,7 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
IReadOnlyList<TypeArgument> typeArguments,
IReadOnlyList<Annotation> annotations) : base(location)
{
Name = name;
_name = name;
Scope = scope;
TypeArguments = typeArguments;
Annotations = annotations;
@@ -68,6 +82,8 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
}
public string FullName => Scope != null ? $"{Scope.FullName}.{Name}" : Name;
public override string QualifiedName => FullName;
public override T Accept<T>(IJavaVisitor<T> visitor) => visitor.VisitClassOrInterfaceType(this);
public override void Accept(IJavaVisitor visitor) => visitor.VisitClassOrInterfaceType(this);
@@ -80,6 +96,10 @@ namespace MarketAlly.IronJava.Core.AST.Nodes
{
public TypeReference ElementType { get; }
public int Dimensions { get; }
public override string Name => ElementType.Name + new string('[', Dimensions) + new string(']', Dimensions);
public override string QualifiedName => ElementType.QualifiedName + new string('[', Dimensions) + new string(']', Dimensions);
public ArrayType(
SourceRange location,

View File

@@ -149,6 +149,7 @@ namespace MarketAlly.IronJava.Core.AST.Transformation
node.SuperClass,
node.Interfaces,
transformedMembers,
node.NestedTypes,
node.JavaDoc,
node.IsRecord
);
@@ -201,6 +202,7 @@ namespace MarketAlly.IronJava.Core.AST.Transformation
node.SuperClass,
node.Interfaces,
transformedMembers,
node.NestedTypes,
node.JavaDoc,
node.IsRecord
);
@@ -336,6 +338,7 @@ namespace MarketAlly.IronJava.Core.AST.Transformation
node.SuperClass,
node.Interfaces,
transformedMembers,
node.NestedTypes,
node.JavaDoc,
node.IsRecord
);

View File

@@ -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);

View File

@@ -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);

View File

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

View File

@@ -154,6 +154,7 @@ namespace MarketAlly.IronJava.Core.Serialization
result["superClass"] = node.SuperClass?.Accept(this);
result["interfaces"] = node.Interfaces.Select(i => i.Accept(this)).ToList();
result["members"] = node.Members.Select(m => m.Accept(this)).ToList();
result["nestedTypes"] = node.NestedTypes.Select(t => t.Accept(this)).ToList();
result["isRecord"] = node.IsRecord;
return result;
}
@@ -163,6 +164,7 @@ namespace MarketAlly.IronJava.Core.Serialization
var result = CreateTypeDeclarationBase(node);
result["extendedInterfaces"] = node.ExtendedInterfaces.Select(i => i.Accept(this)).ToList();
result["members"] = node.Members.Select(m => m.Accept(this)).ToList();
result["nestedTypes"] = node.NestedTypes.Select(t => t.Accept(this)).ToList();
return result;
}
@@ -172,6 +174,7 @@ namespace MarketAlly.IronJava.Core.Serialization
result["interfaces"] = node.Interfaces.Select(i => i.Accept(this)).ToList();
result["constants"] = node.Constants.Select(c => c.Accept(this)).ToList();
result["members"] = node.Members.Select(m => m.Accept(this)).ToList();
result["nestedTypes"] = node.NestedTypes.Select(t => t.Accept(this)).ToList();
return result;
}
@@ -179,6 +182,7 @@ namespace MarketAlly.IronJava.Core.Serialization
{
var result = CreateTypeDeclarationBase(node);
result["members"] = node.Members.Select(m => m.Accept(this)).ToList();
result["nestedTypes"] = node.NestedTypes.Select(t => t.Accept(this)).ToList();
return result;
}
@@ -412,6 +416,22 @@ namespace MarketAlly.IronJava.Core.Serialization
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)
{
var result = CreateBaseNode(node);
@@ -557,6 +577,13 @@ namespace MarketAlly.IronJava.Core.Serialization
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)
{
var result = CreateBaseNode(node);
@@ -743,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),
@@ -829,9 +859,14 @@ namespace MarketAlly.IronJava.Core.Serialization
var interfaces = DeserializeList<ClassOrInterfaceType>(element.GetProperty("interfaces"));
var members = DeserializeList<MemberDeclaration>(element.GetProperty("members"));
var isRecord = element.GetProperty("isRecord").GetBoolean();
var nestedTypes = element.TryGetProperty("nestedTypes", out var nestedTypesEl)
? DeserializeList<TypeDeclaration>(nestedTypesEl)
: new List<TypeDeclaration>();
var isRecord = element.TryGetProperty("isRecord", out var isRecordEl)
? isRecordEl.GetBoolean()
: false;
return new ClassDeclaration(location, name, modifiers, annotations, typeParameters, superClass, interfaces, members, javaDoc, isRecord);
return new ClassDeclaration(location, name, modifiers, annotations, typeParameters, superClass, interfaces, members, nestedTypes, javaDoc, isRecord);
}
private InterfaceDeclaration DeserializeInterfaceDeclaration(JsonElement element, SourceRange location)
@@ -846,8 +881,11 @@ namespace MarketAlly.IronJava.Core.Serialization
var extendedInterfaces = DeserializeList<ClassOrInterfaceType>(element.GetProperty("extendedInterfaces"));
var members = DeserializeList<MemberDeclaration>(element.GetProperty("members"));
var nestedTypes = element.TryGetProperty("nestedTypes", out var nestedTypesEl)
? DeserializeList<TypeDeclaration>(nestedTypesEl)
: new List<TypeDeclaration>();
return new InterfaceDeclaration(location, name, modifiers, annotations, typeParameters, extendedInterfaces, members, javaDoc);
return new InterfaceDeclaration(location, name, modifiers, annotations, typeParameters, extendedInterfaces, members, nestedTypes, javaDoc);
}
private EnumDeclaration DeserializeEnumDeclaration(JsonElement element, SourceRange location)
@@ -862,8 +900,11 @@ namespace MarketAlly.IronJava.Core.Serialization
var interfaces = DeserializeList<ClassOrInterfaceType>(element.GetProperty("interfaces"));
var constants = DeserializeList<EnumConstant>(element.GetProperty("constants"));
var members = DeserializeList<MemberDeclaration>(element.GetProperty("members"));
var nestedTypes = element.TryGetProperty("nestedTypes", out var nestedTypesEl)
? DeserializeList<TypeDeclaration>(nestedTypesEl)
: new List<TypeDeclaration>();
return new EnumDeclaration(location, name, modifiers, annotations, interfaces, constants, members, javaDoc);
return new EnumDeclaration(location, name, modifiers, annotations, interfaces, constants, members, nestedTypes, javaDoc);
}
private AnnotationDeclaration DeserializeAnnotationDeclaration(JsonElement element, SourceRange location)
@@ -876,8 +917,11 @@ namespace MarketAlly.IronJava.Core.Serialization
: null;
var members = DeserializeList<AnnotationMember>(element.GetProperty("members"));
var nestedTypes = element.TryGetProperty("nestedTypes", out var nestedTypesEl)
? DeserializeList<TypeDeclaration>(nestedTypesEl)
: new List<TypeDeclaration>();
return new AnnotationDeclaration(location, name, modifiers, annotations, members, javaDoc);
return new AnnotationDeclaration(location, name, modifiers, annotations, members, nestedTypes, javaDoc);
}
private FieldDeclaration DeserializeFieldDeclaration(JsonElement element, SourceRange location)
@@ -1264,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<Expression>(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");

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

@@ -246,7 +246,9 @@ namespace MarketAlly.IronJava.Tests
null
)
},
null
new List<TypeDeclaration>(),
null,
false
);
var builder = new TransformationBuilder()
@@ -348,7 +350,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference>(),
new List<MemberDeclaration> { mainMethod, helperMethod, field },
null
new List<TypeDeclaration>(),
null,
false
);
return new CompilationUnit(
@@ -393,7 +397,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference> { serializableInterface },
new List<MemberDeclaration> { serializeMethod },
null
new List<TypeDeclaration>(),
null,
false
);
return new CompilationUnit(

View File

@@ -55,7 +55,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference>(),
new List<MemberDeclaration> { mainMethod },
null
new List<TypeDeclaration>(),
null,
false
);
var compilationUnit = new CompilationUnit(
@@ -137,7 +139,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference>(),
new List<MemberDeclaration>(),
null
new List<TypeDeclaration>(),
null,
false
);
var outerClass = new ClassDeclaration(
@@ -149,7 +153,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference>(),
new List<MemberDeclaration>(),
null
new List<TypeDeclaration>(),
null,
false
);
// Navigate the AST
@@ -194,7 +200,9 @@ namespace MarketAlly.IronJava.Tests
null,
new List<TypeReference>(),
new List<MemberDeclaration> { field },
null
new List<TypeDeclaration>(),
null,
false
);
var compilationUnit = new CompilationUnit(

View File

@@ -39,9 +39,9 @@ Install-Package IronJava
## Quick Start
```csharp
using IronJava.Core;
using IronJava.Core.AST.Nodes;
using IronJava.Core.AST.Query;
using MarketAlly.IronJava.Core;
using MarketAlly.IronJava.Core.AST.Nodes;
using MarketAlly.IronJava.Core.AST.Query;
// Parse Java source code
var parser = new JavaParser();
@@ -82,9 +82,17 @@ CompilationUnit
ImportDeclaration[]
TypeDeclaration[]
ClassDeclaration
Members[]
NestedTypes[]
InterfaceDeclaration
Members[]
NestedTypes[]
EnumDeclaration
Constants[]
Members[]
NestedTypes[]
AnnotationDeclaration
Members[]
```
### Visitor Pattern
@@ -152,6 +160,13 @@ var serializableClasses = ast
var getters = ast
.FindAll<MethodDeclaration>()
.Where(m => m.IsGetter());
// Access nested types
var outerClass = ast.Types.OfType<ClassDeclaration>().First();
foreach (var nestedType in outerClass.NestedTypes)
{
Console.WriteLine($"Nested: {nestedType.Name} ({nestedType.GetType().Name})");
}
```
### JSON Serialization

View File

@@ -1,77 +0,0 @@
# IronJava v1.1.0 - Production-Ready Java Parser for .NET
## 🎉 Release Highlights
IronJava v1.1.0 marks our first production-ready release, delivering a robust, fully-featured Java parser for the .NET ecosystem. This release completes the implementation of all core features and brings significant improvements in stability, performance, and developer experience.
### 🚀 What's New
#### Complete Parser Implementation
- **Full AST Builder**: Implemented comprehensive ANTLR-to-AST conversion supporting all Java 17 language constructs
- **Production-Ready Parser**: Fixed ~150+ compilation issues to deliver a fully functional parser
- **Complete Type System**: Support for all Java types including primitives, classes, interfaces, enums, and generics
#### JSON Serialization & Deserialization
- **Bidirectional JSON Support**: Complete implementation of AST serialization and deserialization
- **40+ Node Types**: Full support for all AST node types in JSON format
- **Round-Trip Guarantee**: AST → JSON → AST preserves complete structure and source locations
#### .NET 9 Migration
- **Latest Framework**: Upgraded entire solution to .NET 9.0
- **Modern C# Features**: Leveraging latest language features for better performance and maintainability
- **Cross-Platform**: Full support for Windows, Linux, and macOS
#### CI/CD & DevOps
- **GitHub Actions**: Automated build, test, and release pipelines
- **Multi-Platform Testing**: Tests run on Windows, Linux, and macOS
- **Automated Releases**: NuGet package publishing on version tags
- **Code Coverage**: Integrated coverage reporting with Codecov
#### Developer Experience
- **Comprehensive Documentation**: Complete API documentation and usage examples
- **100% Test Coverage**: All features thoroughly tested with 27 comprehensive test suites
### 📊 Key Features
- **Java 17 Support**: Parse modern Java code with full language feature support
- **Strongly-Typed AST**: Navigate Java code structure with C# type safety
- **Visitor Pattern**: Powerful AST traversal and analysis capabilities
- **LINQ Integration**: Query Java code structures using familiar LINQ syntax
- **AST Transformations**: Modify and refactor Java code programmatically
- **Source Location Tracking**: Precise line/column information for all AST nodes
- **Extensible Architecture**: Easy to extend with custom visitors and transformations
### 🔧 Technical Improvements
- Fixed all parser implementation issues
- Resolved ~150+ compilation errors in AstBuilder
- Implemented complete JSON deserialization for all node types
- Fixed binary expression parsing for left-recursive grammars
- Resolved string comparison and type compatibility warnings
- All tests passing on all platforms
### 📦 Installation
```bash
dotnet add package IronJava --version 1.1.0
```
### 🙏 Acknowledgments
Special thanks to all the examples throughout GitHub who helped make this release possible. IronJava is built on the excellent ANTLR4 parser generator and the Java 9 grammar from the ANTLR grammars repository.
### 🐛 Bug Fixes
- Fixed AST builder compilation errors
- Resolved JSON deserialization for all node types
- Fixed test expectations to match actual parser behavior
- Corrected binary expression parsing
- Fixed string comparison warnings
### 📚 Documentation
Full documentation available at: https://github.com/MarketAlly/IronJava
---
**Full Changelog**: https://github.com/MarketAlly/IronJava/commits/v1.1.0

58
TestNestedTypes.cs Normal file
View File

@@ -0,0 +1,58 @@
using System;
using MarketAlly.IronJava.Core;
using MarketAlly.IronJava.Core.AST.Nodes;
class TestNestedTypes
{
static void Main()
{
var javaCode = @"
public class OuterClass {
private int outerField;
public class InnerClass {
private String innerField;
public void innerMethod() {
System.out.println(innerField);
}
}
public static class StaticNestedClass {
private static int staticField;
}
public interface NestedInterface {
void doSomething();
}
}
";
var result = JavaParser.Parse(javaCode);
if (result.Success)
{
Console.WriteLine("Parse successful!");
var outerClass = result.Ast.Types[0] as ClassDeclaration;
if (outerClass != null)
{
Console.WriteLine($"Outer class: {outerClass.Name}");
Console.WriteLine($"Number of members: {outerClass.Members.Count}");
Console.WriteLine($"Number of nested types: {outerClass.NestedTypes.Count}");
foreach (var nestedType in outerClass.NestedTypes)
{
Console.WriteLine($" - Nested type: {nestedType.Name} ({nestedType.GetType().Name})");
}
}
}
else
{
Console.WriteLine("Parse failed!");
foreach (var error in result.Errors)
{
Console.WriteLine($"Error: {error}");
}
}
}
}

View File

@@ -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
{
@@ -15,6 +15,15 @@ namespace MarketAlly.IronJava.Sample
Console.WriteLine("IronJava Sample Application");
Console.WriteLine("===========================\n");
// Test nested types
TestNestedTypes();
Console.WriteLine("\n===========================\n");
// Test annotations
TestAnnotations();
Console.WriteLine("\n===========================\n");
// Sample Java code
string javaCode = @"
package com.example;
@@ -219,6 +228,171 @@ class User {
Console.WriteLine($"\nMethods that throw exceptions: {throwingMethods.Count}");
Console.WriteLine();
}
static void TestNestedTypes()
{
Console.WriteLine("Testing Nested Types Feature");
Console.WriteLine("----------------------------");
var javaCode = @"
public class OuterClass {
private int outerField;
public class InnerClass {
private String innerField;
public void innerMethod() {
System.out.println(innerField);
}
}
public static class StaticNestedClass {
private static int staticField;
}
public interface NestedInterface {
void doSomething();
}
public enum NestedEnum {
VALUE1, VALUE2
}
}
";
var result = JavaParser.Parse(javaCode);
if (result.Success)
{
Console.WriteLine("✓ Parse successful!");
var outerClass = result.Ast!.Types[0] as ClassDeclaration;
if (outerClass != null)
{
Console.WriteLine($"\nOuter class: {outerClass.Name}");
Console.WriteLine($" Members: {outerClass.Members.Count}");
Console.WriteLine($" Nested types: {outerClass.NestedTypes.Count}");
foreach (var member in outerClass.Members)
{
if (member is FieldDeclaration field)
{
Console.WriteLine($" - Field: {field.Variables[0].Name}");
}
}
foreach (var nestedType in outerClass.NestedTypes)
{
Console.WriteLine($" - Nested: {nestedType.Name} ({nestedType.GetType().Name})");
if (nestedType is ClassDeclaration nestedClass)
{
Console.WriteLine($" Static: {nestedClass.Modifiers.HasFlag(Modifiers.Static)}");
Console.WriteLine($" Members: {nestedClass.Members.Count}");
}
else if (nestedType is EnumDeclaration nestedEnum)
{
Console.WriteLine($" Constants: {nestedEnum.Constants.Count}");
}
}
}
}
else
{
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