Skip to content

Basic implementation of java code generation #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eb52057
Merge pull request #1 from apexlang/main
ArishSultan Oct 12, 2022
6e4db79
basic structure added for java
ArishSultan Oct 12, 2022
e0c9ef0
basic structure - fixes
ArishSultan Oct 13, 2022
3a18be5
go apex reverted
ArishSultan Oct 13, 2022
27a269a
Adding java expected code structure
ArishSultan Oct 13, 2022
2ee27fc
Adding enum visitor, union visitor, alias visitor & interface visitor…
ArishSultan Oct 14, 2022
dd79692
Fixed Java Code Structure By adding static classes to easily share da…
ArishSultan Oct 17, 2022
f5115dd
basic structure added for java
ArishSultan Oct 12, 2022
c909ff4
basic structure - fixes
ArishSultan Oct 13, 2022
9551389
go apex reverted
ArishSultan Oct 13, 2022
be1783a
Adding java expected code structure
ArishSultan Oct 13, 2022
5b5063d
Adding enum visitor, union visitor, alias visitor & interface visitor…
ArishSultan Oct 14, 2022
2a7b149
Fixed Java Code Structure By adding static classes to easily share da…
ArishSultan Oct 17, 2022
c7439c3
Restructuring java layout.
pkedy Oct 21, 2022
fb071b8
Merge remote-tracking branch 'origin/java-codegen' into java-codegen
ArishSultan Oct 21, 2022
fa0a297
Adding Configuration for Java Namespaces + Adding expected Code Struc…
ArishSultan Oct 28, 2022
59dd6c3
Adding Configuration for Java Namespaces + Adding expected Code Struc…
ArishSultan Oct 28, 2022
5697e3c
Merge branch 'main' into java-codegen
ArishSultan Oct 28, 2022
42fd29a
Adding Main, Repository, Service Visitor + Generate Actual Code Struc…
ArishSultan Oct 31, 2022
994376a
Merge remote-tracking branch 'origin/java-codegen' into java-codegen
ArishSultan Oct 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ coverage

!/testdata/*/actual/go.*
!/testdata/*/actual/package.*
!/testdata/*/actual/requirements.txt
!/testdata/*/actual/requirements.txt
/testdata/java/data/
1 change: 1 addition & 0 deletions java.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./dist/java/index.js";
1 change: 1 addition & 0 deletions java.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./dist/java/index.js";
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"exports": {
".": "./dist/index.js",
"./cs": "./dist/cs/index.js",
"./java": "./dist/java/src/index.js",
"./go": "./dist/go/index.js",
"./rust": "./dist/rust/index.js",
"./proto": "./dist/proto/index.js",
Expand Down Expand Up @@ -69,9 +70,8 @@
},
"dependencies": {
"@apexlang/core": "^0.1.1",
"@types/node": "^18.7.23",
"openapi3-ts": "^2.0.2",
"yaml": "^1.10.2"
"yaml": "^1.10.2",
"openapi3-ts": "^2.0.2"
},
"devDependencies": {
"@types/jest": "^29.1.0",
Expand Down
74 changes: 74 additions & 0 deletions src/java/src/default-visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { BaseVisitor, Context, Visitor, Writer } from "@apexlang/core/model";
import {
AliasVisitor,
EnumVisitor,
ImportVisitor,
InterfaceVisitor,
TypeVisitor,
UnionVisitor,
} from "./visitors";

interface Config {
package: string;
module: string;
imports: string[];
}

export class DefaultVisitor extends BaseVisitor {
importVisitor = (writer: Writer): Visitor => new ImportVisitor(writer);
typeVisitor = (writer: Writer): Visitor => new TypeVisitor(writer);
interfaceVisitor = (writer: Writer): Visitor => new InterfaceVisitor(writer);
enumVisitor = (writer: Writer): Visitor => new EnumVisitor(writer);
aliasVisitor = (writer: Writer): Visitor => new AliasVisitor(writer);
unionVisitor = (writer: Writer): Visitor => new UnionVisitor(writer);

visitNamespaceBefore(context: Context): void {
const config = context.config as Config;
const packageName = config.package || "com.apexlang";
this.write(`package ${packageName};\n\n`);
super.visitNamespaceBefore(context);
}

visitNamespace(context: Context) {
const visitor = this.importVisitor(this.writer);
context.namespace.accept(context, visitor);
}

visitType(context: Context) {
const visitor = this.typeVisitor(this.writer);
context.type.accept(context, visitor);
}

visitInterface(context: Context) {
const visitor = this.interfaceVisitor(this.writer);
context.interface.accept(context, visitor);
}

visitEnum(context: Context) {
const visitor = this.enumVisitor(this.writer);
context.enum.accept(context, visitor);
}

visitAlias(context: Context) {
const visitor = this.aliasVisitor(this.writer);
context.alias.accept(context, visitor);
}

visitUnion(context: Context) {
const visitor = this.unionVisitor(this.writer);
context.union.accept(context, visitor);
}

visitNamespaceAfter(context: Context) {
this.write(`\n`);
this.write(`\n`);
this.write(`public static void main(String[] args) {`);
this.write(`\n`);
this.write(`\t System.out.println("Welcome to JAVA. Happy Coding :)");`);
this.write(`\n`);
this.write(`\t }`);
this.write(`\n`);
this.write(`}`);
super.visitNamespaceAfter(context);
}
}
4 changes: 4 additions & 0 deletions src/java/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { DefaultVisitor as default } from "./default-visitor.js";
export { DefaultVisitor } from "./default-visitor.js";

export * as visitors from "./visitors/index.js";
16 changes: 16 additions & 0 deletions src/java/src/utils/conversions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PrimitiveName } from "@apexlang/core/model";

export const convertSignedToUnsigned = (type: string, value: any) => {
switch (type) {
case PrimitiveName.U8:
return `value & 0xff`;
case PrimitiveName.U64:
return `Long.toUnsignedString(${value})`;
case PrimitiveName.U32:
return `${value} & 0x00000000ffffffffL`;
case PrimitiveName.U16:
return `${value} & 0xffff`;
default:
return `${value}`;
}
};
85 changes: 85 additions & 0 deletions src/java/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
AnyType,
Kind,
List,
Map,
Named,
ObjectMap,
Optional,
Primitive,
PrimitiveName,
} from "@apexlang/core/model";
import { pascalCase } from "../../../utils";

export function convertType(typ: AnyType, config: ObjectMap): string {
switch (typ.kind) {
case Kind.List: {
return `List<${convertType((typ as List).type, config)}>`;
}
case Kind.Map: {
return `Map<${convertType((typ as Map).keyType, config)}, ${pascalCase(
convertType((typ as Map).valueType, config)
)}>`;
}
case Kind.Optional: {
return `${convertType((typ as Optional).type, config)}`;
}
case Kind.Void: {
return "void";
}
case Kind.Type:
case Kind.Union:
case Kind.Enum:
const namedValue = (typ as Named).name;
return pascalCase(namedValue);
case Kind.Alias:
return (typ as Named).name.toUpperCase();
case Kind.Primitive: {
return `${convertPrimitive(typ as Primitive, config)}`;
}
default: {
return `${typ.kind}`;
// throw new Error(`Unhandled type conversion for type: ${typ.kind}`);
}
}
}

function convertPrimitive(typ: Primitive, config: ObjectMap): string {
switch (typ.name) {
case PrimitiveName.Bool:
return "bool";
case PrimitiveName.Bytes:
return "byte";
case PrimitiveName.DateTime:
return "LocalTime";
case PrimitiveName.F32:
return "float";
case PrimitiveName.F64:
return "double";
case PrimitiveName.U64:
return "String";
case PrimitiveName.U32:
return "long";
case PrimitiveName.U16:
return "int";
case PrimitiveName.U8:
return "int";
case PrimitiveName.I64:
return "long";
case PrimitiveName.I32:
return "int";
case PrimitiveName.I16:
return "short";
case PrimitiveName.I8:
return "byte";
case PrimitiveName.String:
return "String";
case PrimitiveName.Any:
return "Object";
default:
return `${typ.name}`;
// throw new Error(
// `Unhandled primitive type conversion for type: ${typ.name}`
// );
}
}
18 changes: 18 additions & 0 deletions src/java/src/visitors/alias-visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BaseVisitor, Context } from "@apexlang/core/model";
import { pascalCase } from "../../../utils";

export class AliasVisitor extends BaseVisitor {
visitAliasBefore(context: Context) {
super.visitAliasBefore(context);
}

visitAlias(context: Context) {
// const alias = context.alias.name;
// this.write(`class ${pascalCase(alias)} {} \n\n`);
super.visitAlias(context);
}

visitAliasAfter(context: Context) {
super.visitAliasAfter(context);
}
}
92 changes: 92 additions & 0 deletions src/java/src/visitors/controller-visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {BaseVisitor, Context} from "@apexlang/core/model";
import {convertType} from "../utils/types";
import {PathDirective} from "../../../rest";

interface Config {
package: string;
filename: string;
module: string;
imports: string[];
}

export class ControllerVisitor extends BaseVisitor {
visitInterfacesBefore(context: Context) {
const config = context.config as Config;
const packageName = config.package || "com.example.codegen.controllers";
this.write(`package ${packageName}.controllers;\n\n`);
this.write(`import com.example.codegen.models.MyType;\n`);
this.write(`import com.example.codegen.repositories.MyRepository;\n`);
this.write(`import com.example.codegen.services.MyTypeService;\n`);
this.write(`import org.springframework.beans.factory.annotation.Autowired;\n`);
this.write(`import org.springframework.web.bind.annotation.*;\n`);
this.write(`\n`);
super.visitInterfacesBefore(context);
}

visitInterface(context: Context) {
const {interface: iFace} = context;
iFace.annotations.forEach((a) => {
if (a.name === "service") {
this.write(`@RestController\n`);
let path = "";
context.namespace.annotation("path", (a) => {
path = a?.convert<PathDirective>().value;
});
this.write(`@RequestMapping("${path}") produces = "application/json")\n`);
this.write(`public class ${iFace.name} {\n`);
this.write(`\n`);
this.write(`\t@Autowired\n`);
this.write(`\tprivate Service service;\n`);
this.write(`\n`);
iFace.operations.forEach((o) => {
o.annotations.forEach((a) => {
if (a.name === "GET") {
this.write(
`\t @RequestMapping(value = "/${o.name}", method = RequestMethod.GET)\n`
);
} else if (a.name === "POST") {
this.write(
`\t @RequestMapping(value = "/${o.name}", method = RequestMethod.POST)\n`
);
} else if (a.name === "PUT") {
this.write(
`\t @RequestMapping(value = "/${o.name}", method = RequestMethod.PUT)\n`
);
} else if (a.name === "DELETE") {
this.write(
`\t @RequestMapping(value = "/${o.name}", method = RequestMethod.DELETE)\n`
);
}
})
this.write(
`\t public ${convertType(o.type, context.config)} ${o.name}(`
);
o.parameters.forEach((p, i) => {
this.write(`${convertType(p.type, context.config)} ${p.name}`);
if (i !== o.parameters.length - 1) {
this.write(`, `);
}
});
this.write(`)`);
this.write(`{`);
this.write(`\n`);
this.write(`\t\t`);
if (o.parameters.length > 0) {
this.write(`return service.${o.name}(`);
this.write(`${o.parameters[0].name};`);
this.write(`);`);
} else {
this.write(`return service.${o.name}();`);
}
this.write(`\n`);
this.write(`\t}`);
this.write(`\n\n`);
});

this.write(`\n`);
this.write(`}`);
}
});
super.visitInterface(context);
}
}
65 changes: 65 additions & 0 deletions src/java/src/visitors/enum-visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { BaseVisitor, Context, Writer } from "@apexlang/core/model";

export class EnumVisitor extends BaseVisitor {
constructor(writer: Writer) {
super(writer);
}

visitEnumBefore(context: Context) {
const { enum: enumNode } = context;
enumNode.description && this.write(`// ${enumNode.description}\n`);
super.visitEnumBefore(context);
}

visitEnum(context: Context) {
const { enum: enumNode } = context;
this.write(`public enum ${enumNode.name} {`);
this.write(`\n`);
super.visitEnum(context);
}

visitEnumValuesBefore(context: Context) {
// const {enum: enumNode} = context;
// enumNode.description && this.write(`// ${enumNode.description}\n`);
const { enumValues } = context;
enumValues.map((enumValue, index) => {
if (index > 0) {
enumValue.description && this.write(`,${enumValue.description}`);
} else {
enumValue.description && this.write(`\t // ${enumValue.description}`);
}
});
this.write(`\n`);
// enumValue.node.description?.getValue() && this.write(`// ${enumValue.node.description.getValue()}`);
super.visitEnumValuesBefore(context);
}

visitEnumValue(context: Context) {
const { enum: enumNode, enumValue, enumValues } = context;
if (enumValues.length - 1 === enumValue.index) {
this.write(`,\n`);
this.write(`\t ${enumValue.name}(${enumValue.index});`);
this.write(`\n`);
this.write(`\n`);
this.write(`\t private final int value;`);
this.write(`\n`);
this.write(`\t ${enumNode.name}(int value) {`);
this.write(`\n`);
this.write(`\t\t this.value = value;`);
this.write(`\n`);
this.write(`\t }`);
} else if (enumValue.index > 0) {
this.write(`,\n`);
this.write(`\t ${enumValue.name}(${enumValue.index})`);
} else {
this.write(`\t ${enumValue.name}(${enumValue.index})`);
}
// this.write(`${enumValue.name} = ${enumValue.index} as "${enumValue.display}"`);
super.visitEnumValue(context);
}

visitEnumAfter(context: Context) {
this.write(`\n}`);
super.visitEnumAfter(context);
}
}
Loading