You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by da...@apache.org on 2016/09/08 13:17:46 UTC
svn commit: r1759814 - in /felix/trunk/converter/codec/src:
main/java/org/apache/felix/codec/ main/java/org/apache/felix/serializer/
main/java/org/apache/felix/serializer/impl/
main/java/org/apache/felix/serializer/impl/yaml/
main/java/org/apache/felix...
Author: davidb
Date: Thu Sep 8 13:17:46 2016
New Revision: 1759814
URL: http://svn.apache.org/viewvc?rev=1759814&view=rev
Log:
Felix Converter - rename codec to serializer
Added:
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/Activator.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlCodecImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlDecodingImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlEncodingImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonCodecImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonDecodingImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonEncodingImpl.java
felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonParser.java
felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/
felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Deserializing.java
felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializer.java
felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializing.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonCodecTest.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonParserTest.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonSerializationTest.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyDTO.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyEmbeddedDTO.java
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/
felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/YamlSerializationTest.java
Removed:
felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/
felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/
felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/Activator.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/Activator.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/Activator.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,47 @@
+/*
+ * 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.felix.serializer.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.serializer.impl.yaml.YamlCodecImpl;
+import org.apache.felix.serializer.serializer.json.JsonCodecImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.serializer.Serializer;
+
+public class Activator implements BundleActivator {
+ @Override
+ public void start(BundleContext context) throws Exception {
+ Dictionary<String, Object> jsonProps = new Hashtable<>();
+ jsonProps.put("osgi.codec.mimetype", new String[] {
+ "application/json", "application/x-javascript", "text/javascript",
+ "text/x-javascript", "text/x-json" });
+ context.registerService(Serializer.class, new JsonCodecImpl(), jsonProps);
+
+ Dictionary<String, Object> yamlProps = new Hashtable<>();
+ yamlProps.put("osgi.codec.mimetype", new String[] {
+ "text/yaml", "text/x-yaml", "application/yaml",
+ "application/x-yaml" });
+ context.registerService(Serializer.class, new YamlCodecImpl(), yamlProps);
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlCodecImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlCodecImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlCodecImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlCodecImpl.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,61 @@
+/*
+ * 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.felix.serializer.impl.yaml;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.service.converter.StandardConverter;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+import org.osgi.service.serializer.Serializer;
+import org.osgi.service.serializer.Deserializing;
+import org.osgi.service.serializer.Serializing;
+
+public class YamlCodecImpl implements Serializer {
+ private Map<String, Object> configuration = new ConcurrentHashMap<>();
+ private Converter converter = new StandardConverter();
+
+ @Override
+ public Serializer with(Converter c) {
+ converter = c;
+ return this;
+ }
+
+ @Override
+ public <T> Deserializing<T> deserialize(Class<T> cls) {
+ return new YamlDecodingImpl<T>(converter, cls);
+ }
+
+ @Override
+ public <T> Deserializing<T> deserialize(TypeReference<T> ref) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Deserializing<?> deserialize(Type type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Serializing serialize(Object obj) {
+ return new YamlEncodingImpl(converter, configuration, obj);
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlDecodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlDecodingImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlDecodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlDecodingImpl.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,75 @@
+/*
+ * 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.felix.serializer.impl.yaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Scanner;
+
+import org.apache.felix.converter.impl.Util;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.serializer.Deserializing;
+import org.yaml.snakeyaml.Yaml;
+
+public class YamlDecodingImpl<T> implements Deserializing<T> {
+ private final Converter converter;
+ private final Class<T> clazz;
+
+ public YamlDecodingImpl(Converter c, Class<T> cls) {
+ converter = c;
+ clazz = cls;
+ }
+
+ @Override
+ public T from(InputStream in) {
+ return from(in, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public T from(InputStream in, Charset charset) {
+ try {
+ byte[] bytes = Util.readStream(in);
+ String s = new String(bytes, charset);
+ return from(s);
+ } catch (IOException e) {
+ throw new ConversionException("Error reading inputstream", e);
+ }
+ }
+
+ @Override
+ public T from(Readable in) {
+ try (Scanner s = new Scanner(in)) {
+ s.useDelimiter("\\Z");
+ return from(s.next());
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T from(CharSequence in) {
+ Yaml yaml = new Yaml();
+ Object res = yaml.load(in.toString());
+
+ if (res.getClass().isAssignableFrom(clazz))
+ return (T) res;
+
+ return converter.convert(res).to(clazz);
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlEncodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlEncodingImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlEncodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/impl/yaml/YamlEncodingImpl.java Thu Sep 8 13:17:46 2016
@@ -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.felix.serializer.impl.yaml;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.serializer.Serializing;
+
+public class YamlEncodingImpl implements Serializing {
+ private final Converter converter;
+ private final Map<String, Object> configuration;
+ private final Object object;
+ private final int indentation = 2;
+
+ public YamlEncodingImpl(Converter c, Map<String, Object> cfg, Object obj) {
+ converter = c;
+ configuration = cfg;
+ object = obj;
+ }
+
+ @Override
+ public Appendable to(Appendable out) {
+ try {
+ out.append(encode(object));
+ return out;
+ } catch (IOException e) {
+ throw new ConversionException("Problem converting to YAML", e);
+ }
+ }
+
+
+ @Override
+ public void to(OutputStream os) throws IOException {
+ to(os, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public void to(OutputStream os, Charset charset) {
+ try {
+ os.write(encode(object).getBytes(charset));
+ } catch (IOException e) {
+ throw new ConversionException("Problem converting to YAML", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return encode(object);
+ }
+
+ private String encode(Object obj) {
+ return encode(obj, 0);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private String encode(Object obj, int level) {
+ if (obj == null)
+ return "";
+
+ if (obj instanceof Map) {
+ return encodeMap((Map) obj, level);
+ } else if (obj instanceof Collection) {
+ return encodeCollection((Collection) obj, level);
+ } else if (obj.getClass().isArray()) {
+ return encodeCollection(asCollection(obj), level);
+ } else if (obj instanceof Number) {
+ return obj.toString();
+ } else if (obj instanceof Boolean) {
+ return obj.toString();
+ }
+
+ return "'" + converter.convert(obj).to(String.class) + "'";
+ }
+
+ private Collection<?> asCollection(Object arr) {
+ // Arrays.asList() doesn't work for primitive arrays
+ int len = Array.getLength(arr);
+ List<Object> l = new ArrayList<>(len);
+ for (int i=0; i<len; i++) {
+ l.add(Array.get(arr, i));
+ }
+ return l;
+ }
+
+ private String encodeCollection(Collection<?> collection, int level) {
+ StringBuilder sb = new StringBuilder();
+ for (Object o : collection) {
+ sb.append("\n");
+ sb.append(getIdentPrefix(level));
+ sb.append("- ");
+ sb.append(encode(o, level + 1));
+ }
+ return sb.toString();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private String encodeMap(Map m, int level) {
+ StringBuilder sb = new StringBuilder();
+ for (Entry entry : (Set<Entry>) m.entrySet()) {
+ sb.append("\n");
+ sb.append(getIdentPrefix(level));
+ sb.append(entry.getKey().toString());
+ sb.append(": ");
+ sb.append(encode(entry.getValue(), level + 1));
+ }
+
+ return sb.toString();
+ }
+
+ private String getIdentPrefix(int level) {
+ int numSpaces = indentation * level;
+ StringBuilder sb = new StringBuilder(numSpaces);
+ for (int i=0; i < numSpaces; i++)
+ sb.append(' ');
+ return sb.toString();
+ }
+
+ @Override
+ public Serializing pretty() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Serializing ignoreNull() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonCodecImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonCodecImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonCodecImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonCodecImpl.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,135 @@
+/*
+ * 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.felix.serializer.serializer.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.osgi.service.converter.StandardConverter;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+import org.osgi.service.serializer.Serializer;
+import org.osgi.service.serializer.Deserializing;
+import org.osgi.service.serializer.Serializing;
+
+public class JsonCodecImpl implements Serializer {
+ private Map<String, Object> configuration = new ConcurrentHashMap<>();
+ private ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
+ private Converter converter = new StandardConverter();
+
+ @Override
+ public Serializer with(Converter c) {
+ converter = c;
+ return this;
+ }
+
+ @Override
+ public <T> Deserializing<T> deserialize(Class<T> cls) {
+ return new JsonDecodingImpl<T>(converter, cls);
+ }
+
+ @Override
+ public Serializing serialize(Object obj) {
+ Serializing encoding = new JsonEncodingImpl(converter, configuration, obj);
+
+ if (pretty()) {
+ Boolean top = threadLocal.get();
+ if (top == null) {
+ threadLocal.set(Boolean.TRUE);
+
+ // TODO implement this properly, the following it just a dev temp thing
+ encoding = new EncodingWrapper("{}{}{}{}{}", encoding, "{}{}{}{}{}");
+ }
+ }
+ return encoding;
+ }
+
+ private boolean pretty() {
+ return Boolean.TRUE.equals(Boolean.parseBoolean((String) configuration.get("pretty")));
+ }
+
+ private class EncodingWrapper implements Serializing {
+ private final Serializing delegate;
+ private String prefix;
+ private String postfix;
+
+ EncodingWrapper(String pre, Serializing encoding, String post) {
+ prefix = pre;
+ delegate = encoding;
+ postfix = post;
+ }
+
+ @Override
+ public void to(OutputStream os) {
+ try {
+ os.write(toString().getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return prefix + delegate.toString() + postfix;
+ } finally {
+ threadLocal.set(null);
+ }
+ }
+
+ @Override
+ public Serializing ignoreNull() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Serializing pretty() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void to(OutputStream out, Charset charset) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Appendable to(Appendable out) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ }
+
+ @Override
+ public <T> Deserializing<T> deserialize(TypeReference<T> ref) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Deserializing<?> deserialize(Type type) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonDecodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonDecodingImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonDecodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonDecodingImpl.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,74 @@
+/*
+ * 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.felix.serializer.serializer.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.felix.converter.impl.Util;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.serializer.Deserializing;
+
+public class JsonDecodingImpl<T> implements Deserializing<T> {
+ private final Class<T> clazz;
+ private final Converter converter;
+
+ public JsonDecodingImpl(Converter c, Class<T> cls) {
+ converter = c;
+ clazz = cls;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T from(CharSequence in) {
+ JsonParser jp = new JsonParser(in);
+ Map<?,?> m = jp.getParsed();
+ if (m.getClass().isAssignableFrom(clazz))
+ return (T) m;
+
+ return converter.convert(m).to(clazz);
+ }
+
+ @Override
+ public T from(InputStream in) {
+ return from(in, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public T from(InputStream in, Charset charset) {
+ try {
+ byte[] bytes = Util.readStream(in);
+ String s = new String(bytes, charset);
+ return from(s);
+ } catch (IOException e) {
+ throw new ConversionException("Error reading inputstream", e);
+ }
+ }
+
+ @Override
+ public T from(Readable in) {
+ try (Scanner s = new Scanner(in)) {
+ s.useDelimiter("\\Z");
+ return from(s.next());
+ }
+ }
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonEncodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonEncodingImpl.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonEncodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonEncodingImpl.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,160 @@
+/*
+ * 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.felix.serializer.serializer.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Array;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.osgi.dto.DTO;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.serializer.Serializing;
+
+public class JsonEncodingImpl implements Serializing {
+ private final Converter converter;
+ private final Map<String, Object> configuration;
+ private final Object object;
+ private final boolean ignoreNull;
+
+ JsonEncodingImpl(Converter c, Map<String, Object> cfg, Object obj) {
+ converter = c;
+ configuration = cfg;
+ ignoreNull = Boolean.TRUE.equals(Boolean.parseBoolean((String) configuration.get("ignoreNull")));
+ object = obj;
+ }
+
+ @Override
+ public Appendable to(Appendable out) {
+ try {
+ out.append(encode(object));
+ return out;
+ } catch (IOException e) {
+ throw new ConversionException("Problem converting to JSON", e);
+ }
+ }
+
+ @Override
+ public void to(OutputStream os, Charset charset) {
+ try {
+ os.write(encode(object).getBytes(charset));
+ } catch (IOException e) {
+ throw new ConversionException("Problem converting to JSON", e);
+ }
+ }
+
+ @Override
+ public void to(OutputStream out) throws IOException {
+ to(out, StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public String toString() {
+ return encode(object);
+ }
+
+ @SuppressWarnings("rawtypes")
+ private String encode(Object obj) {
+ if (obj == null) {
+ return ignoreNull ? "" : "null";
+ }
+
+ if (obj instanceof Map) {
+ return encodeMap((Map) obj);
+ } else if (obj instanceof Collection) {
+ return encodeCollection((Collection) obj);
+ } else if (obj instanceof DTO) {
+ return encodeMap(converter.convert(obj).to(Map.class));
+ } else if (obj.getClass().isArray()) {
+ return encodeCollection(asCollection(obj));
+ } else if (obj instanceof Number) {
+ return obj.toString();
+ } else if (obj instanceof Boolean) {
+ return obj.toString();
+ }
+
+ return "\"" + converter.convert(obj).to(String.class) + "\"";
+ }
+
+ private Collection<?> asCollection(Object arr) {
+ // Arrays.asList() doesn't work for primitive arrays
+ int len = Array.getLength(arr);
+ List<Object> l = new ArrayList<>(len);
+ for (int i=0; i<len; i++) {
+ l.add(Array.get(arr, i));
+ }
+ return l;
+ }
+
+ private String encodeCollection(Collection<?> collection) {
+ StringBuilder sb = new StringBuilder("[");
+
+ boolean first = true;
+ for (Object o : collection) {
+ if (first)
+ first = false;
+ else
+ sb.append(',');
+
+ sb.append(encode(o));
+ }
+
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private String encodeMap(Map m) {
+ StringBuilder sb = new StringBuilder("{");
+ for (Entry entry : (Set<Entry>) m.entrySet()) {
+ if (entry.getKey() == null || entry.getValue() == null)
+ if (ignoreNull)
+ continue;
+
+ if (sb.length() > 1)
+ sb.append(',');
+ sb.append('"');
+ sb.append(entry.getKey().toString());
+ sb.append("\":");
+ sb.append(encode(entry.getValue()));
+ }
+ sb.append("}");
+
+ return sb.toString();
+ }
+
+ @Override
+ public Serializing ignoreNull() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Serializing pretty() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonParser.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonParser.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/serializer/serializer/json/JsonParser.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,253 @@
+/*
+ * 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.felix.serializer.serializer.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.converter.impl.Util;
+
+/**
+ * A very small JSON parser.
+ *
+ * The JSON input is parsed into an object structure in the following way:
+ * <ul>
+ * <li>Object names are represented as a {@link String}.
+ * <li>String values are represented as a {@link String}.
+ * <li>Numeric values are represented as a {@link Long} (TODO support floats).
+ * <li>Boolean values are represented as a {@link Boolean}.
+ * <li>Nested JSON objects are parsed into a {@link java.util.Map Map<String, Object>}.
+ * <li>JSON lists are parsed into a {@link java.util.List} which may contain any of the above values.
+ * </ul>
+ */
+public class JsonParser {
+ private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("^\\s*[\"](.+?)[\"]\\s*[:]\\s*(.+)$");
+
+ private enum Scope { QUOTE, CURLY, BRACKET;
+ static Scope getScope(char c) {
+ switch (c) {
+ case '"':
+ return QUOTE;
+ case '[':
+ case ']':
+ return BRACKET;
+ case '{':
+ case '}':
+ return CURLY;
+ default:
+ return null;
+ }
+ }
+ }
+
+ static class Pair<K, V> {
+ final K key;
+ final V value;
+
+ Pair(K k, V v) {
+ key = k;
+ value = v;
+ }
+ }
+
+ private final Map<String, Object> parsed;
+
+ public JsonParser(CharSequence json) {
+ String str = json.toString();
+ str = str.trim().replace('\n', ' ');
+ parsed = parseObject(str);
+ }
+
+ public JsonParser(InputStream is) throws IOException {
+ this(readStreamAsString(is));
+ }
+
+ public Map<String, Object> getParsed() {
+ return parsed;
+ }
+
+ private static Pair<String, Object> parseKeyValue(String jsonKeyValue) {
+ Matcher matcher = KEY_VALUE_PATTERN.matcher(jsonKeyValue);
+ if (!matcher.matches() || matcher.groupCount() < 2) {
+ throw new IllegalArgumentException("Malformatted JSON key-value pair: " + jsonKeyValue);
+ }
+
+ return new Pair<>(matcher.group(1), parseValue(matcher.group(2)));
+ }
+
+ private static Object parseValue(String jsonValue) {
+ jsonValue = jsonValue.trim();
+
+ switch (jsonValue.charAt(0)) {
+ case '\"':
+ if (!jsonValue.endsWith("\""))
+ throw new IllegalArgumentException("Malformatted JSON string: " + jsonValue);
+
+ return jsonValue.substring(1, jsonValue.length() - 1);
+ case '[':
+ List<Object> entries = new ArrayList<>();
+ for (String v : parseListValuesRaw(jsonValue)) {
+ entries.add(parseValue(v));
+ }
+ return entries;
+ case '{':
+ return parseObject(jsonValue);
+ case 't':
+ case 'T':
+ case 'f':
+ case 'F':
+ return Boolean.parseBoolean(jsonValue);
+ case 'n':
+ case 'N':
+ return null;
+ default:
+ return Long.parseLong(jsonValue);
+ }
+ }
+
+ private static Map<String, Object> parseObject(String jsonObject) {
+ if (!(jsonObject.startsWith("{") && jsonObject.endsWith("}")))
+ throw new IllegalArgumentException("Malformatted JSON object: " + jsonObject);
+
+ jsonObject = jsonObject.substring(1, jsonObject.length() - 1);
+ Map<String, Object> values = new HashMap<>();
+ for (String element : parseKeyValueListRaw(jsonObject)) {
+ Pair<String, Object> pair = parseKeyValue(element);
+ values.put(pair.key, pair.value);
+ }
+
+ return values;
+ }
+
+ private static List<String> parseKeyValueListRaw(String jsonKeyValueList) {
+ jsonKeyValueList = jsonKeyValueList + ","; // append comma to simplify parsing
+ List<String> elements = new ArrayList<>();
+
+ int i=0;
+ int start=0;
+ Stack<Scope> scopeStack = new Stack<>();
+ while (i < jsonKeyValueList.length()) {
+ char curChar = jsonKeyValueList.charAt(i);
+ switch (curChar) {
+ case '"':
+ if (i > 0 && jsonKeyValueList.charAt(i-1) == '\\') {
+ // it's escaped, ignore for now
+ } else {
+ if (!scopeStack.empty() && scopeStack.peek() == Scope.QUOTE) {
+ scopeStack.pop();
+ } else {
+ scopeStack.push(Scope.QUOTE);
+ }
+ }
+ break;
+ case '[':
+ case '{':
+ if ((scopeStack.empty() ? null : scopeStack.peek()) == Scope.QUOTE) {
+ // inside quotes, ignore
+ } else {
+ scopeStack.push(Scope.getScope(curChar));
+ }
+ break;
+ case ']':
+ case '}':
+ Scope curScope = scopeStack.empty() ? null : scopeStack.peek();
+ if (curScope == Scope.QUOTE) {
+ // inside quotes, ignore
+ } else {
+ Scope newScope = Scope.getScope(curChar);
+ if (curScope == newScope) {
+ scopeStack.pop();
+ } else {
+ throw new IllegalArgumentException("Unbalanced closing " +
+ curChar + " in: " + jsonKeyValueList);
+ }
+ }
+ break;
+ case ',':
+ if (scopeStack.empty()) {
+ elements.add(jsonKeyValueList.substring(start, i));
+ start = i+1;
+ }
+ break;
+ }
+
+ i++;
+ }
+ return elements;
+ }
+
+ private static List<String> parseListValuesRaw(String jsonList) {
+ if (!(jsonList.startsWith("[") && jsonList.endsWith("]")))
+ throw new IllegalArgumentException("Malformatted JSON list: " + jsonList);
+
+ jsonList = jsonList.substring(1, jsonList.length() - 1);
+ return parseKeyValueListRaw(jsonList);
+ }
+
+ private static String readStreamAsString(InputStream is) throws IOException {
+ byte [] bytes = Util.readStream(is);
+ if (bytes.length < 5)
+ // need at least 5 bytes to establish the encoding
+ throw new IllegalArgumentException("Malformatted JSON");
+
+ int offset = 0;
+ if ((bytes[0] == -1 && bytes[1] == -2)
+ || (bytes[0] == -2 && bytes[1] == -1)) {
+ // Skip UTF16/UTF32 Byte Order Mark (BOM)
+ offset = 2;
+ }
+
+ /* Infer the encoding as described in section 3 of http://www.ietf.org/rfc/rfc4627.txt
+ * which reads:
+ * Encoding
+ *
+ * JSON text SHALL be encoded in Unicode. The default encoding is
+ * UTF-8.
+ *
+ * Since the first two characters of a JSON text will always be ASCII
+ * characters [RFC0020], it is possible to determine whether an octet
+ * stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
+ * at the pattern of nulls in the first four octets.
+ *
+ * 00 00 00 xx UTF-32BE
+ * 00 xx 00 xx UTF-16BE
+ * xx 00 00 00 UTF-32LE
+ * xx 00 xx 00 UTF-16LE
+ * xx xx xx xx UTF-8
+ */
+ String encoding;
+ if (bytes[offset + 2] == 0) {
+ if (bytes[offset + 1] != 0) {
+ encoding = "UTF-16";
+ } else {
+ encoding = "UTF-32";
+ }
+ } else if (bytes[offset + 1] == 0) {
+ encoding = "UTF-16";
+ } else {
+ encoding = "UTF-8";
+ }
+ return new String(bytes, encoding);
+ }
+}
\ No newline at end of file
Added: felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Deserializing.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Deserializing.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Deserializing.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Deserializing.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) OSGi Alliance (2016). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.serializer;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Interface to specify the source of the decoding operation
+ *
+ * @param <T> The target type for the decoding operation.
+ * @author $Id$
+ * @ThreadSafe
+ */
+@ProviderType
+public interface Deserializing<T> {
+ /**
+ * Use an input stream as the source of the decoding operation. As encoding
+ * UTF-8 is used.
+ *
+ * @param in The stream to use.
+ * @return the decoded object.
+ */
+ T from(InputStream in);
+
+ /**
+ * Use an input stream as the source of the decoding operation.
+ *
+ * @param in The stream to use.
+ * @param charset The character set to use.
+ * @return the decoded object.
+ */
+ T from(InputStream in, Charset charset);
+
+ /**
+ * Use a Readable as the source of the decoding operation.
+ *
+ * @param in The readable to use.
+ * @return the decoded object.
+ */
+ T from(Readable in);
+
+ /**
+ * Use a Char Sequence as the source of the decoding operation.
+ *
+ * @param in The char sequence to use.
+ * @return the decoded object.
+ */
+ T from(CharSequence in);
+}
Added: felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializer.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializer.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializer.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializer.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) OSGi Alliance (2016). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.serializer;
+
+import java.lang.reflect.Type;
+
+import org.osgi.annotation.versioning.ProviderType;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+
+/**
+ * The Codec service can be used to encode a given object in a certain
+ * representation, for example JSON, YAML or XML. The Codec service can also
+ * decode the representation it produced. A single Codec service can
+ * encode/decode only a single format. To support multiple encoding formats
+ * register multiple services.
+ *
+ * @author $Id$
+ * @ThreadSafe
+ */
+@ProviderType
+public interface Serializer {
+ /**
+ * Start specifying a decode operation.
+ *
+ * @param <T> The type to decode to.
+ * @param cls The class to decode to.
+ * @return A {@link Deserializing} object to specify the source for the decode
+ * operation.
+ */
+ <T> Deserializing<T> deserialize(Class<T> cls);
+
+ /**
+ * Start specifying a decode operation.
+ *
+ * @param <T> The type to decode to.
+ * @param ref A type reference for the target type.
+ * @return A {@link Deserializing} object to specify the source for the decode
+ * operation.
+ */
+ <T> Deserializing<T> deserialize(TypeReference<T> ref);
+
+ /**
+ * Start specifying a decode operation.
+ *
+ * @param type The type to convert to.
+ * @return A {@link Deserializing} object to specify the source for the decode
+ * operation.
+ */
+ Deserializing< ? > deserialize(Type type);
+
+ /**
+ * Start specifying an encode operation.
+ *
+ * @param obj The object to encode.
+ * @return an Encoding object to specify the target for the decode
+ * operation.
+ */
+ Serializing serialize(Object obj);
+
+ /**
+ * Specify the converter to be used by the code, if an alternative, adapted,
+ * converter is to be used.
+ *
+ * @param converter The converter to use.
+ * @return A codec that uses the converter as specified.
+ */
+ Serializer with(Converter converter);
+}
Added: felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializing.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializing.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializing.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/serializer/Serializing.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) OSGi Alliance (2016). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Interface to specify the target of the encoding operation.
+ *
+ * @author $Id$
+ * @ThreadSafe
+ */
+@ProviderType
+public interface Serializing {
+ /**
+ * Specify that keys with a {@code null} value must not appear in the
+ * result. If not specified {@code null} values will be included in the
+ * result.
+ *
+ * @return This Encoding object to allow further invocations on it.
+ */
+ Serializing ignoreNull();
+
+ /**
+ * Specify that the encoded output should be formatted to look 'pretty',
+ * which may make it easier for humans to read. If not specified, the
+ * encoded output should be formatted to be compact, so save space.
+ *
+ * @return This Encoding object to allow further invocations on it.
+ */
+ Serializing pretty();
+
+ /**
+ * Use an output stream as the target of the encoding operation. UTF-8 will
+ * be used if applicable, the character set may not apply to binary
+ * encodings.
+ *
+ * @param out The output stream to use.
+ * @throws IOException If an I/O error occurred.
+ */
+ void to(OutputStream out) throws IOException;
+
+ /**
+ * Use an output stream as the target of the encoding operation.
+ *
+ * @param out The output stream to use.
+ * @param charset The character set to use, if applicable, the character set
+ * may not apply to binary encodings.
+ * @throws IOException If an I/O error occurred.
+ */
+ void to(OutputStream out, Charset charset) throws IOException;
+
+ /**
+ * Encode the object and append the result to an appendable.
+ *
+ * @param out The appendable object to use.
+ * @return The appendable object provided in, which allows further appends
+ * to it be done in a fluent programming style.
+ */
+ Appendable to(Appendable out);
+
+ /**
+ * Encode the object and return the result as a string.
+ *
+ * @return The encoded object.
+ */
+ @Override
+ String toString();
+}
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonCodecTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonCodecTest.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonCodecTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonCodecTest.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,145 @@
+/*
+ * 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.felix.serializer.impl.json;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.serializer.impl.json.MyDTO.Count;
+import org.apache.felix.serializer.impl.json.MyEmbeddedDTO.Alpha;
+import org.apache.felix.serializer.serializer.json.JsonCodecImpl;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.converter.Adapter;
+import org.osgi.service.converter.StandardConverter;
+import org.osgi.service.converter.Converter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JsonCodecTest {
+ private Converter converter;
+
+ @Before
+ public void setUp() {
+ converter = new StandardConverter();
+ }
+
+ @After
+ public void tearDown() {
+ converter = null;
+ }
+
+ @Test
+ public void testJSONCodec() throws Exception {
+ Map<Object, Object> m1 = new HashMap<>();
+ m1.put("x", true);
+ m1.put("y", null);
+ Map<Object, Object> m = new HashMap<>();
+ m.put(1, 11L);
+ m.put("ab", "cd");
+ m.put(true, m1);
+
+ JsonCodecImpl jsonCodec = new JsonCodecImpl();
+ String json = jsonCodec.serialize(m).toString();
+
+ JSONObject jo = new JSONObject(json);
+ assertEquals(11, jo.getInt("1"));
+ assertEquals("cd", jo.getString("ab"));
+ JSONObject jo2 = jo.getJSONObject("true");
+ assertEquals(true, jo2.getBoolean("x"));
+ assertTrue(jo2.isNull("y"));
+
+ @SuppressWarnings("rawtypes")
+ Map m2 = jsonCodec.deserialize(Map.class).from(json);
+ // m2 is not exactly equal to m, as the keys are all strings now, this is unavoidable with JSON
+ assertEquals(m.size(), m2.size());
+ assertEquals(m.get(1), m2.get("1"));
+ assertEquals(m.get("ab"), m2.get("ab"));
+ assertEquals(m.get(true), m2.get("true"));
+ }
+
+ @Test
+ public void testCodecWithAdapter() throws JSONException {
+ Map<String, Foo> m1 = new HashMap<>();
+ m1.put("f", new Foo("fofofo"));
+ Map<String, Object> m = new HashMap<>();
+ m.put("submap", m1);
+
+ Adapter ca = converter.newAdapter();
+ ca.rule(Foo.class, String.class, Foo::tsFun, v -> Foo.fsFun(v));
+
+ JsonCodecImpl jsonCodec = new JsonCodecImpl();
+ String json = jsonCodec.with(ca).serialize(m).toString();
+
+ JSONObject jo = new JSONObject(json);
+ assertEquals(1, jo.length());
+ JSONObject jo1 = jo.getJSONObject("submap");
+ assertEquals("<fofofo>", jo1.getString("f"));
+
+ // TODO convert back into a Map<String, Foo> via TypeReference
+ }
+
+ @Test
+ public void testDTO() {
+ MyDTO dto = new MyDTO();
+ dto.count = Count.ONE;
+ dto.ping = "'";
+ dto.pong = Long.MIN_VALUE;
+
+ MyEmbeddedDTO embedded = new MyEmbeddedDTO();
+ embedded.alpha = Alpha.B;
+ embedded.marco = "jo !";
+ embedded.polo = 327;
+ dto.embedded = embedded;
+
+ JsonCodecImpl jsonCodec = new JsonCodecImpl();
+ String json = jsonCodec.serialize(dto).toString();
+ assertEquals(
+ "{\"ping\":\"'\",\"count\":\"ONE\",\"pong\":-9223372036854775808,"
+ + "\"embedded\":{\"polo\":327,\"alpha\":\"B\",\"marco\":\"jo !\"}}",
+ json);
+
+ MyDTO dto2 = jsonCodec.deserialize(MyDTO.class).from(json);
+ assertEquals(Count.ONE, dto2.count);
+ assertEquals("'", dto2.ping);
+ assertEquals(Long.MIN_VALUE, dto2.pong);
+ MyEmbeddedDTO embedded2 = dto2.embedded;
+ assertEquals(Alpha.B, embedded2.alpha);
+ assertEquals("jo !", embedded2.marco);
+ assertEquals(327, embedded2.polo);
+ }
+
+ static class Foo {
+ private final String val;
+
+ public Foo(String s) {
+ val = s;
+ }
+
+ public String tsFun() {
+ return "<" + val + ">";
+ }
+
+ public static Foo fsFun(String s) {
+ return new Foo(s.substring(1, s.length() - 1));
+ }
+ }
+}
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonParserTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonParserTest.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonParserTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonParserTest.java Thu Sep 8 13:17:46 2016
@@ -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.felix.serializer.impl.json;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import org.apache.felix.serializer.serializer.json.JsonParser;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JsonParserTest {
+ @Test
+ public void testJsonSimple() {
+ String json = "{\"hi\": \"ho\", \"ha\": true}";
+ JsonParser jp = new JsonParser(json);
+ Map<String, Object> m = jp.getParsed();
+ assertEquals(2, m.size());
+ assertEquals("ho", m.get("hi"));
+ assertTrue((Boolean) m.get("ha"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testJsonComplex() {
+ String json = "{\"a\": [1,2,3,4,5], \"b\": {\"x\": 12, \"y\": 42, \"z\": {\"test test\": \"hello hello\"}}}";
+ JsonParser jp = new JsonParser(json);
+ Map<String, Object> m = jp.getParsed();
+ assertEquals(2, m.size());
+ assertEquals(Arrays.asList(1L, 2L, 3L, 4L, 5L), m.get("a"));
+ Map<String, Object> mb = (Map<String, Object>) m.get("b");
+ assertEquals(3, mb.size());
+ assertEquals(12L, mb.get("x"));
+ assertEquals(42L, mb.get("y"));
+ Map<String, Object> mz = (Map<String, Object>) mb.get("z");
+ assertEquals(1, mz.size());
+ assertEquals("hello hello", mz.get("test test"));
+ }
+
+ @Test
+ public void testJsonArray() {
+ String json = "{\"abc\": [\"x\", \"y\", \"z\"]}";
+ JsonParser jp = new JsonParser(json);
+ Map<String, Object> m = jp.getParsed();
+ assertEquals(1, m.size());
+ assertEquals(Arrays.asList("x", "y", "z"), m.get("abc"));
+ }
+}
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonSerializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonSerializationTest.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonSerializationTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/JsonSerializationTest.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,81 @@
+/*
+ * 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.felix.serializer.impl.json;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.felix.serializer.serializer.json.JsonCodecImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JsonSerializationTest {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testComplexMapSerialization() {
+ Map<String, Object> m = new LinkedHashMap<>();
+ m.put("sKey", "a string");
+ m.put("iKey", 42);
+ m.put("bKey", true);
+ m.put("noKey", null);
+ m.put("simpleArray", new int[] {1,2,3});
+
+ Map<String, Object> m1 = new LinkedHashMap<>();
+ m1.put("a", 1L);
+ m1.put("b", "hello");
+ m.put("simpleObject", m1);
+
+ String expected = "{\"sKey\":\"a string\","
+ + "\"iKey\":42,"
+ + "\"bKey\":true,"
+ + "\"noKey\":null,"
+ + "\"simpleArray\":[1,2,3],"
+ + "\"simpleObject\":{\"a\":1,\"b\":\"hello\"}}";
+ assertEquals(expected, new JsonCodecImpl().serialize(m).toString());
+
+ Map<String, Object> dm = new JsonCodecImpl().deserialize(Map.class).from(expected);
+ Map<String, Object> expected2 = new LinkedHashMap<>();
+ expected2.put("sKey", "a string");
+ expected2.put("iKey", 42L);
+ expected2.put("bKey", true);
+ expected2.put("noKey", null);
+ expected2.put("simpleArray", Arrays.asList(1L,2L,3L));
+ expected2.put("simpleObject", m1);
+ assertEquals(expected2, dm);
+ }
+
+ @Test
+ public void testComplexMapSerialization2() {
+ Map<String, Object> m2 = new LinkedHashMap<>();
+ m2.put("yes", Boolean.TRUE);
+ m2.put("no", Collections.singletonMap("maybe", false));
+
+ Map<String, Object> cm = new LinkedHashMap<>();
+ cm.put("list", Arrays.asList(
+ Collections.singletonMap("x", "y"),
+ Collections.singletonMap("x", "b")));
+ cm.put("embedded", m2);
+
+ String expected = "{\"list\":[{\"x\":\"y\"},{\"x\":\"b\"}],"
+ + "\"embedded\":"
+ + "{\"yes\":true,\"no\":{\"maybe\":false}}}";
+ assertEquals(expected, new JsonCodecImpl().serialize(cm).toString());
+ }
+}
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyDTO.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyDTO.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyDTO.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyDTO.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,32 @@
+/*
+ * 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.felix.serializer.impl.json;
+
+import org.osgi.dto.DTO;
+
+public class MyDTO extends DTO {
+ public enum Count { ONE, TWO, THREE }
+
+ public Count count;
+
+ public String ping;
+
+ public long pong;
+
+ public MyEmbeddedDTO embedded;
+}
+
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyEmbeddedDTO.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyEmbeddedDTO.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyEmbeddedDTO.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/json/MyEmbeddedDTO.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,29 @@
+/*
+ * 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.felix.serializer.impl.json;
+
+import org.osgi.dto.DTO;
+
+public class MyEmbeddedDTO extends DTO {
+ public enum Alpha { A, B, C }
+
+ public Alpha alpha;
+
+ public String marco;
+
+ public long polo;
+}
Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/YamlSerializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/YamlSerializationTest.java?rev=1759814&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/YamlSerializationTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/serializer/impl/yaml/YamlSerializationTest.java Thu Sep 8 13:17:46 2016
@@ -0,0 +1,96 @@
+/*
+ * 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.felix.serializer.impl.yaml;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.felix.serializer.impl.yaml.YamlCodecImpl;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class YamlSerializationTest {
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testComplexMapSerialization() {
+ Map<String, Object> m = new LinkedHashMap<>();
+ m.put("sKey", "a string");
+ m.put("iKey", 42);
+ m.put("bKey", true);
+ m.put("noKey", null);
+ m.put("simpleArray", new int[] {1,2,3});
+
+ Map<String, Object> m1 = new LinkedHashMap<>();
+ m1.put("a", 1L);
+ m1.put("b", "hello");
+ m.put("simpleObject", m1);
+
+ String expected = "sKey: 'a string'\n" +
+ "iKey: 42\n" +
+ "bKey: true\n" +
+ "noKey: \n" +
+ "simpleArray: \n" +
+ " - 1\n" +
+ " - 2\n" +
+ " - 3\n" +
+ "simpleObject: \n" +
+ " a: 1\n" +
+ " b: 'hello'";
+ assertEquals(expected, new YamlCodecImpl().serialize(m).toString().trim());
+
+ Map<String, Object> dm = new YamlCodecImpl().deserialize(Map.class).from(expected);
+ Map<String, Object> expected2 = new LinkedHashMap<>();
+ expected2.put("sKey", "a string");
+ expected2.put("iKey", 42);
+ expected2.put("bKey", true);
+ expected2.put("noKey", null);
+ expected2.put("simpleArray", Arrays.asList(1,2,3));
+
+ Map<String, Object> m2 = new LinkedHashMap<>();
+ m2.put("a", 1);
+ m2.put("b", "hello");
+ expected2.put("simpleObject", m2);
+ assertEquals(expected2, dm);
+ }
+
+ @Test
+ public void testComplexMapSerialization2() {
+ Map<String, Object> m2 = new LinkedHashMap<>();
+ m2.put("yes", Boolean.TRUE);
+ m2.put("no", Collections.singletonMap("maybe", false));
+
+ Map<String, Object> cm = new LinkedHashMap<>();
+ cm.put("list", Arrays.asList(
+ Collections.singletonMap("x", "y"),
+ Collections.singletonMap("x", "b")));
+ cm.put("embedded", m2);
+
+ String expected = "list: \n" +
+ " - \n" +
+ " x: 'y'\n" +
+ " - \n" +
+ " x: 'b'\n" +
+ "embedded: \n" +
+ " yes: true\n" +
+ " no: \n" +
+ " maybe: false";
+ assertEquals(expected, new YamlCodecImpl().serialize(cm).toString().trim());
+ }
+}