Saturday, November 04, 2017

Sharing code between CSharp and Java

There are many ways we can share code between C# and Java, for example:

  • Write the shared code in a lightweight scripting language, and run a VM in C# and Java. Lua is one of this type.
  • Write the shared code in a language than can translate both to C# and java, such as Haxe.
  • Write the shared code in Java and use tools to translate to C#.
Sometimes, we already has some existing code in Java, so we are only left with the third option. Besides, even if there is no existing code, Java to C# is an attractive option thanks to Java's rich & friendly development environment.

Recently, I have to share some common logic between Java and C# . To be more specific, there is a Java based game server which are connected by an existing Flash client, what we want to do is to add another Unity client while preserving both Java and AS3's logic.  After a lot of discussion, we decided to adopt the following strategy:

  • First, refactor and extract the shared logic in the Java server to a standalone module which shouldn't be depend on any other java library except Java's buildin library.
  • Second, translated the extracted module from java to C# use a tool such as sharpen.
We choose sharpen as our translating tool, because:
  • first of all, it's open source and can be modified if necessary
  • then, there are some successful application of it already, such as ngit.
There are lots of sharpen version on the internet, and I used the mono version with some multi dimension array fixed from anders9ustafsson's version, and pushed final result to my folk.

The document is very hard to find, so I made a backup in sharpen-doc

Here are some other resources I found usefull:
  • Build help: https://github.com/mono/sharpen
  • Example configuration: https://github.com/ydanila/sharpen_imazen_config
  • Sharpen C# utility classes: https://github.com/mono/ngit/tree/master/Sharpen/Sharpen
  • Another sharpen blog: http://pauldb-blog.tumblr.com/post/14916717048/a-guide-to-sharpen-a-great-tool-for-converting
When running sharpen,do set -Dfile.encoding=UTF-8 option, or some file won't compile successfully.

Command line Arguments:

ArgumentUsage
-pascalCaseConvert Java identifiers to Pascal case
-pascalCase+Convert Java indentifiers and package names (namespaces) to Pascal case
-cpAdds a new entry to classpath:

-srcFolderAdds a new source folder for sharpening
-nativeTypeSystemMap java classes to .NET classes with a similar functionality. For example: java.lang.Class - System.Type
-nativeInterfacesAdds an "I" in front of the interface name
-organizeUsingsAdds "using" for the types used
-fullyQualifyConverts to a fully-qualified name:
-fullyQualify File
-namespaceMappingMaps a java package name to a .NET namespace. For example:
-namespaceMapping com.db4o Db4objects.Db4o
-methodMappingMaps a java method name to a .NET method (can be method in another class). For example:
-methodMapping java.util.Date.getTime Sharpen.Runtime.ToJavaMilliseconds
-typeMappingMaps a java class to .NET type:
-typeMapping com.db4o.Db4o Db4objects.Db4o.Db4oFactory
-propertyMappingMaps a java method to .NET property:
-propertyMapping com.db4odoc.structured.Car.getPilot Pilot
-runtimeTypeNameName of the runtime class. The runtime class provides implementation for methods that don't have a direct mapping or that are simpler to map at the language level than at the sharpen level. For instance: String.substring, String.valueOf, Exception.printStackTrace, etc.
For a complete list of all the method that can be mapped to the runtime class see Configuration#runtimeMethod call hierarchy.
-headerHeader comment to be added to all converted files.
-header config/copyright_comment.txt
-xmldocSpecifies an xml-overlay file, which overrides javadoc documentation for specific classes:
-xmldoc config/sharpen/ApiOverlay.xml
-eventMappingConverts the methods to an event.
-eventAddMappingMarks the method as an event subscription method. Invocations to the method in the form .method() will be replaced by the c# event subscription idiom: +=
-conditionalCompilation
Add a condition when to translate the Java code
- -conditionalCompilation com.db4o.db4ounit.common.cs !SILVERLIGHT
-configurationClassChange the configuration class. The default is 'sharpen.core.DefaultConfiguration'


Annotation Reference:

AnnotationMeaning
@sharpen.enumMark java class to be processed as a .NET enum
@sharpen.renameSpecifies a different name for the converted type, takes a single name argument. For example:
@sharpen.rename Db4oFactory
@sharpen.privateSpecifies that the element must be declared private in the converted file, though it can be not private in the java source:
/*
* @sharpen.private
*/
public List4 _first;
@sharpen.internalSpecifies that the element must be declared internal in the converted file:
/**
 * @sharpen.internal
*/
public abstract int size();
@sharpen.protectedSpecifies that the element must be declared protected in the converted file:
/**
 * @sharpen.protected
*/
public abstract int size();
@sharpen.newAdds the C#-'new' modifier to the translated code.
@sharpen.eventLinks an event to its arguments. For example:
Java:
/**
* @sharpen.event com.db4o.events.QueryEventArgs
*/
public Event4 queryStarted();
is converted to:
public delegate void QueryEventHandler(
  object sender,
  Db4objects.Db4o.Events.QueryEventArgs args);
.......
event Db4objects.Db4o.Events.QueryEventHandler QueryStarted;
@sharpen.event.addMarks the method as an event subscription method. Invocations to the method in the form .method() will be replaced by the c# event subscription idiom: +=
@sharpen.event.onAddValid for event declaration only (SHARPEN_EVENT). Configures the method to be invoked whenever a new event handler is subscribed to the event.
@sharpen.ifAdd #if #endif declaration:
@sharpen.if
@sharpen.propertyConvert a java method as a property:
/**
 * @sharpen.property
*/
public abstract int size();
@sharpen.indexerMarks an element as an indexer property
@sharpen.ignoreSkip the element while converting
@sharpen.ignore.extendsIgnore the extends clause in Java class definition
@sharpen.ignore.implementsIgnore the implements clause in Java class definition
@sharpen.extendsAdds an extends clause to the converted class definition. For example:
Java:
/**
* @sharpen.extends System.Collections.IList
*/
public interface ObjectSet {...
converts to
public interface IObjectSet : System.Collections.IList
@sharpen.partialMarks the converted class as partial
@sharpen.removeMarks a method invocation that should be removed
@sharpen.remove.first
Removes the first line of the method/constructor when converting to C#:
/**
* @sharpen.remove.first
*/
public void doSomething(){
    System.out.println("Java");
    NextMethod();
}
converts to:
public void DoSomething(){
    NextMethod();
}
@sharpen.structMarks class to be converted as c# struct
@sharpen.unwrap
When a method is marked with this annotation
all method calls are removed. This is useful for removing conversion methods when their aren't required in C#.
/*
* @sharpen.unwrap
*/
public Iterable toIterable(Object[] array){
   return Arrays.asList(array);
}
public void doSomething(Object[] objs){
  Iterable iterable = toIterable(objs);
  // do something with the iterable
}
Is converted to:
public IEnumerable ToIterable(object[] array){
    return Arrays.AsList(array);
}
public void doSomething(object[] objs){
   Iterable iterable = objs;
   // do something with the iterable
}
@sharpen.attribute
Adds an attribute to the converted code:
/*
* @sharpen.attribute TheAttribute
*/
public void doSomething(){}
Will be converted to:
[TheAttribute]
public void DoSomething(){}
@sharpen.macroAdd a replace-pattern macro to your code.



0 评论: