http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationAssembler.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationAssembler.java b/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationAssembler.java new file mode 100644 index 0000000..36b84d6 --- /dev/null +++ b/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/JavaxJsonSerializationAssembler.java @@ -0,0 +1,58 @@ +/* + * 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.serialization.javaxjson; + +import org.apache.polygene.api.serialization.Deserializer; +import org.apache.polygene.api.serialization.Serialization; +import org.apache.polygene.api.serialization.Serializer; +import org.apache.polygene.bootstrap.Assemblers; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.bootstrap.ServiceDeclaration; +import org.apache.polygene.spi.serialization.JsonDeserializer; +import org.apache.polygene.spi.serialization.JsonSerialization; +import org.apache.polygene.spi.serialization.JsonSerializer; + +public class JavaxJsonSerializationAssembler extends Assemblers.VisibilityIdentity<JavaxJsonSerializationAssembler> +{ + private JavaxJsonSettings settings; + + public JavaxJsonSerializationAssembler withJsonSettings( JavaxJsonSettings settings ) + { + this.settings = settings; + return this; + } + + @Override + public void assemble( ModuleAssembly module ) + { + ServiceDeclaration declaration = module.services( JavaxJsonSerializationService.class ) + .withTypes( Serialization.class, + Serializer.class, Deserializer.class, + JsonSerialization.class, + JsonSerializer.class, JsonDeserializer.class ) + .visibleIn( visibility() ); + if( hasIdentity() ) + { + declaration.identifiedBy( identity() ); + } + if( settings != null ) + { + declaration.setMetaInfo( settings ); + } + } +}
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/package.html ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/package.html b/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/package.html new file mode 100644 index 0000000..43db1d9 --- /dev/null +++ b/extensions/serialization-javaxjson/src/main/java/org/apache/polygene/serialization/javaxjson/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>javax.json Serialization.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/CustomJsonAdapterTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/CustomJsonAdapterTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/CustomJsonAdapterTest.java new file mode 100644 index 0000000..bed0492 --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/CustomJsonAdapterTest.java @@ -0,0 +1,180 @@ +/* + * 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.serialization.javaxjson; + +import java.time.LocalDate; +import java.util.function.BiFunction; +import java.util.function.Function; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.json.JsonValue; +import org.apache.polygene.api.injection.scope.Service; +import org.apache.polygene.api.property.Property; +import org.apache.polygene.api.serialization.SerializationException; +import org.apache.polygene.api.type.ValueCompositeType; +import org.apache.polygene.api.type.ValueType; +import org.apache.polygene.api.value.ValueBuilder; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.test.AbstractPolygeneTest; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class CustomJsonAdapterTest extends AbstractPolygeneTest +{ + @Override + public void assemble( ModuleAssembly module ) + { + new JavaxJsonSerializationAssembler() + .withJsonSettings( new JavaxJsonSettings().withJsonAdapter( new CustomValueAdapter() ) + .withJsonAdapter( new CustomStructureAdapter() ) ) + .assemble( module ); + module.values( SomeValue.class ); + } + + interface SomeValue + { + Property<CustomValue> customValue(); + + Property<CustomStructure> customStructure(); + } + + static class CustomValue + { + String state; + + CustomValue( String state ) + { + this.state = state; + } + } + + static class CustomStructure + { + String foo; + LocalDate bar; + + CustomStructure( String foo, LocalDate bar ) + { + this.foo = foo; + this.bar = bar; + } + } + + static class CustomValueAdapter implements JavaxJsonAdapter<CustomValue> + { + @Override + public Class<CustomValue> type() { return CustomValue.class; } + + @Override + public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction ) + { + return JavaxJson.toJsonString( type().cast( object ).state ); + } + + @Override + public CustomValue deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction ) + { + switch( json.getValueType() ) + { + case STRING: + return new CustomValue( ( (JsonString) json ).getString() ); + default: + throw new SerializationException( "Don't know how to deserialize CustomValue from " + json ); + } + } + } + + static class CustomStructureAdapter implements JavaxJsonAdapter<CustomStructure> + { + @Override + public Class<CustomStructure> type() { return CustomStructure.class; } + + @Override + public JsonValue serialize( Object object, Function<Object, JsonValue> serializeFunction ) + { + CustomStructure customStructure = type().cast( object ); + return Json.createObjectBuilder() + .add( "foo", customStructure.foo ) + .add( "bar", serializeFunction.apply( customStructure.bar ) ) + .build(); + } + + @Override + public CustomStructure deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserializeFunction ) + { + if( json.getValueType() != JsonValue.ValueType.OBJECT ) + { + throw new SerializationException( "Don't know how to deserialize CustomStructure from " + json ); + } + JsonObject jsonObject = (JsonObject) json; + String foo = jsonObject.getString( "foo" ); + LocalDate bar = (LocalDate) deserializeFunction.apply( jsonObject.get( "bar" ), ValueType.of( LocalDate.class ) ); + return new CustomStructure( foo, bar ); + } + } + + @Service + private JavaxJsonSerialization serialization; + + @Test + public void customJsonAdapterForPropertyValue() + { + ValueBuilder<SomeValue> builder = valueBuilderFactory.newValueBuilder( SomeValue.class ); + builder.prototype().customValue().set( new CustomValue( "custom-value-state" ) ); + builder.prototype().customStructure().set( new CustomStructure( "foo", LocalDate.of( 2017, 1, 1 ) ) ); + SomeValue someValue = builder.newInstance(); + + System.out.println( someValue.toString() ); + + JsonValue serialized = serialization.toJson( someValue ); + assertThat( serialized.getValueType(), is( JsonValue.ValueType.OBJECT ) ); + + JsonObject jsonObject = (JsonObject) serialized; + assertThat( jsonObject.getString( "customValue" ), equalTo( "custom-value-state" ) ); + JsonObject structure = jsonObject.getJsonObject( "customStructure" ); + assertThat( structure.getString( "foo" ), equalTo( "foo" ) ); + assertThat( structure.getString( "bar" ), equalTo( "2017-01-01" ) ); + + SomeValue deserialized = serialization.fromJson( module, ValueCompositeType.of( SomeValue.class ), serialized ); + + assertThat( deserialized.customValue().get().state, equalTo( "custom-value-state" ) ); + assertThat( deserialized.customStructure().get().foo, equalTo( "foo" ) ); + assertThat( deserialized.customStructure().get().bar, equalTo( LocalDate.of( 2017, 1, 1 ) ) ); + } + + @Test + public void customJsonAdapterForDirectObject() + { + CustomValue customValueObject = new CustomValue( "custom-value-state" ); + JsonValue serialized = serialization.toJson( customValueObject ); + assertThat( serialized.getValueType(), is( JsonValue.ValueType.STRING ) ); + JsonString jsonString = (JsonString) serialized; + assertThat( jsonString.getString(), equalTo( "custom-value-state" ) ); + + CustomStructure customStructureObject = new CustomStructure( "foo", LocalDate.of( 2017, 1, 1 ) ); + serialized = serialization.toJson( customStructureObject ); + assertThat( serialized.getValueType(), is( JsonValue.ValueType.OBJECT ) ); + JsonObject jsonObject = (JsonObject) serialized; + assertThat( jsonObject.getString( "foo" ), equalTo( "foo" ) ); + assertThat( jsonObject.getString( "bar" ), equalTo( "2017-01-01" ) ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonCollectionSerializationTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonCollectionSerializationTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonCollectionSerializationTest.java new file mode 100644 index 0000000..517b630 --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonCollectionSerializationTest.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.serialization.javaxjson; + +import org.apache.polygene.test.serialization.AbstractCollectionSerializationTest; + +public class JavaxJsonCollectionSerializationTest extends AbstractCollectionSerializationTest +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonConfigurationDeserializationTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonConfigurationDeserializationTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonConfigurationDeserializationTest.java new file mode 100644 index 0000000..f054561 --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonConfigurationDeserializationTest.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.serialization.javaxjson; + +import org.apache.polygene.test.entity.AbstractConfigurationDeserializationTest; + +public class JavaxJsonConfigurationDeserializationTest extends AbstractConfigurationDeserializationTest +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDateFormatSerializationTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDateFormatSerializationTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDateFormatSerializationTest.java new file mode 100644 index 0000000..7c0f510 --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonDateFormatSerializationTest.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.serialization.javaxjson; + +import org.apache.polygene.test.serialization.AbstractDateFormatSerializationTest; + +public class JavaxJsonDateFormatSerializationTest extends AbstractDateFormatSerializationTest +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonPlainValueSerializationTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonPlainValueSerializationTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonPlainValueSerializationTest.java new file mode 100644 index 0000000..00391e7 --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonPlainValueSerializationTest.java @@ -0,0 +1,26 @@ +/* + * 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.serialization.javaxjson; + +import org.apache.polygene.test.serialization.AbstractPlainValueSerializationTest; + +public class JavaxJsonPlainValueSerializationTest extends AbstractPlainValueSerializationTest +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonValueCompositeSerializationTest.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonValueCompositeSerializationTest.java b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonValueCompositeSerializationTest.java new file mode 100644 index 0000000..5fe4f5f --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/java/org/apache/polygene/serialization/javaxjson/JavaxJsonValueCompositeSerializationTest.java @@ -0,0 +1,60 @@ +/* + * 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.serialization.javaxjson; + +import java.io.StringReader; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonValue; +import org.apache.polygene.api.injection.scope.Service; +import org.apache.polygene.api.unitofwork.UnitOfWork; +import org.apache.polygene.spi.serialization.JsonSerialization; +import org.apache.polygene.test.serialization.AbstractValueCompositeSerializationTest; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class JavaxJsonValueCompositeSerializationTest extends AbstractValueCompositeSerializationTest +{ + @Service + private JsonSerialization jsonSerialization; + + @Test + public void valueCompositeJsonEquality() + { + try( UnitOfWork uow = unitOfWorkFactory.newUnitOfWork() ) + { + Some some = buildSomeValue( moduleInstance, uow, "42" ); + + // Serialize using injected service + JsonValue jsonState = jsonSerialization.toJson( some ); + String stateString = jsonState.toString(); + System.out.println( jsonState.toString() ); + + // Deserialize using Module API + Some some2 = moduleInstance.newValueFromSerializedState( Some.class, stateString ); + + assertThat( "Value equality", some, equalTo( some2 ) ); + + JsonObject jsonState2 = Json.createReader( new StringReader( some2.toString() ) ).readObject(); + + assertThat( "JSON equality", jsonState, equalTo( jsonState2 ) ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxjson/src/test/resources/configtest.json ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxjson/src/test/resources/configtest.json b/extensions/serialization-javaxjson/src/test/resources/configtest.json new file mode 100644 index 0000000..4c5600b --- /dev/null +++ b/extensions/serialization-javaxjson/src/test/resources/configtest.json @@ -0,0 +1,8 @@ +{ + "identity": "configtest", + "host": { + "ip": "12.23.34.45", + "port": 1234 + }, + "name": "main" +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/build.gradle ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/build.gradle b/extensions/serialization-javaxxml/build.gradle new file mode 100644 index 0000000..4c095f9 --- /dev/null +++ b/extensions/serialization-javaxxml/build.gradle @@ -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. + */ + +apply plugin: 'polygene-extension' + +description = "Apache Polygene⢠javax.xml Serialization Extension" + +jar { manifest { name = "Apache Polygene⢠Extension - Serialization - javax.xml" } } + +dependencies { + api polygene.core.bootstrap + + runtimeOnly polygene.core.runtime + + testImplementation polygene.core.testsupport + testImplementation libraries.xmlunit + + testRuntimeOnly libraries.logback +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/dev-status.xml ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/dev-status.xml b/extensions/serialization-javaxxml/dev-status.xml new file mode 100644 index 0000000..81841be --- /dev/null +++ b/extensions/serialization-javaxxml/dev-status.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + ~ 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. + ~ + ~ + --> +<module xmlns="http://polygene.apache.org/schemas/2008/dev-status/1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://polygene.apache.org/schemas/2008/dev-status/1 + http://polygene.apache.org/schemas/2008/dev-status/1/dev-status.xsd"> + <status> + <!--none,early,beta,stable,mature--> + <codebase>early</codebase> + + <!-- none, brief, good, complete --> + <documentation>none</documentation> + + <!-- none, some, good, complete --> + <unittests>good</unittests> + </status> + <licenses> + <license>ALv2</license> + </licenses> +</module> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/docs/serialization-javaxxml.txt ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/docs/serialization-javaxxml.txt b/extensions/serialization-javaxxml/src/docs/serialization-javaxxml.txt new file mode 100644 index 0000000..6fe6d75 --- /dev/null +++ b/extensions/serialization-javaxxml/src/docs/serialization-javaxxml.txt @@ -0,0 +1,30 @@ +/////////////////////////////////////////////////////////////// + * 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. +/////////////////////////////////////////////////////////////// + +[[extension-serialization-javaxxml,javax.xml serialization]] += javax.xml serialization = + +[devstatus] +-------------- +source=extensions/serialization-javaxxml/dev-status.xml +-------------- + +// TODO Document usage of XmlSerialization +// TODO Include sample model and its output from test code & resources +// TODO Assembly - Serialization extension or sole Service, settings & adapters http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXml.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXml.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXml.java new file mode 100644 index 0000000..3289d30 --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXml.java @@ -0,0 +1,161 @@ +/* + * 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.serialization.javaxxml; + +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * javax.xml utilities. + */ +public class JavaxXml +{ + /** + * Find child elements. + * + * @param parent the parent node + * @return a stream of elements + */ + public static Stream<Element> childElements( Node parent ) + { + return toStream( parent.getChildNodes() ).filter( JavaxXml::isElement ) + .map( JavaxXml::castToElement ); + } + + /** + * Find the first child element. + * + * @param parent the parent node + * @return an optional element + */ + public static Optional<Element> firstChildElement( Node parent ) + { + return childElements( parent ).findFirst(); + } + + /** + * Find child elements named {@literal tagName}. + * + * @param parent the parent node + * @param tagName the tag name + * @return a stream of elements named {@literal tagName} + */ + public static Stream<Element> childElementsNamed( Node parent, String tagName ) + { + return childElements( parent ).filter( element -> tagName.equals( element.getTagName() ) ); + } + + /** + * Find the first child element named {@literal tagName}. + * + * @param parent the parent node + * @param tagName the tag name + * @return an optional element named {@literal tagName} + */ + public static Optional<Element> firstChildElementNamed( Node parent, String tagName ) + { + return childElementsNamed( parent, tagName ).findFirst(); + } + + /** + * Find child nodes holding state. + * + * @param parent the parent node + * @return a stream or child state nodes + */ + public static Stream<Node> stateChildNodes( Node parent ) + { + return toStream( parent.getChildNodes() ).filter( JavaxXml::isStateNode ); + } + + /** + * Find the first child node holding state. + * + * @param parent the parent node + * @return an optional child state node + */ + public static Optional<Node> firstStateChildNode( Node parent ) + { + return stateChildNodes( parent ).findFirst(); + } + + /** + * Test if a node holds state. + * + * Types of nodes holding state: + * <ul> + * <li>{@link Node#ELEMENT_NODE}</li> + * <li>{@link Node#CDATA_SECTION_NODE}</li> + * <li>{@link Node#TEXT_NODE}</li> + * </ul> + * + * @param node the node + * @return {@literal true} if {@literal node} holds state + */ + public static boolean isStateNode( Node node ) + { + switch( node.getNodeType() ) + { + case Node.ELEMENT_NODE: + case Node.CDATA_SECTION_NODE: + case Node.TEXT_NODE: + return true; + default: + return false; + } + } + + private static boolean isElement( Node node ) + { + return node.getNodeType() == Node.ELEMENT_NODE; + } + + private static Element castToElement( Node node ) + { + return (Element) node; + } + + private static Stream<Node> toStream( NodeList nodeList ) + { + return StreamSupport.stream( new Spliterators.AbstractSpliterator<Node>( Long.MAX_VALUE, Spliterator.ORDERED ) + { + private int nextIndex = 0; + + @Override + public boolean tryAdvance( Consumer<? super Node> action ) + { + if( nextIndex >= nodeList.getLength() ) + { + return false; + } + action.accept( nodeList.item( nextIndex ) ); + nextIndex++; + return true; + } + }, false ); + } + + private JavaxXml() {} +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapter.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapter.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapter.java new file mode 100644 index 0000000..3761198 --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapter.java @@ -0,0 +1,56 @@ +/* + * 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.serialization.javaxxml; + +import java.util.function.BiFunction; +import java.util.function.Function; +import org.apache.polygene.api.type.ValueType; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Adapter for XML (de)serialization. + * + * @param <T> the adapted type + */ +public interface JavaxXmlAdapter<T> +{ + /** + * @return the adapted type + */ + Class<T> type(); + + /** + * Serialize. + * + * @param document the Document to use as a Node factory + * @param object Object to serialize, never null + * @param serializationFunction Serialization function for nested structure serialization + * @return Serialized XML representation + */ + Node serialize( Document document, Object object, Function<Object, Node> serializationFunction ); + + /** + * Deserialize. + * + * @param node XML to deserialize from, never null + * @param deserializationFunction Deserialization function for nested structure deserialization + * @return Deserialized object + */ + T deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ); +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapters.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapters.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapters.java new file mode 100644 index 0000000..273789d --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlAdapters.java @@ -0,0 +1,64 @@ +/* + * 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.serialization.javaxxml; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.type.ValueType; + +import static org.apache.polygene.api.type.HasTypesCollectors.closestType; + +@Mixins( JavaxXmlAdapters.Mixin.class ) +public interface JavaxXmlAdapters +{ + void registerAdapter( ValueType valueType, JavaxXmlAdapter<?> adapter ); + + <T> JavaxXmlAdapter<T> adapterFor( ValueType valueType ); + + default <T> JavaxXmlAdapter<T> adapterFor( Class<T> type ) + { + return adapterFor( ValueType.of( type ) ); + } + + class Mixin implements JavaxXmlAdapters + { + private Map<ValueType, JavaxXmlAdapter<?>> adapters = new LinkedHashMap<>(); + + @Override + public void registerAdapter( final ValueType valueType, final JavaxXmlAdapter<?> adapter ) + { + adapters.put( valueType, adapter ); + } + + @Override + public <T> JavaxXmlAdapter<T> adapterFor( final ValueType valueType ) + { + return castAdapter( adapters.keySet().stream() + .collect( closestType( valueType ) ) + .map( adapters::get ) + .orElse( null ) ); + } + + @SuppressWarnings( "unchecked" ) + private <T> JavaxXmlAdapter<T> castAdapter( JavaxXmlAdapter<?> adapter ) + { + return (JavaxXmlAdapter<T>) adapter; + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlDeserializer.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlDeserializer.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlDeserializer.java new file mode 100644 index 0000000..992e000 --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlDeserializer.java @@ -0,0 +1,325 @@ +/* + * 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.serialization.javaxxml; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.polygene.api.association.AssociationDescriptor; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.injection.scope.This; +import org.apache.polygene.api.injection.scope.Uses; +import org.apache.polygene.api.property.PropertyDescriptor; +import org.apache.polygene.api.serialization.SerializationException; +import org.apache.polygene.api.service.ServiceDescriptor; +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.ValueBuilder; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.spi.serialization.AbstractTextDeserializer; +import org.apache.polygene.spi.serialization.XmlDeserializer; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableMap; +import static java.util.Collections.unmodifiableSet; +import static org.apache.polygene.api.util.Collectors.toMapWithNullValues; + +public class JavaxXmlDeserializer extends AbstractTextDeserializer implements XmlDeserializer +{ + private static final String NULL_ELEMENT_NAME = "null"; + + @This + private JavaxXmlAdapters adapters; + + @Uses + private ServiceDescriptor descriptor; + + @Override + public <T> T fromXml( ModuleDescriptor module, ValueType valueType, Document state ) + { + Optional<Element> stateElement = JavaxXml.firstChildElementNamed( state, getSettings().getRootTagName() ); + if( stateElement.isPresent() ) + { + Optional<Node> stateNode = JavaxXml.firstStateChildNode( stateElement.get() ); + if( stateNode.isPresent() ) + { + return doDeserialize( module, valueType, stateNode.get() ); + } + } + return null; + } + + @SuppressWarnings( "unchecked" ) + private <T> T doDeserialize( ModuleDescriptor module, ValueType valueType, Node xml ) + { + if( xml == null ) + { + return valueType.hasType( String.class ) ? (T) "" : null; + } + if( xml.getNodeType() == Node.ELEMENT_NODE && NULL_ELEMENT_NAME.equals( ( (Element) xml ).getTagName() ) ) + { + return null; + } + JavaxXmlAdapter<?> adapter = adapters.adapterFor( valueType ); + if( adapter != null ) + { + return (T) adapter.deserialize( xml, ( element, type ) -> doDeserialize( module, type, element ) ); + } + if( EnumType.class.isAssignableFrom( valueType.getClass() ) ) + { + return (T) Enum.valueOf( (Class) valueType.primaryType(), xml.getNodeValue() ); + } + if( CollectionType.class.isAssignableFrom( valueType.getClass() ) ) + { + return (T) deserializeCollection( module, (CollectionType) valueType, xml ); + } + if( MapType.class.isAssignableFrom( valueType.getClass() ) ) + { + return (T) deserializeMap( module, (MapType) valueType, xml ); + } + if( ValueCompositeType.class.isAssignableFrom( valueType.getClass() ) ) + { + return (T) deserializeValueComposite( module, (ValueCompositeType) valueType, xml ); + } + return (T) doGuessDeserialize( module, valueType, xml ); + } + + private Object deserializeValueComposite( ModuleDescriptor module, ValueCompositeType valueType, Node xml ) + { + Optional<String> typeInfo = getTypeInfo( xml ); + if( typeInfo.isPresent() ) + { + ValueDescriptor descriptor = module.valueDescriptor( typeInfo.get() ); + if( descriptor == null ) + { + String typeInfoName = getSettings().getTypeInfoTagName(); + throw new SerializationException( + typeInfoName + ": " + typeInfo.get() + " could not be resolved while deserializing " + xml ); + } + valueType = descriptor.valueType(); + } + ValueBuilder builder = module.instance().newValueBuilderWithState( + valueType.primaryType(), + propertyFunction( module, xml ), + associationFunction( module, xml ), + manyAssociationFunction( module, xml ), + namedAssociationFunction( module, xml ) ); + return builder.newInstance(); + } + + private Function<PropertyDescriptor, Object> propertyFunction( ModuleDescriptor module, Node xml ) + { + return property -> + { + Optional<Element> element = JavaxXml.firstChildElementNamed( xml, property.qualifiedName().name() ); + if( element.isPresent() ) + { + Node valueNode = JavaxXml.firstStateChildNode( element.get() ).orElse( null ); + Object value = doDeserialize( module, property.valueType(), valueNode ); + if( property.isImmutable() ) + { + if( value instanceof Set ) + { + return unmodifiableSet( (Set<?>) value ); + } + else if( value instanceof List ) + { + return unmodifiableList( (List<?>) value ); + } + else if( value instanceof Map ) + { + return unmodifiableMap( (Map<?, ?>) value ); + } + } + return value; + } + return property.resolveInitialValue( module ); + }; + } + + private Function<AssociationDescriptor, EntityReference> associationFunction( ModuleDescriptor module, Node xml ) + { + return association -> + (EntityReference) JavaxXml.firstChildElementNamed( xml, association.qualifiedName().name() ) + .map( element -> doDeserialize( module, + ValueType.ENTITY_REFERENCE, + JavaxXml.firstStateChildNode( element ) + .orElse( null ) ) ) + .orElse( null ); + } + + private Function<AssociationDescriptor, Stream<EntityReference>> manyAssociationFunction( ModuleDescriptor module, + Node xml ) + { + return association -> + JavaxXml.firstChildElementNamed( xml, association.qualifiedName().name() ) + .map( element -> (List) doDeserialize( module, + ENTITY_REF_LIST_VALUE_TYPE, + JavaxXml.firstStateChildNode( element ) + .orElse( null ) ) ) + .map( List::stream ) + .orElse( Stream.empty() ); + } + + private Function<AssociationDescriptor, Stream<Map.Entry<String, EntityReference>>> namedAssociationFunction( + ModuleDescriptor module, Node xml ) + { + return association -> + (Stream) JavaxXml.firstChildElementNamed( xml, association.qualifiedName().name() ) + .map( element -> (Map) doDeserialize( module, + ENTITY_REF_MAP_VALUE_TYPE, + JavaxXml.firstStateChildNode( element ) + .orElse( null ) ) ) + .map( Map::entrySet ).map( Set::stream ) + .orElse( Stream.empty() ); + } + + @SuppressWarnings( "unchecked" ) + private Collection deserializeCollection( ModuleDescriptor module, CollectionType collectionType, Node xml ) + { + Supplier<Collection> collectionSupplier = () -> collectionType.isSet() + ? new LinkedHashSet<>() + : new ArrayList<>(); + if( !xml.hasChildNodes() ) + { + return collectionSupplier.get(); + } + return JavaxXml + .childElements( xml ) + .map( element -> + { + if( getSettings().getCollectionElementTagName().equals( element.getTagName() ) ) + { + return doDeserialize( module, collectionType.collectedType(), + JavaxXml.firstStateChildNode( element ).get() ); + } + return doDeserialize( module, collectionType.collectedType(), element ); + } ) + .collect( Collectors.toCollection( collectionSupplier ) ); + } + + @SuppressWarnings( "unchecked" ) + private Map deserializeMap( ModuleDescriptor module, MapType mapType, Node xml ) + { + if( !xml.hasChildNodes() ) + { + return new LinkedHashMap<>(); + } + Predicate<Element> complexMapping = element -> getSettings().getMapEntryTagName().equals( element.getTagName() ) + && JavaxXml.firstChildElementNamed( element, "key" ) + .isPresent(); + // This allows deserializing mixed simple/complex mappings for a given map + return JavaxXml.childElements( xml ).map( + element -> + { + if( complexMapping.test( element ) ) + { + Node keyNode = JavaxXml.firstChildElementNamed( element, "key" ) + .flatMap( JavaxXml::firstStateChildNode ) + .get(); + Optional<Node> valueNode = JavaxXml.firstChildElementNamed( element, "value" ) + .flatMap( JavaxXml::firstStateChildNode ); + Object key = doDeserialize( module, mapType.keyType(), keyNode ); + Object value = valueNode.map( node -> doDeserialize( module, mapType.valueType(), node ) ) + .orElse( null ); + return new HashMap.SimpleImmutableEntry<>( key, value ); + } + String key = element.getTagName(); + Object value = JavaxXml.firstStateChildNode( element ) + .map( node -> doDeserialize( module, mapType.valueType(), node ) ) + .orElse( null ); + return (Map.Entry) new HashMap.SimpleImmutableEntry<>( key, value ); + } + ).collect( toMapWithNullValues( LinkedHashMap::new ) ); + } + + private Object doGuessDeserialize( ModuleDescriptor module, ValueType valueType, Node xml ) + { + // TODO Could do better by detecting <collection/>, <map/> and <value/> + Optional<String> typeInfo = getTypeInfo( xml ); + if( typeInfo.isPresent() ) + { + ValueDescriptor valueDescriptor = module.valueDescriptor( typeInfo.get() ); + if( valueDescriptor != null ) + { + return deserializeValueComposite( module, valueDescriptor.valueType(), xml ); + } + } + if( xml.getNodeType() == Node.CDATA_SECTION_NODE ) + { + return deserializeBase64( xml.getNodeValue() ); + } + throw new SerializationException( "Don't know how to deserialize " + valueType + " from " + xml ); + } + + private Object deserializeBase64( String inputString ) + { + byte[] bytes = inputString.getBytes( UTF_8 ); + bytes = Base64.getDecoder().decode( bytes ); + try( ObjectInputStream oin = new ObjectInputStream( new ByteArrayInputStream( bytes ) ) ) + { + return oin.readObject(); + } + catch( IOException | ClassNotFoundException ex ) + { + throw new SerializationException( "Unable to deserialize Base64 serialized " + inputString, ex ); + } + } + + private Optional<String> getTypeInfo( Node xml ) + { + if( xml.getNodeType() != Node.ELEMENT_NODE ) + { + return Optional.empty(); + } + String typeInfo = ( (Element) xml ).getAttribute( getSettings().getTypeInfoTagName() ); + if( typeInfo.isEmpty() ) + { + return Optional.empty(); + } + return Optional.of( typeInfo ); + } + + private JavaxXmlSettings getSettings() + { + return JavaxXmlSettings.orDefault( descriptor.metaInfo( JavaxXmlSettings.class ) ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerialization.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerialization.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerialization.java new file mode 100644 index 0000000..529bdee --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerialization.java @@ -0,0 +1,88 @@ +/* + * 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.serialization.javaxxml; + +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.spi.serialization.XmlSerialization; + +/** + * javax.xml state serialization. + * + * The XML representations consumed and produced by this service are, by default, verbose, and safe to deserialize. + * This is because the default mapping is purely structural. + * You can customize the XML representations using {@link JavaxXmlSettings} and {@link JavaxXmlAdapters}. + * + * The following describe how state is represented by default. + * + * Because a valid XML document can only contain a single node and it must be an element, all + * {@link org.w3c.dom.Document}s have a root element {@literal <state/>}. This serialization implementation + * also impose that the root element can only contain a single node, of any type. + * + * {@literal null} is represented as {@literal <null/>}. + * Plain values are represented as {@link org.w3c.dom.Text} nodes. + * Iterables and Streams are represented as {@literal <collection/>} {@link org.w3c.dom.Element}s. + * Maps are represented as {@literal <dictionary/>} {@link org.w3c.dom.Element}s. + * + * This is how a {@literal null} plain value is represented: {@literal <state><null/></state>}. + * And a plain {@literal LocalDate}: {@literal <state>2017-01-01</state>} + * + * This is how a fictional value including a collection and a map is represented: + * <code> + * <state> + * <stringProperty>and it's value</stringProperty> + * <bigDecimalProperty>4.22376931348623157E+310</bigDecimalProperty> + * <nullProperty><null/></nullProperty> + * <booleanProperty>false</booleanProperty> + * <stringCollectionProperty> + * <collection> + * item1 + * item2 <!-- As multiple text nodes --> + * </collection> + * </stringCollectionProperty> + * <mapProperty> + * <map> + * <foo>bar</foo> + * <bazar>cathedral</bazar> + * </map> + * </mapProperty> + * <complexKeyMapProperty> + * <map> + * <entry> + * <key> + * <foo>bar</foo> + * <bazar>cathedral</bazar> + * </key> + * <value>23</value> + * </entry> + * <entry> + * <key> + * <foo>baz</foo> + * <bazar>bar</bazar> + * </key> + * <value>42</value> + * </entry> + * </map> + * </complexKeyMapProperty> + * </state> + * </code> + * + */ +@Mixins( { JavaxXmlSerializer.class, JavaxXmlDeserializer.class } ) +public interface JavaxXmlSerialization extends XmlSerialization +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationAssembler.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationAssembler.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationAssembler.java new file mode 100644 index 0000000..b97bf12 --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationAssembler.java @@ -0,0 +1,58 @@ +/* + * 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.serialization.javaxxml; + +import org.apache.polygene.api.serialization.Deserializer; +import org.apache.polygene.api.serialization.Serialization; +import org.apache.polygene.api.serialization.Serializer; +import org.apache.polygene.bootstrap.Assemblers; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.bootstrap.ServiceDeclaration; +import org.apache.polygene.spi.serialization.XmlDeserializer; +import org.apache.polygene.spi.serialization.XmlSerialization; +import org.apache.polygene.spi.serialization.XmlSerializer; + +public class JavaxXmlSerializationAssembler extends Assemblers.VisibilityIdentity<JavaxXmlSerializationAssembler> +{ + private JavaxXmlSettings settings; + + public JavaxXmlSerializationAssembler withXmlSettings( JavaxXmlSettings settings ) + { + this.settings = settings; + return this; + } + + @Override + public void assemble( ModuleAssembly module ) + { + ServiceDeclaration declaration = module.services( JavaxXmlSerializationService.class ) + .withTypes( Serialization.class, + Serializer.class, Deserializer.class, + XmlSerialization.class, + XmlSerializer.class, XmlDeserializer.class ) + .visibleIn( visibility() ); + if( hasIdentity() ) + { + declaration.identifiedBy( identity() ); + } + if( settings != null ) + { + declaration.setMetaInfo( settings ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/17b11697/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationService.java ---------------------------------------------------------------------- diff --git a/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationService.java b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationService.java new file mode 100644 index 0000000..3917340 --- /dev/null +++ b/extensions/serialization-javaxxml/src/main/java/org/apache/polygene/serialization/javaxxml/JavaxXmlSerializationService.java @@ -0,0 +1,372 @@ +/* + * 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.serialization.javaxxml; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.ZonedDateTime; +import java.util.function.BiFunction; +import java.util.function.Function; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.identity.Identity; +import org.apache.polygene.api.identity.StringIdentity; +import org.apache.polygene.api.injection.scope.This; +import org.apache.polygene.api.injection.scope.Uses; +import org.apache.polygene.api.mixin.Mixins; +import org.apache.polygene.api.service.ServiceActivation; +import org.apache.polygene.api.service.ServiceDescriptor; +import org.apache.polygene.api.type.ValueType; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +@Mixins( JavaxXmlSerializationService.Activation.class ) +public interface JavaxXmlSerializationService extends JavaxXmlSerialization, ServiceActivation +{ + class Activation implements ServiceActivation + { + @Uses + private ServiceDescriptor descriptor; + + @This + private JavaxXmlAdapters adapters; + + private boolean registrationDone = false; + + @Override + public void activateService() + { + if( !registrationDone ) + { + registerCustomAdapters(); + registerBaseAdapters(); + registrationDone = true; + } + } + + @Override + public void passivateService() {} + + private void registerCustomAdapters() + { + JavaxXmlSettings.orDefault( descriptor.metaInfo( JavaxXmlSettings.class ) ) + .getAdapters() + .forEach( ( valueType, adapter ) -> adapters.registerAdapter( valueType, adapter ) ); + } + + private void registerBaseAdapters() + { + // Primitive Value types + adapters.registerAdapter( ValueType.STRING, new StringAdapter() ); + adapters.registerAdapter( ValueType.CHARACTER, new CharacterAdapter() ); + adapters.registerAdapter( ValueType.BOOLEAN, new BooleanAdapter() ); + adapters.registerAdapter( ValueType.INTEGER, new IntegerAdapter() ); + adapters.registerAdapter( ValueType.LONG, new LongAdapter() ); + adapters.registerAdapter( ValueType.SHORT, new ShortAdapter() ); + adapters.registerAdapter( ValueType.BYTE, new ByteAdapter() ); + adapters.registerAdapter( ValueType.FLOAT, new FloatAdapter() ); + adapters.registerAdapter( ValueType.DOUBLE, new DoubleAdapter() ); + + // Number types + adapters.registerAdapter( ValueType.BIG_DECIMAL, new BigDecimalAdapter() ); + adapters.registerAdapter( ValueType.BIG_INTEGER, new BigIntegerAdapter() ); + + // Date types + adapters.registerAdapter( ValueType.INSTANT, new InstantAdapter() ); + adapters.registerAdapter( ValueType.ZONED_DATE_TIME, new ZonedDateTimeAdapter() ); + adapters.registerAdapter( ValueType.OFFSET_DATE_TIME, new OffsetDateTimeAdapter() ); + adapters.registerAdapter( ValueType.LOCAL_DATE_TIME, new LocalDateTimeAdapter() ); + adapters.registerAdapter( ValueType.LOCAL_DATE, new LocalDateAdapter() ); + adapters.registerAdapter( ValueType.LOCAL_TIME, new LocalTimeAdapter() ); + adapters.registerAdapter( ValueType.DURATION, new DurationAdapter() ); + adapters.registerAdapter( ValueType.PERIOD, new PeriodAdapter() ); + + // Other supported types + adapters.registerAdapter( ValueType.IDENTITY, new IdentityAdapter() ); + adapters.registerAdapter( ValueType.ENTITY_REFERENCE, new EntityReferenceAdapter() ); + } + + private static abstract class ToStringTextNodeAdapter<T> implements JavaxXmlAdapter<T> + { + @Override + public Node serialize( Document document, Object object, Function<Object, Node> serializationFunction ) + { + return document.createTextNode( object.toString() ); + } + } + + private static class StringAdapter extends ToStringTextNodeAdapter<String> + { + @Override + public Class<String> type() { return String.class; } + + @Override + public String deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return node.getNodeValue(); + } + } + + private static class CharacterAdapter extends ToStringTextNodeAdapter<Character> + { + @Override + public Class<Character> type() { return Character.class; } + + @Override + public Character deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + String string = node.getNodeValue(); + return string.isEmpty() ? null : string.charAt( 0 ); + } + } + + private static class BooleanAdapter extends ToStringTextNodeAdapter<Boolean> + { + @Override + public Class<Boolean> type() { return Boolean.class; } + + @Override + public Boolean deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Boolean.valueOf( node.getNodeValue() ); + } + } + + private static class IntegerAdapter extends ToStringTextNodeAdapter<Integer> + { + @Override + public Class<Integer> type() { return Integer.class; } + + @Override + public Integer deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Integer.valueOf( node.getNodeValue() ); + } + } + + private static class LongAdapter extends ToStringTextNodeAdapter<Long> + { + @Override + public Class<Long> type() { return Long.class; } + + @Override + public Long deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Long.valueOf( node.getNodeValue() ); + } + } + + private static class ShortAdapter extends ToStringTextNodeAdapter<Short> + { + @Override + public Class<Short> type() { return Short.class; } + + @Override + public Short deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Short.valueOf( node.getNodeValue() ); + } + } + + private static class ByteAdapter extends ToStringTextNodeAdapter<Byte> + { + @Override + public Class<Byte> type() { return Byte.class; } + + @Override + public Byte deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Byte.valueOf( node.getNodeValue() ); + } + } + + private static class FloatAdapter extends ToStringTextNodeAdapter<Float> + { + @Override + public Class<Float> type() { return Float.class; } + + @Override + public Float deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Float.valueOf( node.getNodeValue() ); + } + } + + private static class DoubleAdapter extends ToStringTextNodeAdapter<Double> + { + @Override + public Class<Double> type() { return Double.class; } + + @Override + public Double deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Double.valueOf( node.getNodeValue() ); + } + } + + private static class BigDecimalAdapter extends ToStringTextNodeAdapter<BigDecimal> + { + @Override + public Class<BigDecimal> type() { return BigDecimal.class; } + + @Override + public BigDecimal deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return new BigDecimal( node.getNodeValue() ); + } + } + + private static class BigIntegerAdapter extends ToStringTextNodeAdapter<BigInteger> + { + @Override + public Class<BigInteger> type() { return BigInteger.class; } + + @Override + public BigInteger deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return new BigInteger( node.getNodeValue() ); + } + } + + private static class InstantAdapter extends ToStringTextNodeAdapter<Instant> + { + @Override + public Class<Instant> type() { return Instant.class; } + + @Override + public Instant deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Instant.parse( node.getNodeValue() ); + } + } + + private static class ZonedDateTimeAdapter extends ToStringTextNodeAdapter<ZonedDateTime> + { + @Override + public Class<ZonedDateTime> type() { return ZonedDateTime.class; } + + @Override + public ZonedDateTime deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return ZonedDateTime.parse( node.getNodeValue() ); + } + } + + private static class OffsetDateTimeAdapter extends ToStringTextNodeAdapter<OffsetDateTime> + { + @Override + public Class<OffsetDateTime> type() { return OffsetDateTime.class; } + + @Override + public OffsetDateTime deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return OffsetDateTime.parse( node.getNodeValue() ); + } + } + + private static class LocalDateTimeAdapter extends ToStringTextNodeAdapter<LocalDateTime> + { + @Override + public Class<LocalDateTime> type() { return LocalDateTime.class; } + + @Override + public LocalDateTime deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return LocalDateTime.parse( node.getNodeValue() ); + } + } + + private static class LocalDateAdapter extends ToStringTextNodeAdapter<LocalDate> + { + @Override + public Class<LocalDate> type() { return LocalDate.class; } + + @Override + public LocalDate deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return LocalDate.parse( node.getNodeValue() ); + } + } + + private static class LocalTimeAdapter extends ToStringTextNodeAdapter<LocalTime> + { + @Override + public Class<LocalTime> type() { return LocalTime.class; } + + @Override + public LocalTime deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return LocalTime.parse( node.getNodeValue() ); + } + } + + private static class DurationAdapter extends ToStringTextNodeAdapter<Duration> + { + @Override + public Class<Duration> type() { return Duration.class; } + + @Override + public Duration deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Duration.parse( node.getNodeValue() ); + } + } + + private static class PeriodAdapter extends ToStringTextNodeAdapter<Period> + { + @Override + public Class<Period> type() { return Period.class; } + + @Override + public Period deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return Period.parse( node.getNodeValue() ); + } + } + + private static class IdentityAdapter extends ToStringTextNodeAdapter<Identity> + { + @Override + public Class<Identity> type() { return Identity.class; } + + @Override + public Identity deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return StringIdentity.fromString( node.getNodeValue() ); + } + } + + private static class EntityReferenceAdapter extends ToStringTextNodeAdapter<EntityReference> + { + @Override + public Class<EntityReference> type() { return EntityReference.class; } + + @Override + public EntityReference deserialize( Node node, BiFunction<Node, ValueType, Object> deserializationFunction ) + { + return EntityReference.parseEntityReference( node.getNodeValue() ); + } + } + } +}
