Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/composer-common/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class SecurityException extends BaseException {
}
class Serializer {
+ void constructor(Factory,ModelManager)
+ Object toJSON(Resource,Object,boolean,boolean,boolean) throws Error
+ Object toJSON(Resource,Object,boolean,boolean,boolean,boolean) throws Error
+ Resource fromJSON(Object,Object,boolean)
}
class ValidationException extends BaseException {
Expand Down
3 changes: 2 additions & 1 deletion packages/composer-common/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
#
# Note that the latest public API is documented using JSDocs and is available in api.txt.
#
Version 0.10.1 {5d850eefdff10604eb0fdef2bd7aef5a} 2017-07-24
Version 0.10.1 {d1fd512551ff5bb30b31f05f6817966e} 2017-07-24
- Added InvalidQueryException, BaseFileException
- Added IdCard to composer-common package
- Added option to Serializer.toJSON to deduplicate resources

Version 0.9.2 {60327250ee4f059647020f8aee5ed67b} 2017-07-06
- Added includeOptionalFields option to Factory.newXXX functions
Expand Down
60 changes: 53 additions & 7 deletions packages/composer-common/lib/codegen/fromcto/java/javavisitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,33 @@ class JavaVisitor {
* @private
*/
visitModelManager(modelManager, parameters) {

parameters.fileWriter.openFile( 'org/hyperledger/composer/system/Resource.java');
parameters.fileWriter.writeLine(0, '// this code is generated and should not be modified');
parameters.fileWriter.writeLine(0, 'package org.hyperledger.composer.system;');
parameters.fileWriter.writeLine(0, 'import com.fasterxml.jackson.annotation.*;');

parameters.fileWriter.writeLine(0, `
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "$class")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "$id")
public abstract class Resource
{
public abstract String getID();
private String $id;

@JsonProperty("$id")
public String get$id() {
return $id;
}
@JsonProperty("$id")
public void set$id(String i) {
$id = i;
}

}
`);
parameters.fileWriter.closeFile();

modelManager.getModelFiles().forEach((modelFile) => {
modelFile.accept(this,parameters);
});
Expand Down Expand Up @@ -122,11 +149,7 @@ class JavaVisitor {
parameters.fileWriter.writeLine(0, '// this code is generated and should not be modified');
parameters.fileWriter.writeLine(0, 'package ' + clazz.getModelFile().getNamespace() + ';');
parameters.fileWriter.writeLine(0, '');

if(!clazz.isSystemType()) {
parameters.fileWriter.writeLine(0, 'import org.hyperledger.composer.system.*;');
parameters.fileWriter.writeLine(0, 'import com.fasterxml.jackson.annotation.*;');
}
parameters.fileWriter.writeLine(0, 'import org.hyperledger.composer.system.*;');
}

/**
Expand Down Expand Up @@ -177,6 +200,16 @@ class JavaVisitor {

this.startClassFile(classDeclaration, parameters);

classDeclaration.getModelFile().getImports().forEach((imported) => {
parameters.fileWriter.writeLine(0, 'import ' + imported + ';' );
});

if(classDeclaration.isConcept()) {
parameters.fileWriter.writeLine(0, 'import com.fasterxml.jackson.annotation.JsonIgnoreProperties;');
parameters.fileWriter.writeLine(0, '');
parameters.fileWriter.writeLine(0, '@JsonIgnoreProperties({"$class"})');
}

let isAbstract = '';
if( classDeclaration.isAbstract() ) {
isAbstract = 'abstract ';
Expand All @@ -186,14 +219,27 @@ class JavaVisitor {
}

let superType = '';

if(classDeclaration.isSystemCoreType()) {
superType = ' extends org.hyperledger.composer.system.Resource';
}

if(classDeclaration.getSuperType()) {
superType = ' extends ' + ModelUtil.getShortName(classDeclaration.getSuperType());
}

parameters.fileWriter.writeLine(0, 'public ' + isAbstract + 'class ' + classDeclaration.getName() + superType + ' {' );

// add the magic $class property
parameters.fileWriter.writeLine(1, 'public String $class;' );
// add the getID abstract type
if(classDeclaration.getIdentifierFieldName()) {
parameters.fileWriter.writeLine(1, `
// the accessor for the identifying field
public String getID() {
return ${classDeclaration.getIdentifierFieldName()};
}
`
);
}

classDeclaration.getOwnProperties().forEach((property) => {
property.accept(this,parameters);
Expand Down
1 change: 0 additions & 1 deletion packages/composer-common/lib/codegen/jsonwriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ class JSONWriter extends Writer {
* @param {string} value - the value
*/
writeKeyStringValue(key,value) {
this.writeComma();
this.writeKey(key);
this.writeStringValue(value);
this.firstItem = false;
Expand Down
7 changes: 6 additions & 1 deletion packages/composer-common/lib/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class Serializer {
* are specified for relationship fields into relationships, false by default.
* @param {boolean} options.permitResourcesForRelationships - Permit resources in the
* place of relationships (serializing them as resources), false by default.
* @param {boolean} options.deduplicateResources - Generate $id for resources and
* if a resources appears multiple times in the object graph only the first instance is
* serialized in full, subsequent instances are replaced with a reference to the $id
* @return {Object} - The Javascript Object that represents the resource
* @throws {Error} - throws an exception if resource is not an instance of
* Resource or fails validation.
Expand All @@ -79,6 +82,7 @@ class Serializer {
parameters.stack = new TypedStack(resource);
parameters.modelManager = this.modelManager;
parameters.seenResources = new Set();
parameters.dedupeResources = new Set();
const classDeclaration = this.modelManager.getType( resource.getFullyQualifiedType() );

// validate the resource against the model
Expand All @@ -93,7 +97,8 @@ class Serializer {

const generator = new JSONGenerator(
options.convertResourcesToRelationships === true,
options.permitResourcesForRelationships === true
options.permitResourcesForRelationships === true,
options.deduplicateResources === true
);
const writer = new JSONWriter();
parameters.writer = writer;
Expand Down
129 changes: 78 additions & 51 deletions packages/composer-common/lib/serializer/jsongenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const ClassDeclaration = require('../introspect/classdeclaration');
const Field = require('../introspect/field');
const RelationshipDeclaration = require('../introspect/relationshipdeclaration');
const Resource = require('../model/resource');
const Identifiable = require('../model/identifiable');
const Typed = require('../model/typed');
const Concept = require('../model/concept');
const ModelUtil = require('../modelutil');
Expand All @@ -42,10 +43,14 @@ class JSONGenerator {
* are specified for relationship fields into relationships, false by default.
* @param {boolean} [permitResourcesForRelationships] Permit resources in the
* place of relationships (serializing them as resources), false by default.
* @param {boolean} [deduplicateResources] If resources appear several times
* in the object graph only the first instance is serialized, with only the $id
* written for subsequent instances, false by default.
*/
constructor(convertResourcesToRelationships, permitResourcesForRelationships) {
constructor(convertResourcesToRelationships, permitResourcesForRelationships, deduplicateResources) {
this.convertResourcesToRelationships = convertResourcesToRelationships;
this.permitResourcesForRelationships = permitResourcesForRelationships;
this.deduplicateResources = deduplicateResources;
}

/**
Expand All @@ -63,7 +68,7 @@ class JSONGenerator {
} else if (thing instanceof Field) {
return this.visitField(thing, parameters);
} else {
throw new Error('Unrecognised ' + JSON.stringify(thing) );
throw new Error('Unrecognised ' + JSON.stringify(thing));
}
}

Expand All @@ -75,24 +80,47 @@ class JSONGenerator {
* @private
*/
visitClassDeclaration(classDeclaration, parameters) {
parameters.writer.openObject();
parameters.writer.writeKeyStringValue('$class', classDeclaration.getFullyQualifiedName());

const obj = parameters.stack.pop();
if(!((obj instanceof Resource) || (obj instanceof Concept))) {
throw new Error('Expected a Resource or a Concept, but found ' + obj );
if (!((obj instanceof Resource) || (obj instanceof Concept))) {
throw new Error('Expected a Resource or a Concept, but found ' + obj);
}
const properties = classDeclaration.getProperties();
for(let n=0; n < properties.length; n++) {
const property = properties[n];
const value = obj[property.getName()];
if(!Util.isNull(value)) {
parameters.stack.push(value);
property.accept(this,parameters);

let writeFields = true;
let id = null;

if (obj instanceof Identifiable && this.deduplicateResources) {
id = obj.toURI();
if( parameters.dedupeResources.has(id)) {
writeFields = false;
parameters.writer.writeStringValue( id );
}
else {
parameters.dedupeResources.add(id);
}
}

if (writeFields) {
parameters.writer.openObject();
parameters.writer.writeKeyStringValue('$class', classDeclaration.getFullyQualifiedName());

if(this.deduplicateResources && id) {
parameters.writer.writeKeyStringValue('$id', id );
}

const properties = classDeclaration.getProperties();
for (let n = 0; n < properties.length; n++) {
const property = properties[n];
const value = obj[property.getName()];
if (!Util.isNull(value)) {
parameters.stack.push(value);
property.accept(this, parameters);
}
}

parameters.writer.closeObject();
}

parameters.writer.closeObject();
return null;
}

Expand All @@ -106,26 +134,23 @@ class JSONGenerator {
visitField(field, parameters) {
const obj = parameters.stack.pop();
parameters.writer.writeKey(field.getName());
if(field.isArray()) {
if (field.isArray()) {
parameters.writer.openArray();
for(let n=0; n < obj.length; n++) {
for (let n = 0; n < obj.length; n++) {
const item = obj[n];
if(!field.isPrimitive() && !ModelUtil.isEnum(field)) {
if (!field.isPrimitive() && !ModelUtil.isEnum(field)) {
parameters.writer.writeComma();
parameters.stack.push(item, Typed);
const classDecl = parameters.modelManager.getType(item.getFullyQualifiedType());
classDecl.accept(this, parameters);
}
else {
parameters.writer.writeArrayValue(this.convertToJSON(field,item));
} else {
parameters.writer.writeArrayValue(this.convertToJSON(field, item));
}
}
parameters.writer.closeArray();
}
else if(field.isPrimitive() || ModelUtil.isEnum(field)) {
parameters.writer.writeValue(this.convertToJSON(field,obj));
}
else {
} else if (field.isPrimitive() || ModelUtil.isEnum(field)) {
parameters.writer.writeValue(this.convertToJSON(field, obj));
} else {
parameters.stack.push(obj);
const classDeclaration = parameters.modelManager.getType(obj.getFullyQualifiedType());
classDeclaration.accept(this, parameters);
Expand All @@ -142,19 +167,22 @@ class JSONGenerator {
* @return {string} the text representation
*/
convertToJSON(field, obj) {
switch(field.getType()) {
case 'DateTime': {
return `"${obj.toISOString()}"`;
}
switch (field.getType()) {
case 'DateTime':
{
return `"${obj.toISOString()}"`;
}
case 'Integer':
case 'Long':
case 'Double':
case 'Boolean':{
return `${obj.toString()}`;
}
default: {
return JSON.stringify(obj.toString());
}
case 'Boolean':
{
return `${obj.toString()}`;
}
default:
{
return JSON.stringify(obj.toString());
}
}
}

Expand All @@ -169,14 +197,14 @@ class JSONGenerator {
const obj = parameters.stack.pop();
parameters.writer.writeKey(relationshipDeclaration.getName());

if(relationshipDeclaration.isArray()) {
if (relationshipDeclaration.isArray()) {
parameters.writer.openArray();
for(let n=0; n < obj.length; n++) {
for (let n = 0; n < obj.length; n++) {
const item = obj[n];
if (this.permitResourcesForRelationships && item instanceof Resource) {
let fqi = item.getFullyQualifiedIdentifier();
if (parameters.seenResources.has(fqi)) {
let relationshipText = this.getRelationshipText(relationshipDeclaration, item );
let relationshipText = this.getRelationshipText(relationshipDeclaration, item);
parameters.writer.writeStringValue(relationshipText);
} else {
parameters.seenResources.add(fqi);
Expand All @@ -187,16 +215,15 @@ class JSONGenerator {
parameters.seenResources.delete(fqi);
}
} else {
let relationshipText = this.getRelationshipText(relationshipDeclaration, item );
let relationshipText = this.getRelationshipText(relationshipDeclaration, item);
parameters.writer.writeArrayStringValue(relationshipText);
}
}
parameters.writer.closeArray();
}
else if (this.permitResourcesForRelationships && obj instanceof Resource) {
} else if (this.permitResourcesForRelationships && obj instanceof Resource) {
let fqi = obj.getFullyQualifiedIdentifier();
if (parameters.seenResources.has(fqi)) {
let relationshipText = this.getRelationshipText(relationshipDeclaration, obj );
let relationshipText = this.getRelationshipText(relationshipDeclaration, obj);
parameters.writer.writeStringValue(relationshipText);
} else {
parameters.seenResources.add(fqi);
Expand All @@ -206,24 +233,24 @@ class JSONGenerator {
parameters.seenResources.delete(fqi);
}
} else {
let relationshipText = this.getRelationshipText(relationshipDeclaration, obj );
let relationshipText = this.getRelationshipText(relationshipDeclaration, obj);
parameters.writer.writeStringValue(relationshipText);
}
return null;
}

/**
* Returns the persistent format for a relationship.
* @param {RelationshipDeclaration} relationshipDeclaration - the relationship being persisted
* @param {Identifiable} relationshipOrResource - the relationship or the resource
* @returns {string} the text to use to persist the relationship
*/
* Returns the persistent format for a relationship.
* @param {RelationshipDeclaration} relationshipDeclaration - the relationship being persisted
* @param {Identifiable} relationshipOrResource - the relationship or the resource
* @returns {string} the text to use to persist the relationship
*/
getRelationshipText(relationshipDeclaration, relationshipOrResource) {
if(relationshipOrResource instanceof Resource) {
if (relationshipOrResource instanceof Resource) {
const allowRelationships =
this.convertResourcesToRelationships || this.permitResourcesForRelationships;
if(!allowRelationships) {
throw new Error('Did not find a relationship for ' + relationshipDeclaration.getFullyQualifiedTypeName() + ' found ' + relationshipOrResource );
if (!allowRelationships) {
throw new Error('Did not find a relationship for ' + relationshipDeclaration.getFullyQualifiedTypeName() + ' found ' + relationshipOrResource);
}
}
return relationshipOrResource.toURI();
Expand Down
Loading