felix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject svn commit: r1757746 - in /felix/trunk/converter/codec: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/felix/ src/main/java/org/apache/felix/codec/ src/main/java/org/apache/felix/codec/impl/ src/m...
Date Thu, 25 Aug 2016 18:34:31 GMT
Author: davidb
Date: Thu Aug 25 18:34:30 2016
New Revision: 1757746

URL: http://svn.apache.org/viewvc?rev=1757746&view=rev
Log:
Felix Convert - separated out codecs.

Added:
    felix/trunk/converter/codec/src/
    felix/trunk/converter/codec/src/main/
    felix/trunk/converter/codec/src/main/java/
    felix/trunk/converter/codec/src/main/java/org/
    felix/trunk/converter/codec/src/main/java/org/apache/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/Activator.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonCodecImpl.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonDecodingImpl.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonEncodingImpl.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonParser.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlCodecImpl.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlDecodingImpl.java
    felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlEncodingImpl.java
    felix/trunk/converter/codec/src/main/java/org/osgi/
    felix/trunk/converter/codec/src/main/java/org/osgi/service/
    felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/
    felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Codec.java
    felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Decoding.java
    felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Encoding.java
    felix/trunk/converter/codec/src/test/
    felix/trunk/converter/codec/src/test/java/
    felix/trunk/converter/codec/src/test/java/org/
    felix/trunk/converter/codec/src/test/java/org/apache/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonCodecTest.java
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonParserTest.java
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonSerializationTest.java
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyDTO.java
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyEmbeddedDTO.java
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/yaml/
    felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/yaml/YamlSerializationTest.java
Modified:
    felix/trunk/converter/codec/pom.xml

Modified: felix/trunk/converter/codec/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/pom.xml?rev=1757746&r1=1757745&r2=1757746&view=diff
==============================================================================
--- felix/trunk/converter/codec/pom.xml (original)
+++ felix/trunk/converter/codec/pom.xml Thu Aug 25 18:34:30 2016
@@ -65,39 +65,24 @@
                 </executions>
                 <configuration>
                     <instructions>
-                        <Bundle-Activator>org.apache.felix.converter.impl.Activator</Bundle-Activator>
-                        <Private-Package>org.apache.felix.converter.*,org.yaml.snakeyaml.*</Private-Package>
-                        <Export-Package>org.osgi.service.converter, org.osgi.service.converter.util</Export-Package>
+                        <Bundle-Activator>org.apache.felix.codec.impl.Activator</Bundle-Activator>
+                        <Private-Package>org.apache.felix.codec.*,org.yaml.snakeyaml.*</Private-Package>
+                        <Export-Package>org.osgi.service.codec</Export-Package>
                         <Import-Package>*</Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
-            <plugin>
-                <groupId>org.apache.rat</groupId>
-                <artifactId>apache-rat-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <includes>
-                        <include>src/**</include>
-                    </includes>
-                    <excludes>
-                        <exclude>src/main/resources/META-INF/services/org.osgi.service.converter.Converter</exclude>
-                    </excludes>
-                </configuration>
-            </plugin>
-            
         </plugins>
     </build>
 
     <dependencies>
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.converter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    
+        <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.annotation</artifactId>
             <version>6.0.1</version>

Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/Activator.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/Activator.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/Activator.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/Activator.java Thu Aug 25 18:34:30 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.codec.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.codec.impl.json.JsonCodecImpl;
+import org.apache.felix.codec.impl.yaml.YamlCodecImpl;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.codec.Codec;
+
+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(Codec.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(Codec.class, new YamlCodecImpl(), yamlProps);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+    }
+}

Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonCodecImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonCodecImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonCodecImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonCodecImpl.java Thu Aug 25 18:34:30 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.codec.impl.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.apache.felix.converter.impl.ConverterImpl;
+import org.osgi.service.codec.Codec;
+import org.osgi.service.codec.Decoding;
+import org.osgi.service.codec.Encoding;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+
+public class JsonCodecImpl implements Codec {
+    private Map<String, Object> configuration = new ConcurrentHashMap<>();
+    private ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
+    private Converter converter = new ConverterImpl();
+
+    @Override
+    public Codec with(Converter c) {
+        converter = c;
+        return this;
+    }
+
+    @Override
+    public <T> Decoding<T> decode(Class<T> cls) {
+        return new JsonDecodingImpl<T>(converter, cls);
+    }
+
+    @Override
+    public Encoding encode(Object obj) {
+        Encoding 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 Encoding {
+        private final Encoding delegate;
+        private String prefix;
+        private String postfix;
+
+        EncodingWrapper(String pre, Encoding 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 Encoding ignoreNull() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        @Override
+        public Encoding 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> Decoding<T> decode(TypeReference<T> ref) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Decoding<?> decode(Type type) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonDecodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonDecodingImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonDecodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonDecodingImpl.java Thu Aug 25 18:34:30 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.codec.impl.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.codec.Decoding;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+
+public class JsonDecodingImpl<T> implements Decoding<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/codec/impl/json/JsonEncodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonEncodingImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonEncodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonEncodingImpl.java Thu Aug 25 18:34:30 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.codec.impl.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.codec.Encoding;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+
+public class JsonEncodingImpl implements Encoding {
+    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 Encoding ignoreNull() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Encoding pretty() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}

Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonParser.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonParser.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/json/JsonParser.java Thu Aug 25 18:34:30 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.codec.impl.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&lt;String, Object&gt;}.
+ * <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/apache/felix/codec/impl/yaml/YamlCodecImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlCodecImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlCodecImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlCodecImpl.java Thu Aug 25 18:34:30 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.codec.impl.yaml;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.converter.impl.ConverterImpl;
+import org.osgi.service.codec.Codec;
+import org.osgi.service.codec.Decoding;
+import org.osgi.service.codec.Encoding;
+import org.osgi.service.converter.Converter;
+import org.osgi.service.converter.TypeReference;
+
+public class YamlCodecImpl implements Codec {
+    private Map<String, Object> configuration = new ConcurrentHashMap<>();
+    private Converter converter = new ConverterImpl();
+
+    @Override
+    public Codec with(Converter c) {
+        converter = c;
+        return this;
+    }
+
+    @Override
+    public <T> Decoding<T> decode(Class<T> cls) {
+        return new YamlDecodingImpl<T>(converter, cls);
+    }
+
+    @Override
+    public <T> Decoding<T> decode(TypeReference<T> ref) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Decoding<?> decode(Type type) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Encoding encode(Object obj) {
+        return new YamlEncodingImpl(converter, configuration, obj);
+    }
+}

Added: felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlDecodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlDecodingImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlDecodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlDecodingImpl.java Thu Aug 25 18:34:30 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.codec.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.codec.Decoding;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+import org.yaml.snakeyaml.Yaml;
+
+public class YamlDecodingImpl<T> implements Decoding<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/codec/impl/yaml/YamlEncodingImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlEncodingImpl.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlEncodingImpl.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/apache/felix/codec/impl/yaml/YamlEncodingImpl.java Thu Aug 25 18:34:30 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.codec.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.codec.Encoding;
+import org.osgi.service.converter.ConversionException;
+import org.osgi.service.converter.Converter;
+
+public class YamlEncodingImpl implements Encoding {
+    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 Encoding pretty() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Encoding ignoreNull() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}

Added: felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Codec.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Codec.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Codec.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Codec.java Thu Aug 25 18:34:30 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.codec;
+
+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 Codec {
+	/**
+	 * Start specifying a decode operation.
+	 *
+	 * @param <T> The type to decode to.
+	 * @param cls The class to decode to.
+	 * @return A {@link Decoding} object to specify the source for the decode
+	 *         operation.
+	 */
+	<T> Decoding<T> decode(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 Decoding} object to specify the source for the decode
+	 *         operation.
+	 */
+	<T> Decoding<T> decode(TypeReference<T> ref);
+
+	/**
+	 * Start specifying a decode operation.
+	 *
+	 * @param type The type to convert to.
+	 * @return A {@link Decoding} object to specify the source for the decode
+	 *         operation.
+	 */
+	Decoding< ? > decode(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.
+	 */
+	Encoding encode(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.
+	 */
+	Codec with(Converter converter);
+}

Added: felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Decoding.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Decoding.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Decoding.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Decoding.java Thu Aug 25 18:34:30 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.codec;
+
+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 Decoding<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/codec/Encoding.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Encoding.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Encoding.java (added)
+++ felix/trunk/converter/codec/src/main/java/org/osgi/service/codec/Encoding.java Thu Aug 25 18:34:30 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.codec;
+
+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 Encoding {
+	/**
+	 * 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.
+	 */
+	Encoding 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.
+	 */
+	Encoding 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/codec/impl/json/JsonCodecTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonCodecTest.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonCodecTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonCodecTest.java Thu Aug 25 18:34:30 2016
@@ -0,0 +1,144 @@
+/*
+ * 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.codec.impl.json;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.codec.impl.json.MyDTO.Count;
+import org.apache.felix.codec.impl.json.MyEmbeddedDTO.Alpha;
+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.Converter;
+import org.osgi.service.converter.util.ConverterFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class JsonCodecTest {
+    private Converter converter;
+
+    @Before
+    public void setUp() {
+        converter = ConverterFactory.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.encode(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.decode(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.getAdapter();
+        ca.rule(Foo.class, String.class, Foo::tsFun, v -> Foo.fsFun(v));
+
+        JsonCodecImpl jsonCodec = new JsonCodecImpl();
+        String json = jsonCodec.with(ca).encode(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.encode(dto).toString();
+        assertEquals(
+            "{\"ping\":\"'\",\"count\":\"ONE\",\"pong\":-9223372036854775808,"
+            + "\"embedded\":{\"polo\":327,\"alpha\":\"B\",\"marco\":\"jo !\"}}",
+            json);
+
+        MyDTO dto2 = jsonCodec.decode(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/codec/impl/json/JsonParserTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonParserTest.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonParserTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonParserTest.java Thu Aug 25 18:34:30 2016
@@ -0,0 +1,63 @@
+/*
+ * 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.codec.impl.json;
+
+import java.util.Arrays;
+import java.util.Map;
+
+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/codec/impl/json/JsonSerializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonSerializationTest.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonSerializationTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/JsonSerializationTest.java Thu Aug 25 18:34:30 2016
@@ -0,0 +1,80 @@
+/*
+ * 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.codec.impl.json;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+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().encode(m).toString());
+
+        Map<String, Object> dm = new JsonCodecImpl().decode(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().encode(cm).toString());
+    }
+}

Added: felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyDTO.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyDTO.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyDTO.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyDTO.java Thu Aug 25 18:34:30 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.codec.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/codec/impl/json/MyEmbeddedDTO.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyEmbeddedDTO.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyEmbeddedDTO.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/json/MyEmbeddedDTO.java Thu Aug 25 18:34:30 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.codec.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/codec/impl/yaml/YamlSerializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/yaml/YamlSerializationTest.java?rev=1757746&view=auto
==============================================================================
--- felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/yaml/YamlSerializationTest.java (added)
+++ felix/trunk/converter/codec/src/test/java/org/apache/felix/codec/impl/yaml/YamlSerializationTest.java Thu Aug 25 18:34:30 2016
@@ -0,0 +1,95 @@
+/*
+ * 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.codec.impl.yaml;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+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().encode(m).toString().trim());
+
+        Map<String, Object> dm = new YamlCodecImpl().decode(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().encode(cm).toString().trim());
+    }
+}



Mime
View raw message