blob: 26daa581ca325bfbc9a3c8b00a032cbd839f4143 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.tinkerpop.gremlin.jsr223.CoreImports
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ConnectedComponent
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PageRank
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.PeerPressure
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ShortestPath
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.apache.tinkerpop.gremlin.process.traversal.P
import org.apache.tinkerpop.gremlin.process.traversal.TextP
import org.apache.tinkerpop.gremlin.process.traversal.IO
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__
import org.apache.tinkerpop.gremlin.process.traversal.step.util.WithOptions
import org.apache.tinkerpop.gremlin.structure.Direction
import java.lang.reflect.Modifier
import java.lang.reflect.TypeVariable
import java.lang.reflect.GenericArrayType
def toCSharpTypeMap = ["Long": "long",
"Double": "double",
"Integer": "int",
"String": "string",
"boolean": "bool",
"Object": "object",
"String[]": "string[]",
"Object[]": "object[]",
"Class": "Type",
"Class[]": "Type[]",
"java.util.Map<java.lang.String, java.lang.Object>": "IDictionary<string, object>",
"java.util.Map<java.lang.String, E2>": "IDictionary<string, E2>",
"java.util.Map<java.lang.String, B>": "IDictionary<string, E2>",
"java.util.Map<java.lang.Object, E2>": "IDictionary<object, E2>",
"java.util.Map<java.lang.Object, B>": "IDictionary<object, E2>",
"java.util.List<E>": "IList<E>",
"java.util.List<A>": "IList<E2>",
"java.util.Map<K, V>": "IDictionary<K, V>",
"java.util.Collection<E2>": "ICollection<E2>",
"java.util.Collection<B>": "ICollection<E2>",
"java.util.Map<K, java.lang.Long>": "IDictionary<K, long>",
"TraversalMetrics": "E2",
"Traversal": "ITraversal",
"Traversal[]": "ITraversal[]",
"Predicate": "IPredicate",
"P": "P",
"TextP": "TextP",
"TraversalStrategy": "ITraversalStrategy",
"TraversalStrategy[]": "ITraversalStrategy[]",
"Function": "IFunction",
"BiFunction": "IBiFunction",
"UnaryOperator": "IUnaryOperator",
"BinaryOperator": "IBinaryOperator",
"Consumer": "IConsumer",
"Supplier": "ISupplier",
"Comparator": "IComparator",
"VertexProgram": "object"]
def useE2 = ["E2", "E2"]
def methodsWithSpecificTypes = ["constant": useE2,
"limit": useE2,
"mean": useE2,
"optional": useE2,
"range": useE2,
"skip": useE2,
"sum": useE2,
"tail": useE2,
"unfold": useE2,
"valueMap": ["IDictionary<TKey, TValue>", "TKey, TValue"],]
def getCSharpGenericTypeParam = { typeName ->
def tParam = ""
if (typeName.contains("E2")) {
tParam = "<E2>"
}
else if (typeName.contains("<K, V>")) {
tParam = "<K, V>"
}
else if (typeName.contains("<K, ")) {
tParam = "<K>"
}
else if (typeName.contains("S")) {
tParam = "<S>"
}
else if (typeName.contains("A")) {
tParam = "<A>"
}
return tParam
}
def toCSharpType = { name ->
String typeName = toCSharpTypeMap.getOrDefault(name, name)
if (typeName.equals(name) && (typeName.contains("? extends") || typeName.equals("Tree"))) {
typeName = "E2"
}
return typeName
}
def toCSharpMethodName = { symbol -> (String) Character.toUpperCase(symbol.charAt(0)) + symbol.substring(1) }
def toCSharpValue = { type, value ->
type == String.class && value != null ? ('"' + value + '"') : value
}
def getJavaGenericTypeParameterTypeNames = { method ->
def typeArguments = method.genericReturnType.actualTypeArguments
return typeArguments.
collect { (it instanceof Class) ? ((Class)it).simpleName : it.typeName }.
collect { name ->
if (name.equals("A")) {
name = "object"
}
else if (name.equals("B")) {
name = "E2"
}
name
}
}
def getJavaParameterTypeNames = { method ->
return method.parameters.
collect { param ->
param.type.simpleName
}
}
def toCSharpParamString = { param, genTypeName ->
String csharpParamTypeName = genTypeName
if (csharpParamTypeName == null){
csharpParamTypeName = toCSharpType(param.type.simpleName)
}
else if (csharpParamTypeName == "M") {
csharpParamTypeName = "object"
}
else if (csharpParamTypeName == "A[]") {
csharpParamTypeName = "object[]"
}
else if (csharpParamTypeName == "A" || csharpParamTypeName == "B") {
csharpParamTypeName = "E2"
}
"${csharpParamTypeName} ${param.name}"
}
def getJavaParamTypeString = { method ->
getJavaParameterTypeNames(method).join(",")
}
def getCSharpParamTypeString = { method ->
return method.parameters.
collect { param ->
toCSharpType(param.type.simpleName)
}.join(",")
}
def getCSharpParamString = { method, useGenericParams ->
def parameters = method.parameters
if (parameters.length == 0)
return ""
def genericTypes = method.getGenericParameterTypes()
def csharpParameters = parameters.
toList().indexed().
collect { index, param ->
String genTypeName = null
if (useGenericParams) {
def genType = genericTypes[index]
if (genType instanceof TypeVariable<?>) {
genTypeName = ((TypeVariable<?>)genType).name
}
else if (genType instanceof GenericArrayType) {
if (((GenericArrayType)genType).getGenericComponentType() instanceof TypeVariable<?>) {
genTypeName = ((TypeVariable<?>)((GenericArrayType)genType).getGenericComponentType()).name + "[]"
}
}
}
toCSharpParamString(param, genTypeName)
}.
toArray()
if (method.isVarArgs()) {
def lastIndex = csharpParameters.length-1
csharpParameters[lastIndex] = "params " + csharpParameters[lastIndex]
}
csharpParameters.join(", ")
}
def getParamNames = { parameters ->
return parameters.
collect { param ->
param.name
}
}
def isParamsArgCastNecessary = { parameterString ->
if (parameterString.contains("params ")) {
def paramsType = parameterString.substring(parameterString.indexOf("params ") + "params ".length(), parameterString.indexOf("[]"))
return paramsType == "E" || paramsType == "S"
}
return false
}
def hasMethodNoGenericCounterPartInGraphTraversal = { method ->
def parameterTypeNames = getJavaParameterTypeNames(method)
if (method.name.equals("fold")) {
return parameterTypeNames.size() == 0
}
return false
}
def t2withSpecialGraphTraversalt2 = ["IList<E2>": "E2"]
def getGraphTraversalT2ForT2 = { t2 ->
if (t2withSpecialGraphTraversalt2.containsKey(t2)) {
return t2withSpecialGraphTraversalt2.get(t2)
}
return t2
}
def gatherTokensFrom = { tokenClasses ->
def m = [:]
tokenClasses.each { tc -> m << [(tc.simpleName) : tc.getFields().sort{ a, b -> a.name <=> b.name }.collectEntries{ f -> [(f.name) : f.get(null)]}]}
return m
}
def binding = ["pmethods": P.class.getMethods().
findAll { Modifier.isStatic(it.getModifiers()) }.
findAll { P.class.isAssignableFrom(it.returnType) }.
collect { it.name }.
unique().
sort { a, b -> a <=> b },
"tpmethods": TextP.class.getMethods().
findAll { Modifier.isStatic(it.getModifiers()) }.
findAll { TextP.class.isAssignableFrom(it.returnType) }.
collect { it.name }.
unique().
sort { a, b -> a <=> b },
"sourceStepMethods": GraphTraversalSource.getMethods(). // SOURCE STEPS
findAll { GraphTraversalSource.class.equals(it.returnType) }.
findAll {
!it.name.equals("clone") &&
!it.name.equals(TraversalSource.Symbols.withRemote) &&
!it.name.equals(TraversalSource.Symbols.withComputer)
}.
// Select unique combination of C# parameter types and sort by Java parameter type combination
sort { a, b -> a.name <=> b.name ?: getJavaParamTypeString(a) <=> getJavaParamTypeString(b) }.
unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }.
collect { javaMethod ->
def parameters = getCSharpParamString(javaMethod, false)
def paramNames = getParamNames(javaMethod.parameters)
return ["methodName": javaMethod.name, "parameters":parameters, "paramNames":paramNames]
},
"sourceSpawnMethods": GraphTraversalSource.getMethods(). // SPAWN STEPS
findAll { GraphTraversal.class.equals(it.returnType) }.
// Select unique combination of C# parameter types and sort by Java parameter type combination
sort { a, b -> a.name <=> b.name ?: getJavaParamTypeString(a) <=> getJavaParamTypeString(b) }.
unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }.
collect { javaMethod ->
def typeNames = getJavaGenericTypeParameterTypeNames(javaMethod)
def t1 = toCSharpType(typeNames[0])
def t2 = toCSharpType(typeNames[1])
def tParam = getCSharpGenericTypeParam(t2)
def parameters = getCSharpParamString(javaMethod, true)
def paramNames = getParamNames(javaMethod.parameters)
def isArgsCastNecessary = isParamsArgCastNecessary(parameters)
return ["methodName": javaMethod.name, "t1":t1, "t2":t2, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "isArgsCastNecessary":isArgsCastNecessary]
},
"graphStepMethods": GraphTraversal.getMethods().
findAll { GraphTraversal.class.equals(it.returnType) }.
findAll { !it.name.equals("clone") && !it.name.equals("iterate") }.
// Select unique combination of C# parameter types and sort by Java parameter type combination
sort { a, b -> a.name <=> b.name ?: getJavaParamTypeString(a) <=> getJavaParamTypeString(b) }.
unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }.
collect { javaMethod ->
def typeNames = getJavaGenericTypeParameterTypeNames(javaMethod)
def t1 = toCSharpType(typeNames[0])
def t2 = toCSharpType(typeNames[1])
def tParam = getCSharpGenericTypeParam(t2)
def specificTypes = methodsWithSpecificTypes.get(javaMethod.name)
if (specificTypes) {
t2 = specificTypes[0]
tParam = specificTypes.size() > 1 ? "<" + specificTypes[1] + ">" : ""
}
def parameters = getCSharpParamString(javaMethod, true)
def paramNames = getParamNames(javaMethod.parameters)
def isArgsCastNecessary = isParamsArgCastNecessary(parameters)
return ["methodName": javaMethod.name, "t1":t1, "t2":t2, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "isArgsCastNecessary":isArgsCastNecessary]
},
"anonStepMethods": __.class.getMethods().
findAll { GraphTraversal.class.equals(it.returnType) }.
findAll { Modifier.isStatic(it.getModifiers()) }.
findAll { !it.name.equals("__") && !it.name.equals("start") }.
// Select unique combination of C# parameter types and sort by Java parameter type combination
sort { a, b -> a.name <=> b.name ?: getJavaParamTypeString(a) <=> getJavaParamTypeString(b) }.
unique { a,b -> a.name <=> b.name ?: getCSharpParamTypeString(a) <=> getCSharpParamTypeString(b) }.
collect { javaMethod ->
def typeNames = getJavaGenericTypeParameterTypeNames(javaMethod)
def t2 = toCSharpType(typeNames[1])
def tParam = getCSharpGenericTypeParam(t2)
def specificTypes = methodsWithSpecificTypes.get(javaMethod.name)
if (specificTypes) {
t2 = specificTypes[0]
tParam = specificTypes.size() > 1 ? "<" + specificTypes[1] + ">" : ""
}
def parameters = getCSharpParamString(javaMethod, true)
def paramNames = getParamNames(javaMethod.parameters)
def callGenericTypeArg = hasMethodNoGenericCounterPartInGraphTraversal(javaMethod) ? "" : tParam
def graphTraversalT2 = getGraphTraversalT2ForT2(t2)
return ["methodName": javaMethod.name, "t2":t2, "tParam":tParam, "parameters":parameters, "paramNames":paramNames, "callGenericTypeArg":callGenericTypeArg, "graphTraversalT2":graphTraversalT2]
},
"tokens": gatherTokensFrom([IO, ConnectedComponent, ShortestPath, PageRank, PeerPressure]),
"toCSharpMethodName": toCSharpMethodName,
"withOptions": WithOptions.getDeclaredFields().
collect {["type": toCSharpType(it.type.simpleName), "name": toCSharpMethodName(it.name), "value": toCSharpValue(it.type, it.get(null))]}]
def engine = new groovy.text.GStringTemplateEngine()
def traversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/GraphTraversal.template")).make(binding)
def traversalFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/GraphTraversal.cs")
traversalFile.newWriter().withWriter{ it << traversalTemplate }
def graphTraversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/GraphTraversalSource.template")).make(binding)
def graphTraversalFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs")
graphTraversalFile.newWriter().withWriter{ it << graphTraversalTemplate }
def anonymousTraversalTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/AnonymousTraversal.template")).make(binding)
def anonymousTraversalFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/__.cs")
anonymousTraversalFile.newWriter().withWriter{ it << anonymousTraversalTemplate }
def pTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/P.template")).make(binding)
def pFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/P.cs")
pFile.newWriter().withWriter{ it << pTemplate }
def tpTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/TextP.template")).make(binding)
def tpFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/TextP.cs")
tpFile.newWriter().withWriter{ it << tpTemplate }
def withOptionsTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/WithOptions.template")).make(binding)
def withOptionsFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/WithOptions.cs")
withOptionsFile.newWriter().withWriter{ it << withOptionsTemplate }
binding.tokens.each {k,v ->
def tokenTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Token.template")).make([tokenFields: v, tokenName: k])
def tokenFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/${k}.cs")
tokenFile.newWriter().withWriter{ it << tokenTemplate }
}
def createEnum = { enumClass ->
def b = ["enumClass": enumClass,
"implementedTypes": enumClass.getInterfaces().
collect { it.getSimpleName() }.
findAll { toCSharpTypeMap.containsKey(it) }.
findAll { toCSharpTypeMap[it] != "object" }.
sort { a, b -> a <=> b }.
collect(["EnumWrapper"]) { typeName ->
return toCSharpTypeMap[typeName]
}.join(", "),
"constants": enumClass.getEnumConstants().
sort { a, b -> a.name() <=> b.name() },
"directionClass": Direction.class ]
def enumTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Enum.template")).make(b)
def enumFile = new File("${projectBaseDir}/src/Gremlin.Net/Process/Traversal/" + enumClass.getSimpleName() + ".cs")
enumFile.newWriter().withWriter{ it << enumTemplate }
}
CoreImports.getClassImports().findAll { Enum.class.isAssignableFrom(it) }.
sort { a, b -> a.getSimpleName() <=> b.getSimpleName() }.
each { createEnum(it) }
def determineVersion = {
def env = System.getenv()
def mavenVersion = env.containsKey("TP_RELEASE_VERSION") ? env.get("DOTNET_RELEASE_VERSION") : "${projectVersion}"
// only want to generate a timestamp for the version if this is a nuget deploy
if (!mavenVersion.endsWith("-SNAPSHOT") || null == System.getProperty("nuget")) return mavenVersion
return mavenVersion.replace("-SNAPSHOT", "-dev-" + System.currentTimeMillis())
}
def versionToUse = determineVersion()
def csprojTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Gremlin.Net.csproj.template")).make(["projectVersion":versionToUse])
def csprojFile = new File("${projectBaseDir}/src/Gremlin.Net/Gremlin.Net.csproj")
csprojFile.newWriter().withWriter{ it << csprojTemplate }
def templateCsprojTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Gremlin.Net.Template.csproj.template")).make(["projectVersion":versionToUse])
def templateCsprojFile = new File("${projectBaseDir}/src/Gremlin.Net.Template/Gremlin.Net.Template.csproj")
templateCsprojFile.newWriter().withWriter{ it << templateCsprojTemplate }
def nuspecTemplate = engine.createTemplate(new File("${projectBaseDir}/glv/Gremlin.Net.Template.nuspec.template")).make(["projectVersion":versionToUse])
def nuspecFile = new File("${projectBaseDir}/src/Gremlin.Net.Template/Gremlin.Net.Template.nuspec")
nuspecFile.newWriter().withWriter{ it << nuspecTemplate }