You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2017/03/13 15:28:44 UTC
[21/48] polygene-java git commit: New (de)serialization API and SPI &
new implementations
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
index dc4db58..ef9816c 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java
@@ -14,12 +14,9 @@
* 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.
- *
- *
*/
package org.apache.polygene.spi.entitystore.helpers;
-import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.time.Instant;
@@ -28,7 +25,15 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Stream;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonValue;
import org.apache.polygene.api.common.Optional;
import org.apache.polygene.api.common.QualifiedName;
import org.apache.polygene.api.entity.EntityDescriptor;
@@ -39,15 +44,11 @@ import org.apache.polygene.api.identity.StringIdentity;
import org.apache.polygene.api.injection.scope.Service;
import org.apache.polygene.api.injection.scope.Structure;
import org.apache.polygene.api.injection.scope.This;
-import org.apache.polygene.api.service.qualifier.Tagged;
import org.apache.polygene.api.structure.Application;
import org.apache.polygene.api.structure.ModuleDescriptor;
-import org.apache.polygene.api.type.ValueType;
import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException;
import org.apache.polygene.api.usecase.Usecase;
-import org.apache.polygene.api.value.ValueSerialization;
-import org.apache.polygene.api.value.ValueSerializationException;
-import org.apache.polygene.spi.PolygeneSPI;
+import org.apache.polygene.serialization.javaxjson.JavaxJson;
import org.apache.polygene.spi.entity.EntityState;
import org.apache.polygene.spi.entity.EntityStatus;
import org.apache.polygene.spi.entitystore.DefaultEntityStoreUnitOfWork;
@@ -56,11 +57,7 @@ import org.apache.polygene.spi.entitystore.EntityStoreException;
import org.apache.polygene.spi.entitystore.EntityStoreSPI;
import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork;
import org.apache.polygene.spi.entitystore.StateCommitter;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONTokener;
-import org.json.JSONWriter;
+import org.apache.polygene.spi.serialization.JsonSerialization;
/**
* Implementation of EntityStore that works with an implementation of MapEntityStore.
@@ -80,14 +77,10 @@ public class MapEntityStoreMixin
private EntityStoreSPI entityStoreSpi;
@Structure
- private PolygeneSPI spi;
-
- @Structure
private Application application;
@Service
- @Tagged( ValueSerialization.Formats.JSON )
- private ValueSerialization valueSerialization;
+ private JsonSerialization jsonSerialization;
@Optional
@Service
@@ -97,58 +90,70 @@ public class MapEntityStoreMixin
private IdentityGenerator identityGenerator;
@Override
- public void activateMapEntityStore()
- throws Exception
- {
- }
+ public void activateMapEntityStore() {}
// EntityStore
@Override
- public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecaseMetaInfo, Instant currentTime )
+ public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecase, Instant currentTime )
{
- return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(), usecaseMetaInfo, currentTime );
+ return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(),
+ usecase, currentTime );
}
// EntityStoreSPI
@Override
- public EntityState newEntityState( EntityStoreUnitOfWork unitOfWork,
- EntityReference reference,
- EntityDescriptor entityDescriptor
- )
+ public EntityState newEntityState( EntityStoreUnitOfWork uow,
+ EntityReference reference, EntityDescriptor entityDescriptor )
{
- return new DefaultEntityState( unitOfWork.currentTime(), reference, entityDescriptor );
+ return new DefaultEntityState( uow.currentTime(), reference, entityDescriptor );
}
@Override
- public synchronized EntityState entityStateOf( EntityStoreUnitOfWork unitofwork,
- ModuleDescriptor module,
- EntityReference reference
- )
+ public synchronized EntityState entityStateOf( EntityStoreUnitOfWork uow,
+ ModuleDescriptor module, EntityReference reference )
{
- Reader in = mapEntityStore.get( reference );
- return readEntityState( module, in );
+ try
+ {
+ Reader in = mapEntityStore.get( reference );
+ EntityState loadedState = readEntityState( module, in );
+ if( loadedState.status() == EntityStatus.UPDATED )
+ {
+ List<EntityState> migrated = new ArrayList<>( 1 );
+ migrated.add( loadedState );
+ synchMigratedEntities( migrated );
+ }
+ return loadedState;
+ }
+ catch( EntityStoreException ex )
+ {
+ throw ex;
+ }
+ catch( Exception ex )
+ {
+ throw new EntityStoreException( ex );
+ }
}
@Override
- public synchronized String versionOf( EntityStoreUnitOfWork unitofwork,
- EntityReference reference
- )
+ public synchronized String versionOf( EntityStoreUnitOfWork uow, EntityReference reference )
{
- Reader in = mapEntityStore.get( reference );
try
{
- JSONObject jsonObject = new JSONObject( new JSONTokener( in ) );
- return jsonObject.getString( JSONKeys.VERSION );
+ Reader in = mapEntityStore.get( reference );
+ return Json.createReader( in ).readObject().getString( JSONKeys.VERSION );
}
- catch( JSONException e )
+ catch( EntityStoreException ex )
{
- throw new EntityStoreException( e );
+ throw ex;
+ }
+ catch( Exception ex )
+ {
+ throw new EntityStoreException( ex );
}
}
@Override
- public StateCommitter applyChanges( final EntityStoreUnitOfWork unitofwork, final Iterable<EntityState> state
- )
+ public StateCommitter applyChanges( EntityStoreUnitOfWork uow, Iterable<EntityState> state )
throws EntityStoreException
{
return new StateCommitter()
@@ -158,34 +163,47 @@ public class MapEntityStoreMixin
{
try
{
- mapEntityStore.applyChanges( changer -> {
- for( EntityState entityState : state )
+ mapEntityStore.applyChanges(
+ changer ->
{
- DefaultEntityState state1 = (DefaultEntityState) entityState;
- if( state1.status().equals( EntityStatus.NEW ) )
+ for( EntityState entityState : state )
{
- try (Writer writer = changer.newEntity( state1.entityReference(), state1.entityDescriptor() ))
+ DefaultEntityState state1 = (DefaultEntityState) entityState;
+ String newVersion = uow.identity().toString();
+ Instant lastModified = uow.currentTime();
+ if( state1.status().equals( EntityStatus.NEW ) )
{
- writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() );
+ try( Writer writer = changer.newEntity( state1.entityReference(),
+ state1.entityDescriptor() ) )
+ {
+ writeEntityState( state1, writer, newVersion, lastModified );
+ }
}
- }
- else if( state1.status().equals( EntityStatus.UPDATED ) )
- {
- try (Writer writer = changer.updateEntity( state1.entityReference(), state1.entityDescriptor() ))
+ else if( state1.status().equals( EntityStatus.UPDATED ) )
{
- writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() );
+ MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange(
+ state1.entityReference(), state1.entityDescriptor(),
+ state1.version(), newVersion, lastModified
+ );
+ try( Writer writer = changer.updateEntity( mapChange ) )
+ {
+ writeEntityState( state1, writer, newVersion, lastModified );
+ }
+ }
+ else if( state1.status().equals( EntityStatus.REMOVED ) )
+ {
+ changer.removeEntity( state1.entityReference(), state1.entityDescriptor() );
}
}
- else if( state1.status().equals( EntityStatus.REMOVED ) )
- {
- changer.removeEntity( state1.entityReference(), state1.entityDescriptor() );
- }
- }
- } );
+ } );
+ }
+ catch( EntityStoreException ex )
+ {
+ throw ex;
}
- catch( IOException e )
+ catch( Exception ex )
{
- throw new EntityStoreException( e );
+ throw new EntityStoreException( ex );
}
}
@@ -197,62 +215,90 @@ public class MapEntityStoreMixin
}
@Override
- public Stream<EntityState> entityStates( final ModuleDescriptor module )
+ public Stream<EntityState> entityStates( ModuleDescriptor module )
{
- List<EntityState> migrated = new ArrayList<>();
- return mapEntityStore
- .entityStates()
- .map( reader ->
- {
- EntityState entity = readEntityState( module, reader );
- if( entity.status() == EntityStatus.UPDATED )
- {
- migrated.add( entity );
- // Synch back 100 at a time
- if( migrated.size() > 100 )
- {
- try
- {
- synchMigratedEntities( migrated );
- }
- catch( IOException e )
- {
- throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e );
- }
- }
- }
- return entity;
- } )
- .onClose( () ->
- {
- // Synch any remaining migrated entities
- if( !migrated.isEmpty() )
- {
- try
- {
- synchMigratedEntities( migrated );
- }
- catch( IOException e )
- {
- throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e );
- }
- }
- } );
+ try
+ {
+ Stream<Reader> stateStream = mapEntityStore.entityStates();
+ List<EntityState> migrated = new ArrayList<>();
+ String migrationErrorMsg = "Synchronization of Migrated Entities failed.";
+ Function<Reader, EntityState> function = reader ->
+ {
+ EntityState entity = readEntityState( module, reader );
+ if( entity.status() == EntityStatus.UPDATED )
+ {
+ migrated.add( entity );
+ // Sync back 100 at a time
+ if( migrated.size() > 100 )
+ {
+ try
+ {
+ synchMigratedEntities( migrated );
+ }
+ catch( EntityStoreException ex )
+ {
+ throw ex;
+ }
+ catch( Exception ex )
+ {
+ throw new EntityStoreException( migrationErrorMsg, ex );
+ }
+ }
+ }
+ return entity;
+ };
+ Runnable closer = () ->
+ {
+ // Sync any remaining migrated entities
+ if( !migrated.isEmpty() )
+ {
+ try
+ {
+ synchMigratedEntities( migrated );
+ }
+ catch( EntityStoreException ex )
+ {
+ throw ex;
+ }
+ catch( Exception ex )
+ {
+ throw new EntityStoreException( migrationErrorMsg, ex );
+ }
+ }
+ };
+ return stateStream.map( function ).onClose( closer );
+ }
+ catch( EntityStoreException ex )
+ {
+ throw ex;
+ }
+ catch( Exception ex )
+ {
+ throw new EntityStoreException( ex );
+ }
}
private void synchMigratedEntities( final List<EntityState> migratedEntities )
- throws IOException
+ throws Exception
{
- mapEntityStore.applyChanges( changer -> {
- for( EntityState migratedEntity : migratedEntities )
+ mapEntityStore.applyChanges(
+ changer ->
{
- DefaultEntityState state = (DefaultEntityState) migratedEntity;
- try (Writer writer = changer.updateEntity( state.entityReference(), state.entityDescriptor() ))
+ for( EntityState migratedEntity : migratedEntities )
{
- writeEntityState( state, writer, state.version(), state.lastModified() );
+ DefaultEntityState state = (DefaultEntityState) migratedEntity;
+ String version = state.version();
+ Instant lastModified = state.lastModified();
+ MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange(
+ state.entityReference(), state.entityDescriptor(),
+ version, version, lastModified
+ );
+ try( Writer writer = changer.updateEntity( mapChange ) )
+ {
+ writeEntityState( state, writer, version, lastModified );
+ }
}
- }
- } );
+ } );
migratedEntities.clear();
}
@@ -261,87 +307,63 @@ public class MapEntityStoreMixin
return identityGenerator.generate( EntityStore.class );
}
- protected void writeEntityState(DefaultEntityState state, Writer writer, String version, Instant lastModified )
+ protected void writeEntityState( DefaultEntityState state, Writer writer, String version, Instant lastModified )
throws EntityStoreException
{
try
{
- JSONWriter json = new JSONWriter( writer );
- JSONWriter properties = json.object().
- key( JSONKeys.IDENTITY ).value( state.entityReference().identity() ).
- key( JSONKeys.APPLICATION_VERSION ).value( application.version() ).
- key( JSONKeys.TYPE ).value( state.entityDescriptor().types().findFirst().get().getName() ).
- key( JSONKeys.VERSION ).value( version ).
- key( JSONKeys.MODIFIED ).value( lastModified.toEpochMilli() ).
- key( JSONKeys.PROPERTIES ).object();
+ JsonObjectBuilder json = Json.createObjectBuilder();
+ json.add( JSONKeys.IDENTITY, state.entityReference().identity().toString() );
+ json.add( JSONKeys.APPLICATION_VERSION, application.version() );
+ json.add( JSONKeys.TYPE, state.entityDescriptor().primaryType().getName() );
+ json.add( JSONKeys.VERSION, version );
+ json.add( JSONKeys.MODIFIED, lastModified.toEpochMilli() );
+ JsonObjectBuilder properties = Json.createObjectBuilder();
EntityDescriptor entityType = state.entityDescriptor();
- entityType.state().properties().forEach( persistentProperty -> {
- Object value = state.properties().get( persistentProperty.qualifiedName() );
- try
- {
- json.key( persistentProperty.qualifiedName().name() );
- if( value == null || ValueType.isPrimitiveValue( value ) )
- {
- json.value( value );
- }
- else
- {
- String serialized = valueSerialization.serialize( value );
- if( serialized.startsWith( "{" ) )
- {
- json.value( new JSONObject( serialized ) );
- }
- else if( serialized.startsWith( "[" ) )
- {
- json.value( new JSONArray( serialized ) );
- }
- else
- {
- json.value( serialized );
- }
- }
- }
- catch( JSONException e )
+ entityType.state().properties().forEach(
+ persistentProperty ->
{
- throw new ValueSerializationException( "Unable to write property " + persistentProperty, e );
- }
- } );
-
- JSONWriter associations = properties.endObject().key( JSONKeys.ASSOCIATIONS ).object();
- for( Map.Entry<QualifiedName, EntityReference> stateNameEntityReferenceEntry : state.associations()
- .entrySet() )
+ Object value = state.properties().get( persistentProperty.qualifiedName() );
+ JsonValue jsonValue = jsonSerialization.toJson( value );
+ properties.add( persistentProperty.qualifiedName().name(), jsonValue );
+ } );
+ json.add( JSONKeys.PROPERTIES, properties.build() );
+
+ JsonObjectBuilder associations = Json.createObjectBuilder();
+ for( Map.Entry<QualifiedName, EntityReference> entry : state.associations().entrySet() )
{
- EntityReference value = stateNameEntityReferenceEntry.getValue();
- associations.key( stateNameEntityReferenceEntry.getKey().name() ).
- value( value != null ? value.identity() : null );
+ EntityReference value = entry.getValue();
+ associations.add( entry.getKey().name(), value == null ? null : value.identity().toString() );
}
+ json.add( JSONKeys.ASSOCIATIONS, associations.build() );
- JSONWriter manyAssociations = associations.endObject().key( JSONKeys.MANY_ASSOCIATIONS ).object();
- for( Map.Entry<QualifiedName, List<EntityReference>> stateNameListEntry : state.manyAssociations()
- .entrySet() )
+ JsonObjectBuilder manyAssociations = Json.createObjectBuilder();
+ for( Map.Entry<QualifiedName, List<EntityReference>> entry : state.manyAssociations().entrySet() )
{
- JSONWriter assocs = manyAssociations.key( stateNameListEntry.getKey().name() ).array();
- for( EntityReference entityReference : stateNameListEntry.getValue() )
+ JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+ for( EntityReference entityReference : entry.getValue() )
{
- assocs.value( entityReference.identity() );
+ arrayBuilder.add( entityReference.identity().toString() );
}
- assocs.endArray();
+ manyAssociations.add( entry.getKey().name(), arrayBuilder.build() );
}
+ json.add( JSONKeys.MANY_ASSOCIATIONS, manyAssociations.build() );
- JSONWriter namedAssociations = manyAssociations.endObject().key( JSONKeys.NAMED_ASSOCIATIONS ).object();
- for( Map.Entry<QualifiedName, Map<String, EntityReference>> stateNameMapEntry : state.namedAssociations()
- .entrySet() )
+ JsonObjectBuilder namedAssociations = Json.createObjectBuilder();
+ for( Map.Entry<QualifiedName, Map<String, EntityReference>> entry : state.namedAssociations().entrySet() )
{
- JSONWriter assocs = namedAssociations.key( stateNameMapEntry.getKey().name() ).object();
- for( Map.Entry<String, EntityReference> namedRef : stateNameMapEntry.getValue().entrySet() )
+ JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+ for( Map.Entry<String, EntityReference> namedRef : entry.getValue().entrySet() )
{
- assocs.key( namedRef.getKey() ).value( namedRef.getValue().identity() );
+ objectBuilder.add( namedRef.getKey(), namedRef.getValue().identity().toString() );
}
- assocs.endObject();
+ namedAssociations.add( entry.getKey().name(), objectBuilder.build() );
}
- namedAssociations.endObject().endObject();
+ json.add( JSONKeys.NAMED_ASSOCIATIONS, namedAssociations.build() );
+ JsonObject jsonState = json.build();
+ Json.createWriter( writer ).write( jsonState );
}
- catch( JSONException e )
+ catch( Exception e )
{
throw new EntityStoreException( "Could not store EntityState", e );
}
@@ -352,31 +374,38 @@ public class MapEntityStoreMixin
{
try
{
- JSONObject jsonObject = new JSONObject( new JSONTokener( entityState ) );
+ JsonObject parsedState = Json.createReader( entityState ).readObject();
+ JsonObjectBuilder jsonStateBuilder = JavaxJson.toBuilder( parsedState );
final EntityStatus[] status = { EntityStatus.LOADED };
- String version = jsonObject.getString( JSONKeys.VERSION );
- Instant modified = Instant.ofEpochMilli(jsonObject.getLong( JSONKeys.MODIFIED ));
- Identity identity = new StringIdentity(jsonObject.getString( JSONKeys.IDENTITY ));
+ String version = parsedState.getString( JSONKeys.VERSION );
+ Instant modified = Instant.ofEpochMilli( parsedState.getJsonNumber( JSONKeys.MODIFIED ).longValueExact() );
+ Identity identity = new StringIdentity( parsedState.getString( JSONKeys.IDENTITY ) );
// Check if version is correct
- String currentAppVersion = jsonObject.optString( JSONKeys.APPLICATION_VERSION, "0.0" );
- if( !currentAppVersion.equals( application.version() ) )
+ JsonObject state;
+ String currentAppVersion = parsedState.getString( JSONKeys.APPLICATION_VERSION, "0.0" );
+ if( currentAppVersion.equals( application.version() ) )
+ {
+ state = jsonStateBuilder.build();
+ }
+ else
{
if( migration != null )
{
- migration.migrate( jsonObject, application.version(), this );
+ state = migration.migrate( jsonStateBuilder.build(), application.version(), this );
}
else
{
// Do nothing - set version to be correct
- jsonObject.put( JSONKeys.APPLICATION_VERSION, application.version() );
+ jsonStateBuilder.add( JSONKeys.APPLICATION_VERSION, application.version() );
+ state = jsonStateBuilder.build();
}
// State changed
status[ 0 ] = EntityStatus.UPDATED;
}
- String type = jsonObject.getString( JSONKeys.TYPE );
+ String type = state.getString( JSONKeys.TYPE );
EntityDescriptor entityDescriptor = module.entityDescriptor( type );
if( entityDescriptor == null )
@@ -385,107 +414,101 @@ public class MapEntityStoreMixin
}
Map<QualifiedName, Object> properties = new HashMap<>();
- JSONObject props = jsonObject.getJSONObject( JSONKeys.PROPERTIES );
- entityDescriptor.state().properties().forEach( propertyDescriptor -> {
- Object jsonValue;
- try
+ JsonObject props = state.getJsonObject( JSONKeys.PROPERTIES );
+ entityDescriptor.state().properties().forEach(
+ property ->
{
- jsonValue = props.get( propertyDescriptor.qualifiedName().name() );
- if( JSONObject.NULL.equals( jsonValue ) )
+ try
{
- properties.put( propertyDescriptor.qualifiedName(), null );
+ JsonValue jsonValue = props.get( property.qualifiedName().name() );
+ Object value = jsonSerialization.fromJson( module, property.valueType(), jsonValue );
+ properties.put( property.qualifiedName(), value );
}
- else
+ catch( JsonException e )
{
- Object value = valueSerialization.deserialize( module, propertyDescriptor.valueType(), jsonValue
- .toString() );
- properties.put( propertyDescriptor.qualifiedName(), value );
+ // Value not found, default it
+ Object initialValue = property.resolveInitialValue( module );
+ properties.put( property.qualifiedName(), initialValue );
+ status[ 0 ] = EntityStatus.UPDATED;
}
- }
- catch( JSONException e )
- {
- // Value not found, default it
- Object initialValue = propertyDescriptor.resolveInitialValue(module);
- properties.put( propertyDescriptor.qualifiedName(), initialValue );
- status[ 0 ] = EntityStatus.UPDATED;
- }
- } );
+ } );
Map<QualifiedName, EntityReference> associations = new HashMap<>();
- JSONObject assocs = jsonObject.getJSONObject( JSONKeys.ASSOCIATIONS );
- entityDescriptor.state().associations().forEach( associationType -> {
- try
+ JsonObject assocs = state.getJsonObject( JSONKeys.ASSOCIATIONS );
+ entityDescriptor.state().associations().forEach(
+ association ->
{
- Object jsonValue = assocs.get( associationType.qualifiedName().name() );
- EntityReference value = jsonValue == JSONObject.NULL
- ? null
- : EntityReference.parseEntityReference( (String) jsonValue );
- associations.put( associationType.qualifiedName(), value );
- }
- catch( JSONException e )
- {
- // Association not found, default it to null
- associations.put( associationType.qualifiedName(), null );
- status[ 0 ] = EntityStatus.UPDATED;
- }
- } );
+ try
+ {
+ String jsonValue = assocs.getString( association.qualifiedName().name(), null );
+ EntityReference value = jsonValue == null
+ ? null
+ : EntityReference.parseEntityReference( jsonValue );
+ associations.put( association.qualifiedName(), value );
+ }
+ catch( JsonException e )
+ {
+ // Association not found, default it to null
+ associations.put( association.qualifiedName(), null );
+ status[ 0 ] = EntityStatus.UPDATED;
+ }
+ } );
- JSONObject manyAssocs = jsonObject.getJSONObject( JSONKeys.MANY_ASSOCIATIONS );
+ JsonObject manyAssocs = state.getJsonObject( JSONKeys.MANY_ASSOCIATIONS );
Map<QualifiedName, List<EntityReference>> manyAssociations = new HashMap<>();
- entityDescriptor.state().manyAssociations().forEach( manyAssociationType -> {
- List<EntityReference> references = new ArrayList<>();
- try
+ entityDescriptor.state().manyAssociations().forEach(
+ association ->
{
- JSONArray jsonValues = manyAssocs.getJSONArray( manyAssociationType.qualifiedName().name() );
- for( int i = 0; i < jsonValues.length(); i++ )
+ List<EntityReference> references = new ArrayList<>();
+ try
{
- Object jsonValue = jsonValues.getString( i );
- EntityReference value = jsonValue == JSONObject.NULL
- ? null
- : EntityReference.parseEntityReference( (String) jsonValue );
- references.add( value );
+ JsonArray jsonValues = manyAssocs.getJsonArray( association.qualifiedName().name() );
+ for( int i = 0; i < jsonValues.size(); i++ )
+ {
+ String jsonValue = jsonValues.getString( i, null );
+ EntityReference value = jsonValue == null
+ ? null
+ : EntityReference.parseEntityReference( jsonValue );
+ references.add( value );
+ }
+ manyAssociations.put( association.qualifiedName(), references );
}
- manyAssociations.put( manyAssociationType.qualifiedName(), references );
- }
- catch( JSONException e )
- {
- // ManyAssociation not found, default to empty one
- manyAssociations.put( manyAssociationType.qualifiedName(), references );
- }
- } );
+ catch( JsonException e )
+ {
+ // ManyAssociation not found, default to empty one
+ manyAssociations.put( association.qualifiedName(), references );
+ }
+ } );
- JSONObject namedAssocs = jsonObject.getJSONObject( JSONKeys.NAMED_ASSOCIATIONS );
+ JsonObject namedAssocs = state.getJsonObject( JSONKeys.NAMED_ASSOCIATIONS );
Map<QualifiedName, Map<String, EntityReference>> namedAssociations = new HashMap<>();
- entityDescriptor.state().namedAssociations().forEach( namedAssociationType -> {
- Map<String, EntityReference> references = new LinkedHashMap<>();
- try
+ entityDescriptor.state().namedAssociations().forEach(
+ association ->
{
- JSONObject jsonValues = namedAssocs.getJSONObject( namedAssociationType.qualifiedName().name() );
- JSONArray names = jsonValues.names();
- if( names != null )
+ Map<String, EntityReference> references = new LinkedHashMap<>();
+ try
{
- for( int idx = 0; idx < names.length(); idx++ )
+ JsonObject jsonValues = namedAssocs.getJsonObject( association.qualifiedName().name() );
+ for( String name : jsonValues.keySet() )
{
- String name = names.getString( idx );
- Object value = jsonValues.get( name );
- EntityReference ref = value == JSONObject.NULL
+ String value = jsonValues.getString( name, null );
+ EntityReference ref = value == null
? null
- : EntityReference.parseEntityReference( (String) value );
+ : EntityReference.parseEntityReference( value );
references.put( name, ref );
}
+ namedAssociations.put( association.qualifiedName(), references );
}
- namedAssociations.put( namedAssociationType.qualifiedName(), references );
- }
- catch( JSONException e )
- {
- // NamedAssociation not found, default to empty one
- namedAssociations.put( namedAssociationType.qualifiedName(), references );
- }
- } );
+ catch( JsonException e )
+ {
+ // NamedAssociation not found, default to empty one
+ namedAssociations.put( association.qualifiedName(), references );
+ }
+ } );
return new DefaultEntityState( version,
modified,
- EntityReference.create(identity),
+ EntityReference.create( identity ),
status[ 0 ],
entityDescriptor,
properties,
@@ -494,25 +517,26 @@ public class MapEntityStoreMixin
namedAssociations
);
}
- catch( JSONException e )
+ catch( Exception e )
{
throw new EntityStoreException( e );
}
}
@Override
- public JSONObject jsonStateOf( String id )
- throws IOException
+ public JsonObject jsonStateOf( String id )
{
- JSONObject jsonObject;
- try (Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) ))
+ try( Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) ) )
+ {
+ return Json.createReader( reader ).readObject();
+ }
+ catch( EntityStoreException ex )
{
- jsonObject = new JSONObject( new JSONTokener( reader ) );
+ throw ex;
}
- catch( JSONException e )
+ catch( Exception ex )
{
- throw new IOException( e );
+ throw new EntityStoreException( ex );
}
- return jsonObject;
}
}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
index 4b5e8e6..6a8ebfb 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java
@@ -20,14 +20,14 @@
package org.apache.polygene.spi.entitystore.helpers;
-import org.json.JSONException;
-import org.json.JSONObject;
+import javax.json.JsonException;
+import javax.json.JsonObject;
/**
* State Migration SPI.
*/
public interface Migration
{
- boolean migrate( JSONObject state, String toVersion, StateStore stateStore )
- throws JSONException;
+ JsonObject migrate( JsonObject state, String toVersion, StateStore stateStore )
+ throws JsonException;
}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
index 1f1f728..8aa9e6e 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java
@@ -20,14 +20,13 @@
package org.apache.polygene.spi.entitystore.helpers;
-import java.io.IOException;
-import org.json.JSONObject;
+import javax.json.JsonObject;
+import org.apache.polygene.spi.entitystore.EntityStoreException;
/**
* StateStore SPI.
*/
public interface StateStore
{
- JSONObject jsonStateOf( String id )
- throws IOException;
+ JsonObject jsonStateOf( String id ) throws EntityStoreException;
}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
index 8aa7c0d..eccc612 100644
--- a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
+++ b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java
@@ -21,10 +21,10 @@ package org.apache.polygene.spi.module;
import org.apache.polygene.api.identity.IdentityGenerator;
import org.apache.polygene.api.metrics.MetricsProvider;
+import org.apache.polygene.api.serialization.Serialization;
import org.apache.polygene.api.structure.Module;
-import org.apache.polygene.api.structure.TypeLookup;
-import org.apache.polygene.api.value.ValueSerialization;
import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.type.ValueTypeFactory;
public interface ModuleSpi extends Module
{
@@ -32,9 +32,9 @@ public interface ModuleSpi extends Module
IdentityGenerator identityGenerator();
- ValueSerialization valueSerialization();
-
- TypeLookup typeLookup();
+ Serialization serialization();
MetricsProvider metricsProvider();
+
+ ValueTypeFactory valueTypeFactory();
}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
new file mode 100644
index 0000000..a8f2c2c
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UncheckedIOException;
+import java.util.Base64;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+
+import static java.util.stream.Collectors.joining;
+
+public abstract class AbstractBinaryDeserializer extends AbstractDeserializer
+{
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+ {
+ String stateString;
+ try( BufferedReader buffer = new BufferedReader( state ) )
+ {
+ stateString = buffer.lines().collect( joining( "\n" ) );
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ byte[] decoded = Base64.getDecoder().decode( stateString );
+ return deserialize( module, valueType, new ByteArrayInputStream( decoded ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
new file mode 100644
index 0000000..e673ad7
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.Base64;
+import org.apache.polygene.api.common.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Base Binary Serializer.
+ *
+ * Implementations work on bytes, this base serializer encode these bytes in Base64 to produce Strings.
+ */
+public abstract class AbstractBinarySerializer extends AbstractSerializer
+{
+ @Override
+ public void serialize( Options options, Writer writer, @Optional Object object )
+ {
+ try
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ serialize( options, output, object );
+ byte[] base64 = Base64.getEncoder().encode( output.toByteArray() );
+ writer.write( new String( base64, UTF_8 ) );
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
new file mode 100644
index 0000000..17982f3
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.CollectionType;
+import org.apache.polygene.api.type.MapType;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.spi.module.ModuleSpi;
+
+public abstract class AbstractDeserializer implements Deserializer
+{
+ protected static final ValueType ENTITY_REF_LIST_VALUE_TYPE = CollectionType.listOf( EntityReference.class );
+ protected static final ValueType ENTITY_REF_MAP_VALUE_TYPE = MapType.of( String.class, EntityReference.class );
+
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, ValueType valueType, String state )
+ {
+ return deserialize( module, valueType, new StringReader( state ) );
+ }
+
+ @Override
+ public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, ValueType valueType )
+ {
+ return state -> deserialize( module, valueType, new StringReader( state ) );
+ }
+
+ @Override
+ public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, Iterable<String> states )
+ {
+ return StreamSupport.stream( states.spliterator(), false )
+ .map( state -> deserialize( module, valueType, new StringReader( state ) ) );
+ }
+
+ @Override
+ public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, String... states )
+ {
+ return Stream.of( states ).map( state -> deserialize( module, valueType, new StringReader( state ) ) );
+ }
+
+ @Override
+ public <T> T fromBytes( ModuleDescriptor module, ValueType valueType, byte[] bytes )
+ {
+ return deserialize( module, valueType, new ByteArrayInputStream( bytes ) );
+ }
+
+ @Override
+ public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, ValueType valueType )
+ {
+ return bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) );
+ }
+
+ @Override
+ public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, Iterable<byte[]> states )
+ {
+ return StreamSupport.stream( states.spliterator(), false )
+ .map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) );
+ }
+
+ @Override
+ public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, byte[]... states )
+ {
+ return Stream.of( states ).map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) );
+ }
+
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, Class<T> type, InputStream state )
+ {
+ return deserialize( module, valueTypeOf( module, type ), state );
+ }
+
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, Class<T> type, Reader state )
+ {
+ return deserialize( module, valueTypeOf( module, type ), state );
+ }
+
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, Class<T> type, String state )
+ {
+ return deserialize( module, valueTypeOf( module, type ), state );
+ }
+
+ @Override
+ public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, Class<T> type )
+ {
+ return deserializeFunction( module, valueTypeOf( module, type ) );
+ }
+
+ @Override
+ public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, Iterable<String> states )
+ {
+ return deserializeEach( module, valueTypeOf( module, type ), states );
+ }
+
+ @Override
+ public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, String... states )
+ {
+ return deserializeEach( module, valueTypeOf( module, type ), states );
+ }
+
+ @Override
+ public <T> T fromBytes( ModuleDescriptor module, Class<T> type, byte[] bytes )
+ {
+ return fromBytes( module, valueTypeOf( module, type ), bytes );
+ }
+
+ @Override
+ public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, Class<T> type )
+ {
+ return fromBytesFunction( module, valueTypeOf( module, type ) );
+ }
+
+ @Override
+ public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, Iterable<byte[]> states )
+ {
+ return fromBytesEach( module, valueTypeOf( module, type ), states );
+ }
+
+ @Override
+ public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, byte[]... states )
+ {
+ return fromBytesEach( module, valueTypeOf( module, type ), states );
+ }
+
+ private ValueType valueTypeOf( ModuleDescriptor module, Class<?> type )
+ {
+ // TODO Remove (ModuleSpi) cast
+ return ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
new file mode 100644
index 0000000..b5f10ff
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.Serializer;
+
+public abstract class AbstractSerializer implements Serializer
+{
+ @Override
+ public void serialize( Writer writer, @Optional Object object )
+ {
+ serialize( Options.DEFAULT, writer, object );
+ }
+
+ @Override
+ public void serialize( OutputStream output, @Optional Object object )
+ {
+ serialize( Options.DEFAULT, output, object );
+ }
+
+ @Override
+ public String serialize( Options options, @Optional Object object )
+ {
+ StringWriter writer = new StringWriter();
+ serialize( options, writer, object );
+ return writer.toString();
+ }
+
+ @Override
+ public String serialize( @Optional Object object )
+ {
+ return serialize( Options.DEFAULT, object );
+ }
+
+ @Override
+ public <T> Function<T, String> serializeFunction( Options options )
+ {
+ return object -> serialize( options, object );
+ }
+
+ @Override
+ public <T> Function<T, String> serializeFunction()
+ {
+ return object -> serialize( Options.DEFAULT, object );
+ }
+
+ @Override
+ public Stream<String> serializeEach( Options options, Iterable<Object> objects )
+ {
+ return StreamSupport.stream( objects.spliterator(), false )
+ .map( object -> serialize( options, object ) );
+ }
+
+ @Override
+ public Stream<String> serializeEach( Iterable<Object> objects )
+ {
+ return StreamSupport.stream( objects.spliterator(), false )
+ .map( object -> serialize( Options.DEFAULT, object ) );
+ }
+
+ @Override
+ public Stream<String> serializeEach( Options options, Object... objects )
+ {
+ return Stream.of( objects ).map( object -> serialize( options, object ) );
+ }
+
+ @Override
+ public Stream<String> serializeEach( Object... objects )
+ {
+ return Stream.of( objects ).map( object -> serialize( Options.DEFAULT, object ) );
+ }
+
+ @Override
+ public byte[] toBytes( Options options, @Optional Object object )
+ {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ serialize( options, output, object );
+ return output.toByteArray();
+ }
+
+ @Override
+ public byte[] toBytes( @Optional Object object )
+ {
+ return toBytes( Options.DEFAULT, object );
+ }
+
+ @Override
+ public <T> Function<T, byte[]> toBytesFunction( Options options )
+ {
+ return object -> toBytes( options, object );
+ }
+
+ @Override
+ public <T> Function<T, byte[]> toBytesFunction()
+ {
+ return object -> toBytes( Options.DEFAULT, object );
+ }
+
+ @Override
+ public Stream<byte[]> toBytesEach( Options options, Iterable<Object> objects )
+ {
+ return StreamSupport.stream( objects.spliterator(), false )
+ .map( object -> toBytes( options, object ) );
+ }
+
+ @Override
+ public Stream<byte[]> toBytesEach( Iterable<Object> objects )
+ {
+ return StreamSupport.stream( objects.spliterator(), false )
+ .map( object -> toBytes( Options.DEFAULT, object ) );
+ }
+
+ @Override
+ public Stream<byte[]> toBytesEach( Options options, Object... objects )
+ {
+ return Stream.of( objects ).map( object -> toBytes( options, object ) );
+ }
+
+ @Override
+ public Stream<byte[]> toBytesEach( Object... objects )
+ {
+ return Stream.of( objects ).map( object -> toBytes( Options.DEFAULT, object ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
new file mode 100644
index 0000000..0575489
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public abstract class AbstractTextDeserializer extends AbstractDeserializer
+{
+ @Override
+ public <T> T deserialize( ModuleDescriptor module, ValueType valueType, InputStream state )
+ {
+ return deserialize( module, valueType, new InputStreamReader( state, UTF_8 ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
new file mode 100644
index 0000000..3d8bb16
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.UncheckedIOException;
+import org.apache.polygene.api.common.Optional;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Base Text Serializer.
+ *
+ * Implementations work on Strings, this base serializer encode these strings in UTF-8 to produce bytes.
+ */
+public abstract class AbstractTextSerializer extends AbstractSerializer
+{
+ public void serialize( Options options, OutputStream output, @Optional Object object )
+ {
+ try
+ {
+ StringWriter writer = new StringWriter();
+ serialize( options, writer, object );
+ output.write( writer.toString().getBytes( UTF_8 ) );
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
new file mode 100644
index 0000000..84c8993
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UncheckedIOException;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParsingException;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.spi.module.ModuleSpi;
+
+import static java.util.stream.Collectors.joining;
+
+public interface JsonDeserializer extends Deserializer
+{
+ <T> T fromJson( ModuleDescriptor module, ValueType valueType, JsonValue state );
+
+ default <T> Function<JsonValue, T> fromJsonFunction( ModuleDescriptor module, ValueType valueType )
+ {
+ return state -> fromJson( module, valueType, state );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Stream<JsonValue> states )
+ {
+ return states.map( fromJsonFunction( module, valueType ) );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Iterable<JsonValue> states )
+ {
+ return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, JsonValue... states )
+ {
+ return fromJsonEach( module, valueType, Stream.of( states ) );
+ }
+
+ default <T> T fromJson( ModuleDescriptor module, Class<T> type, JsonValue state )
+ {
+ // TODO Remove (ModuleSpi) cast
+ ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+ return fromJson( module, valueType, state );
+ }
+
+ default <T> Function<JsonValue, T> fromJson( ModuleDescriptor module, Class<T> type )
+ {
+ return state -> fromJson( module, type, state );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Stream<JsonValue> states )
+ {
+ return states.map( fromJson( module, valueType ) );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Iterable<JsonValue> states )
+ {
+ return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+ }
+
+ default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, JsonValue... states )
+ {
+ return fromJsonEach( module, valueType, Stream.of( states ) );
+ }
+
+ @Override
+ default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+ {
+ // JSR-353 Does not allow reading "out of structure" values
+ // See https://www.jcp.org/en/jsr/detail?id=353
+ // And commented JsonReader#readValue() method in the javax.json API
+ // BUT, it will be part of the JsonReader contract in the next version
+ // See https://www.jcp.org/en/jsr/detail?id=374
+ // Implementation by provider is optional though, so we'll always need a default implementation here.
+ // Fortunately, JsonParser has new methods allowing to read structures while parsing so it will be easy to do.
+ // In the meantime, a poor man's implementation reading the json into memory will do.
+ // TODO Revisit values out of structure JSON deserialization when JSR-374 is out
+ String stateString;
+ try( BufferedReader buffer = new BufferedReader( state ) )
+ {
+ stateString = buffer.lines().collect( joining( "\n" ) );
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ // We want plain Strings, BigDecimals, BigIntegers to be deserialized even when unquoted
+ Function<String, T> plainValueFunction = string ->
+ {
+ String poorMans = "{\"value\":" + string + "}";
+ JsonObject poorMansJson = Json.createReader( new StringReader( poorMans ) ).readObject();
+ JsonValue value = poorMansJson.get( "value" );
+ return fromJson( module, valueType, value );
+ };
+ Function<String, T> outOfStructureFunction = string ->
+ {
+ // Is this an unquoted plain value?
+ try
+ {
+ return plainValueFunction.apply( '"' + string + '"' );
+ }
+ catch( JsonParsingException ex )
+ {
+ return plainValueFunction.apply( string );
+ }
+ };
+ try( JsonParser parser = Json.createParser( new StringReader( stateString ) ) )
+ {
+ if( parser.hasNext() )
+ {
+ JsonParser.Event e = parser.next();
+ switch( e )
+ {
+ case VALUE_NULL:
+ return null;
+ case START_ARRAY:
+ case START_OBJECT:
+ // JSON Structure
+ try( JsonReader reader = Json.createReader( new StringReader( stateString ) ) )
+ {
+ return fromJson( module, valueType, reader.read() );
+ }
+ default:
+ // JSON Value out of structure
+ return outOfStructureFunction.apply( stateString );
+ }
+ }
+ }
+ catch( JsonParsingException ex )
+ {
+ return outOfStructureFunction.apply( stateString );
+ }
+ // Empty state string?
+ JsonValue emptyJsonString = Json.createReader( new StringReader( "{\"empty\":\"\"}" ) )
+ .readObject().get( "empty" );
+ return fromJson( module, valueType, emptyJsonString );
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
new file mode 100644
index 0000000..a98e70f
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import org.apache.polygene.api.serialization.Serialization;
+
+public interface JsonSerialization extends Serialization, JsonSerializer, JsonDeserializer
+{
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
new file mode 100644
index 0000000..b64f240
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.Serializer;
+
+public interface JsonSerializer extends Serializer
+{
+ <T> Function<T, JsonValue> toJsonFunction( Options options );
+
+ default <T> Function<T, JsonValue> toJsonFunction()
+ {
+ return object -> toJsonFunction( Options.DEFAULT ).apply( object );
+ }
+
+ default JsonValue toJson( Options options, @Optional Object object )
+ {
+ return toJsonFunction( options ).apply( object );
+ }
+
+ default JsonValue toJson( @Optional Object object )
+ {
+ return toJsonFunction( Options.DEFAULT ).apply( object );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Options options, Stream<T> objects )
+ {
+ return objects.map( toJsonFunction( options ) );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Options options, Iterable<T> objects )
+ {
+ return toJsonEach( options, StreamSupport.stream( objects.spliterator(), false ) );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Options options, Object... objects )
+ {
+ return toJsonEach( options, Stream.of( objects ) );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Stream<T> objects )
+ {
+ return objects.map( toJsonFunction( Options.DEFAULT ) );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Iterable<T> objects )
+ {
+ return toJsonEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) );
+ }
+
+ default <T> Stream<JsonValue> toJsonEach( Object... objects )
+ {
+ return toJsonEach( Options.DEFAULT, Stream.of( objects ) );
+ }
+
+ default void serialize( Options options, Writer writer, @Optional Object object )
+ {
+ JsonValue jsonValue = toJson( options, object );
+ if( jsonValue == null )
+ {
+ return;
+ }
+ try
+ {
+ // TODO FIX ThIS SHIT of "out of structure" value (de)serialization
+ // We want plain Strings to be serialized without quotes which is non JSON compliant
+ // See https://java.net/jira/browse/JSON_PROCESSING_SPEC-65
+ if( jsonValue.getValueType() == JsonValue.ValueType.STRING )
+ {
+ writer.write( ( (JsonString) jsonValue ).getString() );
+ }
+ else
+ {
+ writer.write( jsonValue.toString() );
+ }
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
new file mode 100644
index 0000000..c7ac42b
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.polygene.api.serialization.Deserializer;
+import org.apache.polygene.api.serialization.SerializationException;
+import org.apache.polygene.api.structure.ModuleDescriptor;
+import org.apache.polygene.api.type.CollectionType;
+import org.apache.polygene.api.type.EnumType;
+import org.apache.polygene.api.type.MapType;
+import org.apache.polygene.api.type.ValueCompositeType;
+import org.apache.polygene.api.type.ValueType;
+import org.apache.polygene.api.value.ValueDescriptor;
+import org.apache.polygene.spi.module.ModuleSpi;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public interface XmlDeserializer extends Deserializer
+{
+ <T> T fromXml( ModuleDescriptor module, ValueType valueType, Document state );
+
+ default <T> Function<Document, T> fromXmlFunction( ModuleDescriptor module, ValueType valueType )
+ {
+ return state -> fromXml( module, valueType, state );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Stream<Document> states )
+ {
+ return states.map( fromXmlFunction( module, valueType ) );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Iterable<Document> states )
+ {
+ return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Document... states )
+ {
+ return fromXmlEach( module, valueType, Stream.of( states ) );
+ }
+
+ default <T> T fromXml( ModuleDescriptor module, Class<T> type, Document state )
+ {
+ // TODO Remove (ModuleSpi) cast
+ ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type );
+ return fromXml( module, valueType, state );
+ }
+
+ default <T> Function<Document, T> fromXml( ModuleDescriptor module, Class<T> type )
+ {
+ return state -> fromXml( module, type, state );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Stream<Document> states )
+ {
+ return states.map( fromXml( module, valueType ) );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Iterable<Document> states )
+ {
+ return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) );
+ }
+
+ default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Document... states )
+ {
+ return fromXmlEach( module, valueType, Stream.of( states ) );
+ }
+
+ @Override
+ default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
+ {
+ try
+ {
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ Document doc = docBuilder.parse( new InputSource( state ) );
+ return fromXml( module, valueType, doc );
+ }
+ catch( SAXException | IOException | ParserConfigurationException ex )
+ {
+ throw new SerializationException( "Unable to read XML document", ex );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
new file mode 100644
index 0000000..12fda54
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import org.apache.polygene.api.serialization.Serialization;
+
+public interface XmlSerialization extends Serialization, XmlSerializer, XmlDeserializer
+{
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
new file mode 100644
index 0000000..32ce539
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+package org.apache.polygene.spi.serialization;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.polygene.api.common.Optional;
+import org.apache.polygene.api.serialization.SerializationException;
+import org.apache.polygene.api.serialization.Serializer;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * XML State Serializer.
+ */
+public interface XmlSerializer extends Serializer
+{
+ <T> Function<T, Document> toXmlFunction( Options options );
+
+ default <T> Function<T, Document> toXmlFunction()
+ {
+ return object -> toXmlFunction( Options.DEFAULT ).apply( object );
+ }
+
+ default Document toXml( Options options, @Optional Object object )
+ {
+ return toXmlFunction( options ).apply( object );
+ }
+
+ default Document toXml( @Optional Object object )
+ {
+ return toXmlFunction( Options.DEFAULT ).apply( object );
+ }
+
+ default <T> Stream<Document> toXmlEach( Options options, Stream<T> objects )
+ {
+ return objects.map( toXmlFunction( options ) );
+ }
+
+ default <T> Stream<Document> toXmlEach( Options options, Iterable<T> objects )
+ {
+ return toXmlEach( options, StreamSupport.stream( objects.spliterator(), false ) );
+ }
+
+ default <T> Stream<Document> toXmlEach( Options options, Object... objects )
+ {
+ return toXmlEach( options, Stream.of( objects ) );
+ }
+
+ default <T> Stream<Document> toXmlEach( Stream<T> objects )
+ {
+ return objects.map( toXmlFunction( Options.DEFAULT ) );
+ }
+
+ default <T> Stream<Document> toXmlEach( Iterable<T> objects )
+ {
+ return toXmlEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) );
+ }
+
+ default <T> Stream<Document> toXmlEach( Object... objects )
+ {
+ return toXmlEach( Options.DEFAULT, Stream.of( objects ) );
+ }
+
+ default void serialize( Options options, Writer writer, @Optional Object object )
+ {
+ Document xmlDocument = toXml( options, object );
+ if( xmlDocument == null )
+ {
+ return;
+ }
+ try
+ {
+ // We want plain Strings to be serialized without quotes
+ if( xmlDocument.getNodeType() == Node.TEXT_NODE )
+ {
+ writer.write( xmlDocument.getNodeValue() );
+ }
+ else
+ {
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
+ transformer.setOutputProperty( OutputKeys.VERSION, "1.1" );
+ transformer.setOutputProperty( OutputKeys.STANDALONE, "yes" );
+ transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
+ transformer.transform( new DOMSource( xmlDocument ), new StreamResult( writer ) );
+ }
+ }
+ catch( IOException ex )
+ {
+ throw new UncheckedIOException( ex );
+ }
+ catch( TransformerException ex )
+ {
+ throw new SerializationException( "Unable to transform XML Document to String", ex );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
----------------------------------------------------------------------
diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
new file mode 100644
index 0000000..2e2f188
--- /dev/null
+++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ ~
+ ~
+ -->
+<html>
+ <body>
+ <h2>Serialization SPI.</h2>
+ </body>
+</html>