Skip to content
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

feat(map): Codegen for Java Target #58

Merged
merged 23 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
19919c8
feat(map): add map for javavisitor
jonathan-casey Oct 10, 2023
d9a482b
feat(map): update snapshot
jonathan-casey Oct 10, 2023
a561e7a
feat(map): add test cases
jonathan-casey Oct 10, 2023
2fdb635
remove redundant ternary expression
jonathan-casey Oct 11, 2023
d49ab1c
feat(map): get type from ModelFile
jonathan-casey Oct 21, 2023
f34f96b
chore(deps): upgrade concerto
jonathan-casey Oct 21, 2023
6d25e39
fix(csharp):remove semicolon (;) from map property declaration (#60)
ragi-dayananda Oct 11, 2023
0f05e7f
chore: fix package version
jonathan-casey Oct 11, 2023
99b4e09
chore(actions): publish v3.16.2 to npm (#61)
github-actions[bot] Oct 11, 2023
36e2e7f
feat(map): Codegen for GraphQL Target (#66)
jonathan-casey Oct 19, 2023
e4630cd
feat!(codegen): remove loopback src (#59)
jonathan-casey Oct 19, 2023
673e012
chore(deps-dev): bump @babel/traverse from 7.22.11 to 7.23.2 (#67)
dependabot[bot] Oct 19, 2023
f97ef0f
test(codegen) update HR model to use multiple namespaces (#68)
dselman Nov 2, 2023
4147fe1
feat(map): update snapshot
jonathan-casey Oct 10, 2023
541fb78
chore(deps): upgrade concerto
jonathan-casey Oct 21, 2023
5b2fca7
chore(deps): upgrade concerto
jonathan-casey Nov 3, 2023
5cf9d21
test(*): snapshot update
jonathan-casey Nov 3, 2023
a01fe38
fix merge conflict
jonathan-casey Nov 3, 2023
5b97a34
test(*): snapshot update
jonathan-casey Nov 3, 2023
1a55b9d
feat(map): write util import
jonathan-casey Nov 3, 2023
0746a66
test(*): fix regressions
jonathan-casey Nov 3, 2023
60415e5
test(*): add test coverage for map imports
jonathan-casey Nov 3, 2023
64b1f1f
test(*): uses isMap util fun
jonathan-casey Nov 6, 2023
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
16 changes: 15 additions & 1 deletion lib/codegen/fromcto/java/javavisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ class JavaVisitor {
parameters.fileWriter.writeLine(0, `import ${namespace}.${typeName};` );
});

let hasImportedJavaMap = false;
classDeclaration.getOwnProperties().forEach(p => {
if(ModelUtil.isMap(p) && !hasImportedJavaMap) {
parameters.fileWriter.writeLine(0, 'import java.util.HashMap;');
parameters.fileWriter.writeLine(0, 'import java.util.Map;');
hasImportedJavaMap = true;
}
});

parameters.fileWriter.writeLine(0, 'import com.fasterxml.jackson.annotation.*;');
parameters.fileWriter.writeLine(0, '');

Expand Down Expand Up @@ -278,7 +287,12 @@ class JavaVisitor {
parameters.fileWriter.writeLine(1, '}');
break;
default:
parameters.fileWriter.writeLine(1, 'private ' + fieldType + ' ' + fieldName + ';' );
if (ModelUtil.isMap(field)) {
const decl = field.getModelFile().getType(field.ast.type.name);
parameters.fileWriter.writeLine(1, `private Map<${this.toJavaType(decl.getKey().getType())}, ${this.toJavaType(decl.getValue().getType())}> ${field.getName()} = new HashMap<>();`);
jonathan-casey marked this conversation as resolved.
Show resolved Hide resolved
} else {
parameters.fileWriter.writeLine(1, 'private ' + fieldType + ' ' + fieldName + ';' );
}
}
} else {
parameters.fileWriter.writeLine(1, 'private ' + fieldType + ' ' + fieldName + ';' );
Expand Down
32 changes: 20 additions & 12 deletions test/codegen/__snapshots__/codegen.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1065,17 +1065,19 @@ import concerto.Asset;
import concerto.Transaction;
import concerto.Participant;
import concerto.Event;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.*;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "$class")
public class Company extends Concept {
private String name;
private Address headquarters;
private CompanyProperties companyProperties;
private EmployeeDirectory employeeDirectory;
private EmployeeTShirtSizes employeeTShirtSizes;
private EmployeeProfiles employeeProfiles;
private EmployeeSocialSecurityNumbers employeeSocialSecurityNumbers;
private Map<String, String> companyProperties = new HashMap<>();
private Map<SSN, Employee> employeeDirectory = new HashMap<>();
private Map<SSN, TShirtSizeType> employeeTShirtSizes = new HashMap<>();
private Map<String, Concept> employeeProfiles = new HashMap<>();
private Map<String, SSN> employeeSocialSecurityNumbers = new HashMap<>();
public String getName() {
return this.name;
}
Expand Down Expand Up @@ -1253,6 +1255,8 @@ import concerto.Asset;
import concerto.Transaction;
import concerto.Participant;
import concerto.Event;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.*;

@JsonIgnoreProperties({"id"})
Expand All @@ -1272,7 +1276,7 @@ public abstract class Person extends Participant {
private String ssn;
private double height;
private java.util.Date dob;
private NextOfKin nextOfKin;
private Map<KinName, KinTelephone> nextOfKin = new HashMap<>();
public String getEmail() {
return this.email;
}
Expand Down Expand Up @@ -6613,17 +6617,19 @@ import concerto.Asset;
import concerto.Transaction;
import concerto.Participant;
import concerto.Event;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.*;

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "$class")
public class Company extends Concept {
private String name;
private Address headquarters;
private CompanyProperties companyProperties;
private EmployeeDirectory employeeDirectory;
private EmployeeTShirtSizes employeeTShirtSizes;
private EmployeeProfiles employeeProfiles;
private EmployeeSocialSecurityNumbers employeeSocialSecurityNumbers;
private Map<String, String> companyProperties = new HashMap<>();
private Map<SSN, Employee> employeeDirectory = new HashMap<>();
private Map<SSN, TShirtSizeType> employeeTShirtSizes = new HashMap<>();
private Map<String, Concept> employeeProfiles = new HashMap<>();
private Map<String, SSN> employeeSocialSecurityNumbers = new HashMap<>();
public String getName() {
return this.name;
}
Expand Down Expand Up @@ -6801,6 +6807,8 @@ import concerto.Asset;
import concerto.Transaction;
import concerto.Participant;
import concerto.Event;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.*;

@JsonIgnoreProperties({"id"})
Expand All @@ -6820,7 +6828,7 @@ public abstract class Person extends Participant {
private String ssn;
private double height;
private java.util.Date dob;
private NextOfKin nextOfKin;
private Map<KinName, KinTelephone> nextOfKin = new HashMap<>();
public String getEmail() {
return this.email;
}
Expand Down
14 changes: 13 additions & 1 deletion test/codegen/fromcto/java/javamissingplugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ const sinon = require('sinon');

const JavaVisitor = require('../../../../lib/codegen/fromcto/java/javavisitor.js');
const AbstractPlugin = require('../../../../lib/codegen/abstractplugin.js');
const ModelUtil = require('@accordproject/concerto-core').ModelUtil;

const ClassDeclaration = require('@accordproject/concerto-core').ClassDeclaration;
const EnumDeclaration = require('@accordproject/concerto-core').EnumDeclaration;
const FileWriter = require('@accordproject/concerto-util').FileWriter;

let sandbox = sinon.createSandbox();

describe('JavaMissingPlugin', function () {
let javaVisit;
let mockFileWriter;
Expand Down Expand Up @@ -115,6 +118,15 @@ describe('JavaMissingPlugin', function () {

sinon.stub(javaVisit, 'startClassFile');
sinon.stub(javaVisit, 'endClassFile');

sandbox.stub(ModelUtil, 'isMap').callsFake(() => {
return true;
});
});

afterEach(() => {
sandbox.restore();

});

it('should fail to write a class declaration and call accept on each property', () => {
Expand All @@ -126,4 +138,4 @@ describe('JavaMissingPlugin', function () {
});
});

});
});
82 changes: 81 additions & 1 deletion test/codegen/fromcto/java/javavisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const sinon = require('sinon');
const JavaVisitor = require('../../../../lib/codegen/fromcto/java/javavisitor.js');

const ClassDeclaration = require('@accordproject/concerto-core').ClassDeclaration;
const MapDeclaration = require('@accordproject/concerto-core').MapDeclaration;
const ModelUtil = require('@accordproject/concerto-core').ModelUtil;
const EnumDeclaration = require('@accordproject/concerto-core').EnumDeclaration;
const EnumValueDeclaration = require('@accordproject/concerto-core').EnumValueDeclaration;
const Field = require('@accordproject/concerto-core').Field;
Expand All @@ -29,12 +31,21 @@ const ModelManager = require('@accordproject/concerto-core').ModelManager;
const RelationshipDeclaration = require('@accordproject/concerto-core').RelationshipDeclaration;
const FileWriter = require('@accordproject/concerto-util').FileWriter;

let sandbox = sinon.createSandbox();

describe('JavaVisitor', function () {
let javaVisit;
let mockFileWriter;
beforeEach(() => {
javaVisit = new JavaVisitor();
mockFileWriter = sinon.createStubInstance(FileWriter);
sandbox.stub(ModelUtil, 'isMap').callsFake(() => {
return false;
});
});

afterEach(() => {
sandbox.restore();
});

describe('visit', () => {
Expand Down Expand Up @@ -376,6 +387,36 @@ describe('JavaVisitor', function () {
acceptSpy.withArgs(javaVisit, Object.assign({},param,{mode:'setter'})).calledTwice.should.be.ok;
mockEndClassFile.withArgs(mockClassDeclaration, param).calledOnce.should.be.ok;
});

it('should write a class declaration, including imports for java.util Map & HashMap types', () => {
mockClassDeclaration.getIdentifierFieldName.returns('employeeID');
sandbox.restore();
sandbox.stub(ModelUtil, 'isMap').callsFake(() => {
return true;
});
javaVisit.visitClassDeclaration(mockClassDeclaration, param);

mockStartClassFile.withArgs(mockClassDeclaration, param).calledOnce.should.be.ok;
param.fileWriter.writeLine.callCount.should.deep.equal(9);
param.fileWriter.writeLine.getCall(0).args.should.deep.equal([0, 'import fruit.oranges;']);
param.fileWriter.writeLine.getCall(1).args.should.deep.equal([0, 'import fruit.apples;']);
param.fileWriter.writeLine.getCall(2).args.should.deep.equal([0, 'import java.util.HashMap;']);
param.fileWriter.writeLine.getCall(3).args.should.deep.equal([0, 'import java.util.Map;']);
param.fileWriter.writeLine.getCall(4).args.should.deep.equal([0, 'import com.fasterxml.jackson.annotation.*;']);
param.fileWriter.writeLine.getCall(5).args.should.deep.equal([0, '']);
param.fileWriter.writeLine.getCall(6).args.should.deep.equal([0, 'public class Bob {']);
param.fileWriter.writeLine.getCall(7).args.should.deep.equal([1, `
// the accessor for the identifying field
public String getID() {
return this.getEmployeeID();
}
`]);
param.fileWriter.writeLine.getCall(8).args.should.deep.equal([0, '}']);
acceptSpy.withArgs(javaVisit, Object.assign({},param,{mode:'field'})).calledTwice.should.be.ok;
acceptSpy.withArgs(javaVisit, Object.assign({},param,{mode:'getter'})).calledTwice.should.be.ok;
acceptSpy.withArgs(javaVisit, Object.assign({},param,{mode:'setter'})).calledTwice.should.be.ok;
mockEndClassFile.withArgs(mockClassDeclaration, param).calledOnce.should.be.ok;
});
});

describe('visitField', () => {
Expand Down Expand Up @@ -413,6 +454,45 @@ describe('JavaVisitor', function () {
param.fileWriter.writeLine.withArgs(1, 'private JavaType[] Bob;').calledOnce.should.be.ok;
});

it('should write a line with a HashMap', () => {
let param = {
fileWriter: mockFileWriter,
mode: 'field'
};

sandbox.restore();
sandbox.stub(ModelUtil, 'isMap').callsFake(() => {
return true;
});

const mockField = sinon.createStubInstance(Field);
const getType = sinon.stub();

mockField.ast = { type: { name: 'Dummy Value'} };
mockField.getModelFile.returns({ getType: getType });

const mockMapDeclaration = sinon.createStubInstance(MapDeclaration);
const getKeyType = sinon.stub();
const getValueType = sinon.stub();

mockField.getName.returns('Bob');
mockField.getType.returns('SpecialType');

getType.returns(mockMapDeclaration);
getKeyType.returns('String');
getValueType.returns('String');
mockField.getName.returns('Map1');
mockMapDeclaration.getName.returns('Map1');
mockMapDeclaration.isMapDeclaration.returns(true);
mockMapDeclaration.getKey.returns({ getType: getKeyType });
mockMapDeclaration.getValue.returns({ getType: getValueType });

javaVisit.visitField(mockField,param);

param.fileWriter.writeLine.withArgs(1, 'private Map<String, String> Map1 = new HashMap<>();').calledOnce.should.be.ok;
sandbox.reset();
});

it('should write a line defining a field', () => {
let mockField = sinon.createStubInstance(Field);
mockField.isField.returns(true);
Expand Down Expand Up @@ -673,4 +753,4 @@ describe('JavaVisitor', function () {
javaVisit.toJavaType('Penguin').should.deep.equal('Penguin');
});
});
});
});
Loading