From dafcee91d69a4f763beee161fdb2073b40eedcd0 Mon Sep 17 00:00:00 2001 From: Kevin Glynn Date: Wed, 13 Apr 2011 11:45:38 +0200 Subject: [PATCH] Use parseString to generate (java) Collection methods when translating a type that implements ICollection --- .../Collections/Generic/KeyCollection'1.xml | 10 + .../System/Collections/IEnumerator.xml | 20 ++ .../Generic/ICollectionSupport.java | 4 +- .../src/CS2JTranslator/CS2JMain/Fragments.cs | 181 ++++++++++++ .../CS2JTranslator/CS2JTransform/NetMaker.g | 264 +++++++++++++++++- 5 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 CS2JLibrary/NetFramework/System/Collections/IEnumerator.xml create mode 100644 CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Fragments.cs diff --git a/CS2JLibrary/NetFramework/System/Collections/Generic/KeyCollection'1.xml b/CS2JLibrary/NetFramework/System/Collections/Generic/KeyCollection'1.xml index b6d21f6..664f39c 100644 --- a/CS2JLibrary/NetFramework/System/Collections/Generic/KeyCollection'1.xml +++ b/CS2JLibrary/NetFramework/System/Collections/Generic/KeyCollection'1.xml @@ -42,6 +42,16 @@ CopyTo System.Void + + + CS2JNet.JavaSupport.Collections.Generic.EnumeratorSupport + + new EnumeratorSupport*[T]*(${this}.iterator()) + + + GetEnumerator + System.Collections.Generic.IEnumerator*[T]* + diff --git a/CS2JLibrary/NetFramework/System/Collections/IEnumerator.xml b/CS2JLibrary/NetFramework/System/Collections/IEnumerator.xml new file mode 100644 index 0000000..fab0041 --- /dev/null +++ b/CS2JLibrary/NetFramework/System/Collections/IEnumerator.xml @@ -0,0 +1,20 @@ + + + + + CS2JNet.System.Collections.Generic.IEnumeratorSupport + + IEnumeratorSupport + System.Collections.IEnumerator + + System.Object + Current> + ${this:16}.getcurrent() + +jEYi0rdl/kMLr7uBNG0M9LMlbCQ=C/hbBQnMGlWnPvwE61XrMusMlyKT+K792pEYR7D7CnpO7Td1gFgCCfkSdFVZ1axk+sLzebWIQKH8+NFRC6+hlNpViODt46v/300e+fi2Yx8BWhpq5e51zfNAhHIdaUemMqHMHNG5BMOngpFx1yrO/zxHvj7HPJqGBwJV7w8yPtU= diff --git a/CS2JLibrary/src/CS2JNet/System/Collections/Generic/ICollectionSupport.java b/CS2JLibrary/src/CS2JNet/System/Collections/Generic/ICollectionSupport.java index fc1f4ec..cc788b5 100644 --- a/CS2JLibrary/src/CS2JNet/System/Collections/Generic/ICollectionSupport.java +++ b/CS2JLibrary/src/CS2JNet/System/Collections/Generic/ICollectionSupport.java @@ -20,12 +20,14 @@ package CS2JNet.System.Collections.Generic; +import java.util.Collection; + /** * @author kevin.glynn@twigletsoftware.com * */ -public interface ICollectionSupport extends Iterable { +public interface ICollectionSupport extends Collection { public boolean Contains(T x) throws Exception; diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Fragments.cs b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Fragments.cs new file mode 100644 index 0000000..62f779f --- /dev/null +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JMain/Fragments.cs @@ -0,0 +1,181 @@ +/* + Copyright 2010,2011 Kevin Glynn (kevin.glynn@twigletsoftware.com) +*/ + +using System; +using System.Collections.Generic; +using Antlr.Runtime.Tree; + +namespace Twiglet.CS2J.Translator +{ + public class Fragments + { + + public static string GenericCollectorMethods(string T, string S) + { + return genericCollectorMethodsStr.Replace("${T}", T).Replace("${S}", S); + } + + private static string genericCollectorMethodsStr = @" + public Iterator<${T}> iterator() { + Iterator<${T}> ret = null; + try + { + ret = this.GetEnumerator().iterator(); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + } + + public boolean add(${T} el) { + try + { + this.Add(el); + } + catch (Exception e) + { + e.printStackTrace(); + } + return true; + } + + public boolean addAll(Collection c) { + for (${T} el : c) { + this.add(el); + } + return true; + } + + public void clear() { + try + { + this.Clear(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public boolean contains(Object o) { + boolean ret = false; + try + { + ret = this.Contains((${T}) o); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + } + + public boolean containsAll(Collection c) { + boolean ret = true; + for (Object el : c) { + if (!this.contains(el)) { + ret = false; + break; + } + } + return ret; + } + + public boolean isEmpty() { + return this.size() == 0; + } + + public boolean remove(Object o) { + boolean ret = false; + try + { + ret = this.Remove((${T}) o); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + } + + public boolean removeAll(Collection c) { + boolean ret = false; + for (Object el : c) { + ret = ret | this.remove(el); + } + return ret; + } + + public boolean retainAll(Collection c) { + boolean ret = false; + Object[] thisCopy = this.toArray(); + for (Object el : thisCopy) { + if (!c.contains(el)) { + ret = ret | this.remove(el); + } + } + return false; + } + + public int size() { + int ret = -1; + try { + return this.getCount(); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + } + + + public Object[] toArray() { + Object[] ret = new Object[this.size()]; + int i = 0; + for (Object el : this) { + ret[i] = el; + } + return ret; + } + + // NOTE: Moved to after the method name, like C# not Java, to help the poor parser. + public ${S}[] toArray<${S}>(${S}[] a) { + ArrayList<${T}> ret = new ArrayList<${T}>(this.size()); + for (${T} el : this) { + ret.add(el); + } + return ret.toArray(a); + } +"; + + public static string GenIterator = @" + public Iterator<${T}> iterator() { + Iterator<${T}> ret = null; + try + { + ret = this.GetEnumerator().iterator(); + } + catch (Exception e) + { + e.printStackTrace(); + } + return ret; + }"; + + private static Dictionary _fragmentsLibrary = null; + public static Dictionary FragmentsLibrary + { get + { + if (_fragmentsLibrary == null) + { + _fragmentsLibrary = new Dictionary(); + } + return _fragmentsLibrary; + } + } + } +} diff --git a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g index e1bc3e3..8c880df 100644 --- a/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g +++ b/CSharpTranslator/antlr3/src/CS2JTranslator/CS2JTransform/NetMaker.g @@ -26,6 +26,11 @@ scope NSContext { List typeVariables; // all typevariables in all scopes List globalTypeVariables; + + // Does this type implement ICollection? + bool IsGenericICollection; + string GenericICollectionTyVar; + bool IsICollection; } // A scope to keep track of the mapping from variables to types @@ -185,7 +190,6 @@ scope MkNonGeneric { } private ClassRepTemplate dateType = null; - protected ClassRepTemplate DateType { get { if (dateType == null) { @@ -195,6 +199,26 @@ scope MkNonGeneric { } } + private InterfaceRepTemplate iCollectionType = null; + protected InterfaceRepTemplate ICollectionType { + get { + if (iCollectionType == null) { + iCollectionType = (InterfaceRepTemplate)findType("System.Collections.ICollection"); + } + return iCollectionType; + } + } + + private InterfaceRepTemplate genericICollectionType = null; + protected InterfaceRepTemplate GenericICollectionType { + get { + if (genericICollectionType == null) { + genericICollectionType = (InterfaceRepTemplate)findType("System.Collections.Generic.ICollection", new TypeRepTemplate[] {ObjectType}); + } + return genericICollectionType; + } + } + // Map of Java built in types to their object based equivalents Dictionary primitive_to_object_type_map = new Dictionary() { @@ -319,6 +343,7 @@ scope MkNonGeneric { protected int dummyScrutVarCtr = 0; protected int dummyForeachVarCtr = 0; protected int dummyStaticConstructorCatchVarCtr = 0; + protected int dummyTyVarCtr = 0; // It turns out that 'default:' doesn't have to be last in the switch statement, so // we need some jiggery pokery when converting to if-then-else. @@ -518,6 +543,11 @@ scope NSContext, PrimitiveRep, MkNonGeneric; $NSContext::typeVariables = new List(); $NSContext::globalTypeVariables = new List(); + + $NSContext::IsGenericICollection = false; + $NSContext::GenericICollectionTyVar = ""; + $NSContext::IsICollection = false; + }: ^(pkg=PACKAGE ns=PAYLOAD { $NSContext::currentNS = $ns.text; } dec=type_declaration ) -> ^($pkg $ns { mkImports() } $dec); @@ -1630,6 +1660,11 @@ scope NSContext,SymTab; $NSContext::globalNamespaces = new List(((NSContext_scope)$NSContext.ToArray()[1]).globalNamespaces); $NSContext::typeVariables = new List(); $NSContext::globalTypeVariables = new List(((NSContext_scope)$NSContext.ToArray()[1]).globalTypeVariables); + + $NSContext::IsGenericICollection = false; + $NSContext::GenericICollectionTyVar = ""; + $NSContext::IsICollection = false; + $SymTab::symtab = new Dictionary(); } : @@ -1659,6 +1694,12 @@ scope NSContext,SymTab; } $SymTab::symtab["super"] = baseType; } + if ($NSContext::IsICollection) { + Debug(10, $NSContext::currentNS + " is a Collection"); + } + if ($NSContext::IsGenericICollection) { + Debug(10, $NSContext::currentNS + " is a Generic Collection"); + } } class_body magicAnnotation[$modifiers.tree, $identifier.tree, null, $c.token]) -> {$class_implements.hasExtends && $class_implements.extendDotNetType.IsA(AppEnv.Search("System.Attribute", new UnknownRepTemplate("System.Attribute")), AppEnv)}? magicAnnotation @@ -1680,7 +1721,8 @@ class_extend: // If first implements type is a class then convert to extends class_implements returns [bool hasExtends, TypeRepTemplate extendDotNetType]: - class_implement_or_extend { $hasExtends = $class_implement_or_extend.hasExtends; $extendDotNetType = $class_implement_or_extend.extendDotNetType; }class_implement* ; + class_implement_or_extend { $hasExtends = $class_implement_or_extend.hasExtends; $extendDotNetType = $class_implement_or_extend.extendDotNetType; } + class_implement* ; class_implement_or_extend returns [bool hasExtends, TypeRepTemplate extendDotNetType] @init { @@ -1691,18 +1733,36 @@ class_implement_or_extend returns [bool hasExtends, TypeRepTemplate extendDotNet $hasExtends = true; $extendDotNetType = $t.dotNetType; } + if($t.dotNetType.IsA(ICollectionType,AppEnv)) $NSContext::IsICollection = true; + if($t.dotNetType.IsA(GenericICollectionType,AppEnv)) { + $NSContext::IsGenericICollection = true; + $NSContext::GenericICollectionTyVar = $t.dotNetType.TypeParams[0]; + } } ) -> { $t.dotNetType is ClassRepTemplate }? ^(EXTENDS[$i.token, "extends"] type) -> ^($i $t); class_implement: - ^(IMPLEMENTS type) ; + ^(IMPLEMENTS t=type + { + if($t.dotNetType.IsA(ICollectionType,AppEnv)) $NSContext::IsICollection = true; + if($t.dotNetType.IsA(GenericICollectionType,AppEnv)) { + $NSContext::IsGenericICollection = true; + $NSContext::GenericICollectionTyVar = $t.dotNetType.TypeParams[0]; + } + }) ; -interface_type_list: - type (',' type)* ; - -class_body: - '{' class_member_declarations? '}' ; +class_body +@init { + CommonTree collectorNodes = null; + if ($NSContext::IsGenericICollection) { + collectorNodes = this.parseString("class_member_declarations", Fragments.GenericCollectorMethods($NSContext::GenericICollectionTyVar, $NSContext::GenericICollectionTyVar + "__" + dummyTyVarCtr++)); + AddToImports("java.util.Iterator"); + AddToImports("java.util.Collection"); + AddToImports("java.util.ArrayList"); + } +}: + '{' class_member_declarations? '}' -> '{' class_member_declarations? { dupTree(collectorNodes) } '}' ; class_member_declarations: class_member_declaration+ ; @@ -1816,6 +1876,10 @@ scope NSContext,SymTab; $NSContext::globalNamespaces = new List(((NSContext_scope)$NSContext.ToArray()[1]).globalNamespaces); $NSContext::typeVariables = new List(); $NSContext::globalTypeVariables = new List(((NSContext_scope)$NSContext.ToArray()[1]).globalTypeVariables); + + $NSContext::IsGenericICollection = false; + $NSContext::IsICollection = false; + $SymTab::symtab = new Dictionary(); } : @@ -1881,8 +1945,6 @@ interface_declaration: class_extends? interface_body ) ; interface_modifiers: modifier+ ; -interface_base: - ':' interface_type_list ; interface_body: '{' interface_member_declarations? '}' ; interface_member_declarations: @@ -2384,3 +2446,185 @@ magicThrowableType[bool isOn, IToken tok]: magicEventCollectionType[IToken tok, CommonTree type]: -> ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "IEventCollection"] LTHAN[tok, "<"] { dupTree(type) } GT[tok, ">"] ) ; + +// METHOD{ public TYPE{ Iterator < TYPE{ JAVAWRAPPER{ T } } > } kiterator { TYPE{ Iterator < TYPE{ JAVAWRAPPER{ T } } > } ret = null ; try{ { ret = APPLY{ .{ JAVAWRAPPER{ ${this:16}.GetEnumerator() this EXPRESSION{ this } } iterator } } ; } catch{ TYPE{ JAVAWRAPPER{ Exception } } e { APPLY{ .{ e printStackTrace } } ; } } } return{ ret } } Exception } +// + +magicXXGenericIterator[bool isOn, IToken tok, String tyVar] +@init { + if (isOn) AddToImports("java.util.Iterator"); +} +: +-> {isOn}? ^(METHOD[tok, "METHOD"] + PUBLIC[tok, "public"] + ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "Iterator"] LTHAN[tok, "<"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, tyVar]) GT[tok, ">"]) + IDENTIFIER[tok, "iterator"] + OPEN_BRACE[tok, "{"] + // Iterator ret = null; + ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "Iterator"] LTHAN[tok, "<"] ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, tyVar]) GT[tok, ">"]) IDENTIFIER[tok, "ret"] ASSIGN[tok,"="] NULL[tok,"null"] SEMI[tok, ";"] + // try { ret = this.GetEnumerator().iterator(); } catch (Exception e) { e.printstackTrace(); }} + ^(TRY[tok, "try"] + OPEN_BRACE[tok, "{"] + IDENTIFIER[tok, "ret"] ASSIGN[tok,"="] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] THIS[tok, "this"] IDENTIFIER[tok, "GetEnumerator"])) IDENTIFIER[tok,"iterator"])) SEMI[tok,";"] + CLOSE_BRACE[tok, "}"] + ^(CATCH[tok, "catch"] ^(TYPE[tok,"TYPE"] IDENTIFIER[tok, "Exception"]) IDENTIFIER[tok, "e"] + OPEN_BRACE[tok, "{"] + ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] IDENTIFIER[tok, "e"] IDENTIFIER[tok,"printStackTrace"])) SEMI[tok,";"] + CLOSE_BRACE[tok, "}"] + ) + ) + // return ret; + ^(RETURN[tok, "return"] IDENTIFIER[tok, "ret"]) + CLOSE_BRACE[tok, "}"] + ) + +-> +; + +// IDENTIFIER[tok, "ret"] ASSIGN[tok,"="] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] THIS[tok, "this"] IDENTIFIER[tok, "GetEnumerator"])) IDENTIFIER[tok,"iterator"])) SEMI[tok,";"] + +magicGenericIterator[bool isOn, IToken tok, String tyVar] +@init { + if (isOn) AddToImports("java.util.Iterator"); +} +: + magicType[isOn, tok, "Iterator", new string[\] {tyVar}] + n=magicToken[isOn, tok, NULL, "null"] + magicAssignment[isOn, tok, $magicType.tree, "ret", $n.tree] + + thisT=magicToken[isOn, tok, THIS, "this"] + thisEnum=magicDot[isOn, tok, $thisT.tree, "GetEnumerator"] + mkIter=magicDot[isOn, tok, $thisEnum.tree, "iterator"] + tryBody=magicApply[isOn, tok, $mkIter.tree, null] + + magicTryCatch[isOn, tok, $tryBody.tree] + + magicMethod[isOn, tok, "iterator", $magicType.tree, null, $magicTryCatch.tree] + +-> {isOn}? magicMethod +-> +; + +magicIterator[bool isOn, IToken tok] +@init { + if (isOn) AddToImports("java.util.Iterator"); +} +: + magicType[isOn, tok, "Iterator", null] + n=magicToken[isOn, tok, NULL, "null"] + magicAssignment[isOn, tok, $magicType.tree, "ret", $n.tree] +// magicSmotherExceptions[isOn, tok, ] + magicMethod[isOn, tok, "iterator", $magicType.tree, null, $magicAssignment.tree] +-> {isOn}? magicMethod +// ^(METHOD[tok, "METHOD"] +// PUBLIC[tok, "public"] +// ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "Iterator"]) +// IDENTIFIER[tok, "iterator"] +// OPEN_BRACE[tok, "{"] +// // Iterator ret = null; +// ^(TYPE[tok, "TYPE"] IDENTIFIER[tok, "Iterator"]) IDENTIFIER[tok, "ret"] ASSIGN[tok,"="] NULL[tok,"null"] SEMI[tok, ";"] +// // try { ret = this.GetEnumerator().iterator(); } catch (Exception e) { e.printstackTrace(); }} +// ^(TRY[tok, "try"] +// OPEN_BRACE[tok, "{"] +// IDENTIFIER[tok, "ret"] ASSIGN[tok,"="] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] THIS[tok, "this"] IDENTIFIER[tok, "GetEnumerator"])) IDENTIFIER[tok,"iterator"])) SEMI[tok,";"] +// CLOSE_BRACE[tok, "}"] +// ^(CATCH[tok, "catch"] ^(TYPE[tok,"TYPE"] IDENTIFIER[tok, "Exception"]) IDENTIFIER[tok, "e"] +// OPEN_BRACE[tok, "{"] +// ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] IDENTIFIER[tok, "e"] IDENTIFIER[tok,"printStackTrace"])) SEMI[tok,";"] +// CLOSE_BRACE[tok, "}"] +// ) +// ) +// // return ret; +// ^(RETURN[tok, "return"] IDENTIFIER[tok, "ret"]) +// CLOSE_BRACE[tok, "}"] +// ) +// +-> +; + +magicToken[bool isOn, IToken tok, int tokenType, string text] +@init { + CommonTree ret = null; + if (isOn) + ret = (CommonTree)adaptor.Create(tokenType, tok, text); +}: +-> {isOn}? { ret } +-> +; + +// public { } +magicMethod[bool isOn, IToken tok, string name, CommonTree retType, CommonTree args, CommonTree body]: +-> {isOn}? + ^(METHOD[tok, "METHOD"] + PUBLIC[tok, "public"] + { dupTree(retType) } + IDENTIFIER[tok, name] + { dupTree(args) } + OPEN_BRACE[tok, "{"] + { dupTree(body) } + CLOSE_BRACE[tok, "}"] + ) +-> +; + +magicType[bool isOn, IToken tok, string name, string[\] args] +@init { + CommonTree argsTree = null; + if (args != null && args.Length > 0) { + CommonTree root = (CommonTree)adaptor.Nil; + adaptor.AddChild(root, (CommonTree)adaptor.Create(LTHAN, tok, "<")); + foreach (string a in args) { + CommonTree root0 = (CommonTree)adaptor.Nil; + root0 = (CommonTree)adaptor.BecomeRoot((CommonTree)adaptor.Create(TYPE, tok, "TYPE"), root0); + adaptor.AddChild(root0, (CommonTree)adaptor.Create(IDENTIFIER, tok, a)); + adaptor.AddChild(root, root0); + } + adaptor.AddChild(root, (CommonTree)adaptor.Create(GT, tok, ">")); + argsTree = (CommonTree)adaptor.RulePostProcessing(root); + } +} +: +-> {isOn}? + ^(TYPE[tok, "TYPE"] + IDENTIFIER[tok, name] + { dupTree(argsTree) } + ) +-> +; + +// ? = exp ; +magicAssignment[bool isOn, IToken tok, CommonTree type, string name, CommonTree exp]: +-> {isOn}? + { dupTree(type) } + IDENTIFIER[tok, name] + ASSIGN[tok, "="] + { dupTree(exp) } + SEMI[tok, ";"] +-> +; + +magicTryCatch[bool isOn, IToken tok, CommonTree body]: +-> {isOn}? + ^(TRY[tok, "try"] + OPEN_BRACE[tok, "{"] + { dupTree(body) } + CLOSE_BRACE[tok, "}"] + ^(CATCH[tok, "catch"] ^(TYPE[tok,"TYPE"] IDENTIFIER[tok, "Exception"]) IDENTIFIER[tok, "e"] + OPEN_BRACE[tok, "{"] + ^(APPLY[tok, "APPLY"] ^(DOT[tok, "."] IDENTIFIER[tok, "e"] IDENTIFIER[tok,"printStackTrace"])) SEMI[tok,";"] + CLOSE_BRACE[tok, "}"] + ) + ) +-> +; + +magicDot[bool isOn, IToken tok, CommonTree lhs, string rhs]: +-> {isOn}? ^(DOT[tok, "."] { dupTree(lhs) } IDENTIFIER[tok, rhs]) +-> +; + +magicApply[bool isOn, IToken tok, CommonTree methodExp, CommonTree args]: +-> {isOn}? ^(APPLY[tok, "APPLY"] { dupTree(methodExp) } { dupTree(args) }) +-> +; +