/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.felix.serializer.impl.yaml; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.osgi.service.serializer.Serializing; import org.osgi.util.converter.ConversionException; import org.osgi.util.converter.Converter; public class YamlSerializingImpl implements Serializing { private volatile Converter converter; private final Map<String, Object> configuration; private final Object object; private final int indentation = 2; public YamlSerializingImpl(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).trim(); } @SuppressWarnings("rawtypes") private String encode(Object obj, int level) { if (obj == null) return ""; if (obj instanceof Map) { return encodeMap((Map) obj, level); } else if (obj instanceof Collection) { return encodeCollection((Collection) obj, level); } else if (obj.getClass().isArray()) { return encodeCollection(asCollection(obj), level); } else if (obj instanceof Number) { return obj.toString(); } else if (obj instanceof Boolean) { return obj.toString(); } return "'" + converter.convert(obj).to(String.class) + "'"; } private Collection<?> asCollection(Object arr) { // Arrays.asList() doesn't work for primitive arrays int len = Array.getLength(arr); List<Object> l = new ArrayList<>(len); for (int i=0; i<len; i++) { l.add(Array.get(arr, i)); } return l; } private String encodeCollection(Collection<?> collection, int level) { StringBuilder sb = new StringBuilder(); for (Object o : collection) { sb.append("\n"); sb.append(getIdentPrefix(level)); sb.append("- "); sb.append(encode(o, level + 1)); } return sb.toString(); } @SuppressWarnings({ "rawtypes", "unchecked" }) private String encodeMap(Map m, int level) { StringBuilder sb = new StringBuilder(); for (Entry entry : (Set<Entry>) m.entrySet()) { sb.append("\n"); sb.append(getIdentPrefix(level)); sb.append(entry.getKey().toString()); sb.append(": "); sb.append(encode(entry.getValue(), level + 1)); } return sb.toString(); } private String getIdentPrefix(int level) { int numSpaces = indentation * level; StringBuilder sb = new StringBuilder(numSpaces); for (int i=0; i < numSpaces; i++) sb.append(' '); return sb.toString(); } @Override public Serializing pretty() { return this; } @Override public Serializing ignoreNull() { return this; } @Override public Serializing with(Converter c) { converter = c; return this; } }