Author: davidb Date: Tue Apr 5 12:51:42 2016 New Revision: 1737831 URL: http://svn.apache.org/viewvc?rev=1737831&view=rev Log: Very initial version of the Converter Service.
The API comes from OSGi RFC 215 and is ASL-2 licensed. It's included here as the API is not yet release by OSGi. The implementation of this service is utterly incomplete. This is just a very small starting point written by me which can be built out in the Felix codebase. Added: felix/trunk/converter/ felix/trunk/converter/pom.xml felix/trunk/converter/src/ felix/trunk/converter/src/main/ felix/trunk/converter/src/main/java/ felix/trunk/converter/src/main/java/org/ felix/trunk/converter/src/main/java/org/apache/ felix/trunk/converter/src/main/java/org/apache/felix/ felix/trunk/converter/src/main/java/org/apache/felix/converter/ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterImpl.java felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonCodecImpl.java felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonDecodingImpl.java felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonEncodingImpl.java felix/trunk/converter/src/main/java/org/osgi/ felix/trunk/converter/src/main/java/org/osgi/service/ felix/trunk/converter/src/main/java/org/osgi/service/converter/ felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Codec.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Converter.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Converting.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Decoding.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Encoding.java felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java felix/trunk/converter/src/main/java/org/osgi/service/converter/TypeReference.java felix/trunk/converter/src/main/java/org/osgi/service/converter/package-info.java felix/trunk/converter/src/test/ felix/trunk/converter/src/test/java/ felix/trunk/converter/src/test/java/org/ felix/trunk/converter/src/test/java/org/apache/ felix/trunk/converter/src/test/java/org/apache/felix/ felix/trunk/converter/src/test/java/org/apache/felix/converter/ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java Added: felix/trunk/converter/pom.xml URL: http://svn.apache.org/viewvc/felix/trunk/converter/pom.xml?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/pom.xml (added) +++ felix/trunk/converter/pom.xml Tue Apr 5 12:51:42 2016 @@ -0,0 +1,94 @@ +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.felix</groupId> + <artifactId>felix-parent</artifactId> + <version>4</version> + <relativePath>../pom/pom.xml</relativePath> + </parent> + + <name>Apache Felix Converter Service</name> + <artifactId>org.apache.felix.converter</artifactId> + <version>0.1-SNAPSHOT</version> + <packaging>jar</packaging> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/converter</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/converter</developerConnection> + <url>http://svn.apache.org/viewvc/felix/trunk/converter/</url> + </scm> + + <properties> + <felix.java.version>8</felix.java.version> + <felix.java.signature.artifactId>java18</felix.java.signature.artifactId> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>2.5.3</version> + <executions> + <execution> + <id>bundle</id> + <phase>package</phase> + <goals> + <goal>bundle</goal> + </goals> + </execution> + <execution> + <id>baseline</id> + <goals> + <goal>baseline</goal> + </goals> + </execution> + </executions> + <configuration> + <instructions> + <Private-Package> + org.apache.felix.converter.* + </Private-Package> + <Export-Package>org.osgi.service.converter</Export-Package> + <Import-Package> + * + </Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>osgi.annotation</artifactId> + <version>6.0.1</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> Added: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/AdapterImpl.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,134 @@ +/* + * 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.converter.impl; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import org.osgi.service.converter.Adapter; +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Converting; +import org.osgi.service.converter.Rule; +import org.osgi.service.converter.TypeReference; + +public class AdapterImpl implements Adapter { + private final Converter delegate; + private final Map<ClassPair, Function<Object, Object>> classRules = + new ConcurrentHashMap<>(); + + public AdapterImpl(Converter converter) { + this.delegate = converter; + } + + @Override + public Converting convert(Object obj) { + Converting converting = delegate.convert(obj); + return new ConvertingWrapper(obj, converting); + } + + @Override + public Adapter getAdapter() { + return new AdapterImpl(this); + } + + @SuppressWarnings("unchecked") + @Override + public <F, T> Adapter rule(Class<F> fromCls, Class<T> toCls, + Function<F, T> toFun, Function<T, F> fromFun) { + if (fromCls.equals(toCls)) + throw new IllegalArgumentException(); + + classRules.put(new ClassPair(fromCls, toCls), (Function<Object, Object>) toFun); + classRules.put(new ClassPair(toCls, fromCls), (Function<Object, Object>) fromFun); + return this; + } + + + @Override + public <F, T> Adapter rule(Rule<F, T> rule) { + // TODO Auto-generated method stub + return null; + } + + @Override + public <F, T> Adapter rule(Function<F, T> toFun, Function<T, F> fromFun) { + // TODO Auto-generated method stub + return this; + } + + private class ConvertingWrapper implements Converting { + private final Converting del; + private final Object object; + + ConvertingWrapper(Object obj, Converting c) { + object = obj; + del = c; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T to(Class<T> cls) { + Function<Object, Object> f = classRules.get(new ClassPair(object.getClass(), cls)); + if (f != null) + return (T) f.apply(object); + + return del.to(cls); + } + + @Override + public <T> T to(TypeReference<T> ref) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object to(Type type) { + // TODO Auto-generated method stub + return null; + } + } + + static class ClassPair { + private final Class<?> from; + private final Class<?> to; + + ClassPair(Class<?> from, Class<?> to) { + this.from = from; + this.to = to; + } + + @Override + public int hashCode() { + return Objects.hash(from, to); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof ClassPair)) + return false; + + ClassPair o = (ClassPair) obj; + return Objects.equals(from, o.from) && + Objects.equals(to, o.to); + } + } +} Added: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConverterImpl.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,33 @@ +/* + * 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.converter.impl; + +import org.osgi.service.converter.Adapter; +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Converting; + +public class ConverterImpl implements Converter { + @Override + public Adapter getAdapter() { + return new AdapterImpl(this); + } + + @Override + public Converting convert(Object obj) { + return new ConvertingImpl(this, obj); + } +} Added: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/ConvertingImpl.java Tue Apr 5 12:51:42 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.converter.impl; + +import java.lang.reflect.Method; +import java.lang.reflect.Type; + +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Converting; +import org.osgi.service.converter.TypeReference; + +public class ConvertingImpl implements Converting { + private Converter converter; + private final Object object; + + ConvertingImpl(Converter c, Object obj) { + converter = c; + object = obj; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T to(Class<T> cls) { + if (String.class.equals(cls)) { + if (object instanceof Object[]) + return (T) ((Object[])object)[0]; + return (T) object.toString(); + } else if (String[].class.equals(cls)) { + String[] res = new String[1]; + res[0] = object.toString(); + return (T) res; + } + + T res = tryStandardMethods(cls); + + if (res != null) { + return res; + } else { + return null; + } + } + + @Override + public <T> T to(TypeReference<T> ref) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Object to(Type type) { + // TODO Auto-generated method stub + return null; + } + + @SuppressWarnings("unchecked") + private <T> T tryStandardMethods(Class<T> cls) { + try { + Method m = cls.getDeclaredMethod("valueOf", String.class); + if (m != null) { + return (T) m.invoke(null, object); + } + } catch (Exception e) { + return null; + } + return null; + } +} Added: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonCodecImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonCodecImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonCodecImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonCodecImpl.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.felix.converter.impl; + +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.Codec; +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Decoding; +import org.osgi.service.converter.Encoding; +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(); // TODO inject? + + @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); + + 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) throws IOException { + os.write(toString().getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String toString() { + try { + return prefix + delegate.toString() + postfix; + } finally { + threadLocal.set(null); + } + } + + @Override + public Encoding pretty() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void to(OutputStream out, Charset charset) throws IOException { + // 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/src/main/java/org/apache/felix/converter/impl/JsonDecodingImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonDecodingImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonDecodingImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonDecodingImpl.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,149 @@ +/* + * 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.converter.impl; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Decoding; + +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 + public T from(CharSequence in) { + if (Map.class.isAssignableFrom(clazz)) { + return createMapFromJSONString(in); + } + return deserializeSingleJSONValue(clazz, in); + } + + private T createMapFromJSONString(CharSequence in) { + Map m = new HashMap(); + String s = in.toString().trim(); + if (!s.startsWith("{") || !s.endsWith("}")) + throw new IllegalArgumentException("JSON Should start and end with '{' and '}': " + s); + + // Eat braces + s = s.substring(1, s.length() - 1); + + int commaIdx = -1; + do { + int colonIdx = s.indexOf(':'); + if (colonIdx <= 0) + throw new IllegalArgumentException("JSON Should contain key-value pairs: " + s); + + String key = s.substring(0, colonIdx).trim(); + if (!key.startsWith("\"") || !key.endsWith("\"")) + throw new IllegalArgumentException("JSON key should be double-quoted: " + s); + key = key.substring(1, key.length() - 1); + + // move to after ':' + s = s.substring(colonIdx + 1); + + commaIdx = getNextComma(s); + String val; + if (commaIdx > 0) { + val = s.substring(0, commaIdx); + + // move to after ',' + s = s.substring(commaIdx + 1); + } else { + val = s; + } + + + val = val.trim(); + Object parsed; + if (val.startsWith("{")) { + parsed = new JsonCodecImpl().decode(Map.class).from(val); + } else { + if ("null".equals(val)) + parsed = null; + else if ("true".equals(val)) + parsed = true; + else if ("false".equals(val)) + parsed = false; + else if (val.startsWith("\"")) + parsed = val; + else if (val.contains(".")) + parsed = Double.valueOf(val); + else + parsed = Integer.valueOf(val); + } + m.put(key, parsed); + } while (commaIdx > 0); + + return (T) m; + } + + private int getNextComma(String s) { + int bracelevel = 0; + for (int i=0; i<s.length(); i++) { + switch(s.charAt(i)) { + case '{': bracelevel++; + break; + case '}': bracelevel--; + break; + case ',': if (bracelevel == 0) return i; + break; + } + } + return -1; + } + + @SuppressWarnings("unchecked") + private <T> T deserializeSingleJSONValue(Class<T> cls, CharSequence cs) { + try { + Method m = cls.getDeclaredMethod("valueOf", String.class); + if (m != null) { + return (T) m.invoke(null, cs); + } + } catch (Exception e) { + return null; + } + return null; + } + + @Override + public T from(InputStream in) { + // TODO Auto-generated method stub + return null; + } + + @Override + public T from(InputStream in, Charset charset) { + // TODO Auto-generated method stub + return null; + } + + @Override + public T from(Readable in) { + // TODO Auto-generated method stub + return null; + } +} Added: felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonEncodingImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonEncodingImpl.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonEncodingImpl.java (added) +++ felix/trunk/converter/src/main/java/org/apache/felix/converter/impl/JsonEncodingImpl.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.felix.converter.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.osgi.service.converter.Converter; +import org.osgi.service.converter.Encoding; + +public class JsonEncodingImpl implements Encoding { + private final Converter converter; + private final Map<String, Object> configuration; + private final Object object; + + JsonEncodingImpl(Converter c, Map<String, Object> cfg, Object obj) { + converter = c; + configuration = cfg; + object = obj; + } + + private boolean ignoreNull() { + return Boolean.TRUE.equals(Boolean.parseBoolean((String) configuration.get("ignoreNull"))); + } + + @Override + public void to(OutputStream os) throws IOException { + os.write(encode(object).getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String toString() { + return encode(object); + } + + public String encode(Object obj) { + if (obj == null) { + return ignoreNull() ? "" : "null"; + } + + if (obj instanceof Map) { + return encodeMap((Map) 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 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 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; + } +} Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Adapter.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,73 @@ +/* + * 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.converter; + +import java.util.function.Function; + +/** + * An {@link Adapter} is used to modify the behaviour of the Converter service, + * which can be useful when some of the conversions should be done different to + * the Converter Specification. + * + * @author $Id:$ + */ +public interface Adapter extends Converter { + /** + * Specify a conversion rule by providing a rule object. + * + * @param rule The conversion rule. + * @return The current adapter, can be used to chain invocations. + */ + <F, T> Adapter rule(Rule<F, T> rule); + + /** + * Specify a rule for the conversion to and from two classes. The rule + * specifies the conversion in both directions. This overload makes it easy + * to provide the conversions as lambdas, for example: + * + * <pre> + * adapter.rule(String[].class, String.class, + * v -> Stream.of(v).collect(Collectors.joining(",")), + * v -> v.split(",")); + * </pre> + * + * @param <F> the type to convert from. + * @param <T> the type to convert to. + * @param fromCls the class to convert from. + * @param toCls the class to convert to. + * @param toFun the function to perform the conversion. + * @param fromFun the function to perform the reverse conversion. + * @return The current adapter, can be used to chain invocations. + */ + <F, T> Adapter rule(Class<F> fromCls, Class<T> toCls, + Function<F, T> toFun, Function<T, F> fromFun); + + /** + * Specify a rule for the conversion to and from two classes. The rule + * specifies the conversion in both directions. This overload makes it easy + * to provide the conversions as method references. + * + * @param <F> the type to convert from. + * @param <T> the type to convert to. + * @param toFun the function to perform the conversion. + * @param fromFun the function to perform the reverse conversion. + * @return The current adapter, can be used to chain invocations. + */ + <F, T> Adapter rule(Function<F, T> toFun, Function<T, F> fromFun); + +// <F, T> Adapter rule(Function<TypeReference<F>, TypeReference<T>> toFun, +// Function<TypeReference<T>,TypeReference<F>> fromFun); +} Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/Codec.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Codec.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Codec.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Codec.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,76 @@ +/* + * 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.converter; + +import java.lang.reflect.Type; + +/** + * 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:$ + */ +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 opertation. + * + * @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/src/main/java/org/osgi/service/converter/Converter.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Converter.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Converter.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Converter.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,42 @@ +/* + * 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.converter; + +/** + * The Converter service is used to start a conversion. The service is obtained + * from the service registry. The conversion is then completed via the + * Converting interface that has methods to specify the target type. + * + * @author $Id:$ + */ +public interface Converter { + /** + * Start a conversion for the given object. + * + * @param obj The object that should be converted. + * @return A {@link Converting} object to complete the conversion. + */ + Converting convert(Object obj); + + /** + * Obtain an adapter to this converter. The adapter behaves just like the + * converter except for the exception rules registered with is. For more + * details see the {@link Adapter} interface. + * + * @return An adapter to this converter. + */ + Adapter getAdapter(); +} Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/Converting.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Converting.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Converting.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Converting.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,61 @@ +/* + * 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.converter; + +import java.lang.reflect.Type; + +/** + * This interface is used to specify the target that an object should be + * converted to. A {@link Converting} instance can be obtained via the + * {@link Converter} service by starting a conversion for a specific object. + * + * @author $Id:$ + */ +public interface Converting { + /** + * Specify the target object type for the conversion as a class object. + * + * @param cls The class to convert to. + * @return The converted object. + */ + <T> T to(Class<T> cls); + + /** + * Specify the target object type as a {@link TypeReference}. If the target + * class carries generics information a TypeReference should be used as this + * preserves the generic information whereas a Class object has this + * information erased. Example use: + * + * <pre> + * List<String> result = + * converter.convert(Arrays.asList(1,2,3)). + * to(new TypeReference<List<String>>() {}); + * </pre> + * + * @param ref A type reference to the object being converted to. + * @return The converted object. + */ + <T> T to(TypeReference<T> ref); + + /** + * Specify the target object type as a Java Refelection Type object. + * + * @param type A Type object to represent the target type to be converted + * to. + * @return The converted object. + */ + Object to(Type type); +} Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/Decoding.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Decoding.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Decoding.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Decoding.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,61 @@ +/* + * 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.converter; + +import java.io.InputStream; +import java.nio.charset.Charset; + +/** + * Interface to specify the source of the decoding operation + * + * @param <T> The target type for the decoding operation. + * @author $Id:$ + */ +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/src/main/java/org/osgi/service/converter/Encoding.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Encoding.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Encoding.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Encoding.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,69 @@ +/* + * 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.converter; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** + * Interface to specify the target of the encoding operation. + * + * @author $Id:$ + */ +public interface Encoding { + /** + * 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. + * + * @param out The output stream to use. + */ + 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. + */ + 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/src/main/java/org/osgi/service/converter/Rule.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/Rule.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,60 @@ +/* + * 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.converter; + +import java.util.function.Function; + +/** + * A rule is a data entity can hold all the information needed to specify a + * custom conversion for use by an @{link Adapter}. + * + * @param <F> The type to convert from. + * @param <T> The type to convert to. + * @author $Id:$ + */ +public class Rule<F, T> { + private final Function<F, T> toFun; + private final Function<T, F> fromFun; + + /** + * Specify the functions to do the conversions in both directions. + * + * @param to The function that performs the conversion. + * @param from The function that performs the reverse conversion. + */ + public Rule(Function<F, T> to, Function<T, F> from) { + toFun = to; + fromFun = from; + } + + /** + * Obtain the conversion function. + * + * @return The conversion function. + */ + public Function<F, T> getToFunction() { + return toFun; + } + + /** + * Obtain the reverse conversion function. + * + * @return The reverse conversion function. + */ + public Function<T, F> getFromFunction() { + return fromFun; + } +} Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/TypeReference.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/TypeReference.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/TypeReference.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/TypeReference.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,54 @@ +/* + * Copyright (c) OSGi Alliance (2015, 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.converter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * An object does not carry any runtime information about its generic type. + * However sometimes it is necessary to specify a generic type, that is the + * purpose of this class. It allows you to specify an generic type by defining a + * type T, then subclassing it. The subclass will have a reference to the super + * class that contains this generic information. Through reflection, we pick + * this reference up and return it with the getType() call. + * + * <pre> + * List<String> result = + * converter.convert(Arrays.asList(1,2,3)). + * to(new TypeReference<List<String>>() {}); + * </pre> + * + * @param <T> The target type for the conversion. + * @author $Id:$ + */ +public class TypeReference<T> { + /** + * A {@link TypeReference} cannot be directly instantiated. To use it it has + * to be extended, typically as an anonymous inner class. + */ + protected TypeReference() { + } + + /** + * Return the actual type of this Type Reference + * + * @return the type of this reference. + */ + public Type getType() { + return ((ParameterizedType) getClass(). + getGenericSuperclass()).getActualTypeArguments()[0]; + } +} \ No newline at end of file Added: felix/trunk/converter/src/main/java/org/osgi/service/converter/package-info.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/main/java/org/osgi/service/converter/package-info.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/main/java/org/osgi/service/converter/package-info.java (added) +++ felix/trunk/converter/src/main/java/org/osgi/service/converter/package-info.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,24 @@ +/* + * 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. + */ + +/** + * Converter Package. + * + * @author $Id:$ + */ +@org.osgi.annotation.versioning.Version("1.0") +package org.osgi.service.converter; + Added: felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java URL: http://svn.apache.org/viewvc/felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java?rev=1737831&view=auto ============================================================================== --- felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java (added) +++ felix/trunk/converter/src/test/java/org/apache/felix/converter/impl/ConverterTest.java Tue Apr 5 12:51:42 2016 @@ -0,0 +1,31 @@ +/* + * 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.converter.impl; + +import org.junit.Test; +import org.osgi.service.converter.Converter; + +import static org.junit.Assert.assertEquals; + +public class ConverterTest { + @Test + public void testConverter() { + Converter c = new ConverterImpl(); + int i = c.convert("123").to(Integer.class); + assertEquals(123, i); + } +}