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

Dev/marchesini 4 #20

Closed
wants to merge 9 commits into from
1 change: 0 additions & 1 deletion bundles/org.eclipse.sirius.emfjson/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,3 @@ Import-Package: javax.xml.bind;version="0.0.0",
org.eclipse.emf.ecore.resource.impl;version="0.0.0",
org.eclipse.emf.ecore.util;version="0.0.0"
Automatic-Module-Name: org.eclipse.sirius.emfjson
Require-Bundle: javax.xml.bind;bundle-version="2.2.0"
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Comparator;
import java.util.Map;

import org.eclipse.emf.common.util.URI;
Expand Down Expand Up @@ -418,6 +419,12 @@ interface IEObjectHandler {
*/
String OPTION_ID_MANAGER = "OPTION_ID_MANAGER"; //$NON-NLS-1$

/**
* An option to provide a {@link Comparator} of {@link EStructuralFeature} to determine the order of the attributes
* in the serialized Json.
*/
Object OPTION_SAVE_FEATURES_ORDER_COMPARATOR = "OPTION_SAVE_FEATURES_ORDER_COMPARATOR"; //$NON-NLS-1$

/**
* Associate an ID to the {@link EObject}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.utils;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
Expand All @@ -22,6 +23,7 @@
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -38,6 +40,7 @@
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
Expand Down Expand Up @@ -422,7 +425,9 @@ private void deserializeData(JsonObject properties, EClass eClass, EObject eObje
if (properties != null) {
Set<Entry<String, JsonElement>> entrySet = properties.entrySet();
for (Entry<String, JsonElement> entry : entrySet) {
EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(entry.getKey());
EStructuralFeature eStructuralFeature = (this.extendedMetaData != null) ? //
this.extendedMetaData.getElement(eClass, eClass.getEPackage().getNsURI(), entry.getKey()) : //
eClass.getEStructuralFeature(entry.getKey());

if (eStructuralFeature instanceof EAttribute) {
this.deserializeEAttribute((EAttribute) eStructuralFeature, entry.getValue(), eObject);
Expand Down Expand Up @@ -884,23 +889,76 @@ private EObject loadEGenericType(JsonObject jsonObject) {
private void deserializeEAttribute(EAttribute eAttribute, JsonElement jsonElement, EObject eObject) {
EDataType dataType = eAttribute.getEAttributeType();
if (!eAttribute.isMany()) {
String newValue = this.getAsFlexibleString(jsonElement);
Object value = EcoreUtil.createFromString(dataType, newValue);
Object value = null;
if (this.isPojo(dataType)) {
value = new Gson().fromJson(jsonElement, eAttribute.getEType().getInstanceClass());
} else {
String newValue = this.getAsFlexibleString(jsonElement);
value = this.tryCreateDataTypeFromString(dataType, newValue);
}
this.helper.setValue(eObject, eAttribute, value);
} else {
JsonArray asJsonArray = this.getAsFlexibleArray(jsonElement);
Object eGet = this.helper.getValue(eObject, eAttribute);
if (eGet instanceof Collection<?>) {
for (JsonElement jElement : asJsonArray) {
Object value = EcoreUtil.createFromString(dataType, jElement.getAsString());
@SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) eGet;
collection.add(value);
Object value = null;
if (this.isPojo(dataType)) {
value = new Gson().fromJson(jElement, eAttribute.getEType().getInstanceClass());
} else {
value = this.tryCreateDataTypeFromString(dataType, jElement.getAsString());
}
this.helper.setValue(eObject, eAttribute, value);
}
}
}
}

/**
* List of the datatypes defined by the {@link EcorePackage}.
*/
private static final List<EDataType> ECORE_PACKAGE_DATA_TYPES = Arrays.asList(EcorePackage.eINSTANCE.getEString(), EcorePackage.eINSTANCE.getEBoolean(), EcorePackage.eINSTANCE.getEBooleanObject(),
EcorePackage.eINSTANCE.getEInt(), EcorePackage.eINSTANCE.getEIntegerObject(), EcorePackage.eINSTANCE.getEBigDecimal(), EcorePackage.eINSTANCE.getEBigInteger(),
EcorePackage.eINSTANCE.getEByte(), EcorePackage.eINSTANCE.getEByteArray(), EcorePackage.eINSTANCE.getEByteObject(), EcorePackage.eINSTANCE.getEChar(),
EcorePackage.eINSTANCE.getECharacterObject(), EcorePackage.eINSTANCE.getEDate(), EcorePackage.eINSTANCE.getEDouble(), EcorePackage.eINSTANCE.getEDoubleObject(),
EcorePackage.eINSTANCE.getEFloat(), EcorePackage.eINSTANCE.getEFloatObject(), EcorePackage.eINSTANCE.getELong(), EcorePackage.eINSTANCE.getELongObject(),
EcorePackage.eINSTANCE.getEShort(), EcorePackage.eINSTANCE.getEShortObject());

/**
* Tests if the given {@link EDataType} is a POJO.
*
* @param dataType
* A {@link EDataType} to test
* @return true if the given {@link EDataType} is not one of the {@link EcorePackage} DataTypes nor an
* {@link EEnum}.
*/
private boolean isPojo(EDataType dataType) {
return !ECORE_PACKAGE_DATA_TYPES.contains(dataType) && //
dataType.eClass() != EcorePackage.eINSTANCE.getEEnum() && //
dataType.getInstanceClass() != null;
}

/**
* Try to create a {@link EDataType} instance from a serialized form of the value. If the serialization is not
* compatible with the given EDataType, returns the serialized form of the value unchanged.
*
* @param dataType
* The {@link EDataType} to instantiate
* @param serializedValue
* The serialized form of the value
* @return A new EDataType instance or the given serializedValue if the EDataType could not be instantiated from the
* serializedValue.
*/
private Object tryCreateDataTypeFromString(EDataType dataType, String serializedValue) {
Object value;
try {
value = EcoreUtil.createFromString(dataType, serializedValue);
} catch (IllegalArgumentException e) {
value = serializedValue;
}
return value;
}

/**
* Read a JSON element as a string. If the element is a non-empty array, unwrap it and consider the first element
* inside instead of trying to convert the array into a string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.sirius.emfjson.utils;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand All @@ -24,12 +25,14 @@
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import javax.xml.bind.DatatypeConverter;

Expand Down Expand Up @@ -863,10 +866,18 @@ private JsonElement createJsonHeader() {
* The EObject to serialize
* @return A JsonObject containing all the properties of the given object
*/
@SuppressWarnings("unchecked")
private JsonObject serializeEAllStructuralFeatures(EObject eObject) {
JsonObject properties = new JsonObject();
EClass eClass = eObject.eClass();
EList<EStructuralFeature> eAllStructuralFeatures = eClass.getEAllStructuralFeatures();
List<EStructuralFeature> eAllStructuralFeatures = eClass.getEAllStructuralFeatures();

Object orderFeatures = this.options.get(JsonResource.OPTION_SAVE_FEATURES_ORDER_COMPARATOR);
if (orderFeatures instanceof Comparator<?>) {
eAllStructuralFeatures = eAllStructuralFeatures.stream() //
.sorted((Comparator<? super EStructuralFeature>) orderFeatures) //
.collect(Collectors.toList());
}

for (EStructuralFeature eStructuralFeature : eAllStructuralFeatures) {
if (this.shouldSerialize(eObject, eStructuralFeature)) {
Expand Down Expand Up @@ -1017,22 +1028,31 @@ private JsonElement serializeEDataType(EObject eObject, EAttribute eAttribute) {

Object value = this.helper.getValue(eObject, eAttribute);
EFactory eFactoryInstance = eAttribute.getEType().getEPackage().getEFactoryInstance();
boolean isPojo = eAttribute.getEAttributeType().getInstanceClass() != null;
if (eAttribute.isMany()) {
JsonArray jsonArray = new JsonArray();
if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
for (Object object : collection) {
jsonArray.add(new JsonPrimitive(eFactoryInstance.convertToString(eAttribute.getEAttributeType(), object)));
if (isPojo) {
jsonArray.add(new Gson().toJsonTree(object));
} else {
jsonArray.add(new JsonPrimitive(eFactoryInstance.convertToString(eAttribute.getEAttributeType(), object)));
}
}
}
jsonElement = jsonArray;
} else {
String stringValue = eFactoryInstance.convertToString(eAttribute.getEAttributeType(), value);

if (stringValue == null) {
stringValue = ""; //$NON-NLS-1$
if (isPojo) {
jsonElement = new Gson().toJsonTree(value);
} else {
String stringValue = eFactoryInstance.convertToString(eAttribute.getEAttributeType(), value);
if (stringValue == null) {
stringValue = ""; //$NON-NLS-1$
}
jsonElement = new JsonPrimitive(stringValue);
}
jsonElement = new JsonPrimitive(stringValue);

}

return jsonElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package org.eclipse.sirius.emfjson.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -581,7 +582,8 @@ public EClassifier getType(EFactory eFactory, String typeName) {
}

/**
* Sets the value of the feature for the given object.
* Sets the value of the feature for the given object. If the feature is multivalued, add the value to the
* collection of values already set.
*
* @param object
* the given object
Expand All @@ -591,7 +593,13 @@ public EClassifier getType(EFactory eFactory, String typeName) {
* the value to set
*/
public void setValue(EObject object, EStructuralFeature feature, Object value) {
object.eSet(feature, value);
if (feature.isMany()) {
@SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) object.eGet(feature);
collection.add(value);
} else {
object.eSet(feature, value);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.sirius.emfjson.resource.JsonResourceFactoryImpl;
import org.eclipse.sirius.emfjson.resource.JsonResourceImpl;
import org.eclipse.sirius.emfjson.utils.JsonHelper;
import org.junit.Assert;

/**
Expand Down Expand Up @@ -276,6 +277,10 @@ public Resource createResource(URI uriValue) {
resource = resourceSet.createResource(uri);
String extension = resource.getURI().fileExtension();
if (extension.equals(JsonResourceFactoryImpl.EXTENSION)) {
if (this.options.get(JsonResource.OPTION_CUSTOM_HELPER) instanceof JsonHelper) {
JsonHelper jsonHelper = (JsonHelper) this.options.get(JsonResource.OPTION_CUSTOM_HELPER);
jsonHelper.setResource(resource);
}
resource.load(inputStream, this.options);
} else {
resource.load(inputStream, Collections.EMPTY_MAP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,20 @@ public void testMultipleSerialiationDataType() {
this.testLoad("NodeMultipleCustomDataType.xmi"); //$NON-NLS-1$
}

/**
* Test deserialization of POJO EDataType EAttribute monovalued.
*/
@Test
public void testLoadSingleValueAttributePojoDataType() {
this.testLoad("NodeSingleValueAttributePojoDataType.xmi"); //$NON-NLS-1$
}

/**
* Test deserialization of POJO EDataType EAttribute multivalued.
*/
@Test
public void testLoadMultiValuedAttributePojoDataType() {
this.testLoad("NodeMultiValuedAttributePojoDataType.xmi"); //$NON-NLS-1$
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.emfjson.tests.internal.unit.load;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.BasicExtendedMetaData;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.sirius.emfjson.tests.internal.AbstractEMFJsonTests;
import org.junit.Test;

/**
* Tests loading with ExtendedMetaData.
*/
public class ExtendedMetaDataAttributesLoadTests extends AbstractEMFJsonTests {

/**
* {@inheritDoc}
*
* @see org.eclipse.sirius.emfjson.tests.internal.AbstractEMFJsonTests#getRootPath()
*/
@Override
protected String getRootPath() {
return "/unit/references/extendedmetadata/"; //$NON-NLS-1$
}

/**
* Change the name of a monovalued EReference.
*/
@Test
public void testChangeReferenceNameMono() {
// CHECKSTYLE:OFF
ExtendedMetaData metaData = new BasicExtendedMetaData() {

/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.util.BasicExtendedMetaData#getElement(org.eclipse.emf.ecore.EClass,
* java.lang.String, java.lang.String)
*/
@Override
public EStructuralFeature getElement(EClass eClass, String namespace, String name) {
if ("NodeSingleValueReference".equals(eClass.getName()) && "singleValuedReferenceOld".equals(name)) {
// $NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("singleValuedReference"); //$NON-NLS-1$
}
// return super.getElement(eClass, namespace, name); // Doesn't work
return eClass.getEStructuralFeature(name);
}

};
// CHECKSTYLE:ON
this.options.put(JsonResource.OPTION_EXTENDED_META_DATA, metaData);
this.testLoad("TestChangeReferenceNameMono.xmi"); //$NON-NLS-1$
}

/**
* Change the name of a multivalued EReference.
*/
@Test
public void testChangeReferenceNameMulti() {
// CHECKSTYLE:OFF
ExtendedMetaData metaData = new BasicExtendedMetaData() {

/**
* {@inheritDoc}
*
* @see org.eclipse.emf.ecore.util.BasicExtendedMetaData#getElement(org.eclipse.emf.ecore.EClass,
* java.lang.String, java.lang.String)
*/
@Override
public EStructuralFeature getElement(EClass eClass, String namespace, String name) {
if ("NodeMultiValuedAttribute".equals(eClass.getName()) && "multiStringAttributeOld".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
return eClass.getEStructuralFeature("multiStringAttribute"); //$NON-NLS-1$
}
// return super.getElement(eClass, namespace, name); // Doesn't work
return eClass.getEStructuralFeature(name);
}

};
// CHECKSTYLE:ON
this.options.put(JsonResource.OPTION_EXTENDED_META_DATA, metaData);
this.testLoad("TestChangeReferenceNameMulti.xmi"); //$NON-NLS-1$
}

}
Loading