| /* |
| * 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.sling.feature.io; |
| |
| import java.io.Writer; |
| import java.lang.reflect.Array; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| |
| import javax.json.Json; |
| import javax.json.stream.JsonGenerator; |
| import javax.json.stream.JsonGeneratorFactory; |
| |
| import org.apache.sling.feature.Configuration; |
| |
| /** |
| * Helper class to write JSON structures as defined in |
| * <a href="https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html#d0e131765">OSGi Configurator Specification 1.0</a>. |
| * |
| */ |
| public class ConfiguratorUtil { |
| |
| private ConfiguratorUtil() { |
| } |
| |
| protected static final JsonGenerator newGenerator(final Writer writer) { |
| JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true)); |
| |
| // prevent closing of the underlying writer |
| Writer closeShieldWriter = new CloseShieldWriter(writer); |
| return generatorFactory.createGenerator(closeShieldWriter); |
| } |
| |
| /** Write the OSGi configuration to a JSON structure. |
| * The writer is not closed. |
| * |
| * @param writer Writer |
| * @param props The configuration properties to write */ |
| public static void writeConfiguration(final Writer writer, final Dictionary<String, Object> props) { |
| try (JsonGenerator generator = newGenerator(writer)) { |
| generator.writeStartObject(); |
| writeConfiguration(generator, props); |
| generator.writeEnd(); |
| } |
| } |
| |
| public static void writeConfiguration(final JsonGenerator generator, final Dictionary<String, Object> props) { |
| final Enumeration<String> e = props.keys(); |
| while (e.hasMoreElements()) { |
| final String name = e.nextElement(); |
| if (Configuration.PROP_ARTIFACT_ID.equals(name)) { |
| continue; |
| } |
| final Object val = props.get(name); |
| writeConfigurationProperty(generator, name, val); |
| } |
| } |
| |
| private static void writeConfigurationProperty(JsonGenerator generator, String name, Object val) { |
| String dataType = getDataType(val); |
| writeConfigurationProperty(generator, name, dataType, val); |
| } |
| |
| private static void writeConfigurationProperty(JsonGenerator generator, String name, String dataType, Object val) { |
| String nameWithDataPostFix = name; |
| if (dataType != null) { |
| nameWithDataPostFix += ":" + dataType; |
| } |
| if (val.getClass().isArray()) { |
| generator.writeStartArray(nameWithDataPostFix); |
| for (int i = 0; i < Array.getLength(val); i++) { |
| writeArrayItem(generator, Array.get(val, i)); |
| } |
| generator.writeEnd(); |
| } else if (val instanceof Collection) { |
| generator.writeStartArray(nameWithDataPostFix); |
| for (Object item : Collection.class.cast(val)) { |
| writeArrayItem(generator, item); |
| } |
| generator.writeEnd(); |
| } else { |
| writeNameValuePair(generator, nameWithDataPostFix, val); |
| } |
| } |
| |
| private static void writeNameValuePair(JsonGenerator generator, String name, Object item) { |
| if (item instanceof Boolean) { |
| generator.write(name, (Boolean) item); |
| } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) { |
| generator.write(name, ((Number)item).longValue()); |
| } else if (item instanceof Double) { |
| generator.write(name, (Double) item); |
| } else if (item instanceof Float) { |
| generator.write(name, (Float) item); |
| } else { |
| generator.write(name, item.toString()); |
| } |
| } |
| |
| private static void writeArrayItem(JsonGenerator generator, Object item) { |
| if (item instanceof Boolean) { |
| generator.write((Boolean) item); |
| } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) { |
| generator.write(((Number)item).longValue()); |
| } else if (item instanceof Double) { |
| generator.write((Double) item); |
| } else if (item instanceof Float) { |
| generator.write((Float) item); |
| } else { |
| generator.write(item.toString()); |
| } |
| } |
| |
| private static String getDataType(Object object) { |
| if (object instanceof Collection) { |
| // check class of first item |
| Iterator<?> it = ((Collection<?>) object).iterator(); |
| if (it.hasNext()) { |
| Class<?> itemClass = it.next().getClass(); |
| return "Collection<" + getDataType(itemClass, false) + ">"; |
| } else { |
| throw new IllegalStateException("Empty collections are invalid"); |
| } |
| } else { |
| return getDataType(object.getClass(), true); |
| } |
| } |
| |
| private static String getDataType(Class<?> clazz, boolean allowEmpty) { |
| if (clazz.isArray()) { |
| String dataType = getDataType(clazz.getComponentType(), false); |
| if (dataType != null) { |
| return dataType + "[]"; |
| } else { |
| return null; |
| } |
| } |
| // default classes used by native JSON types |
| else if (clazz.isAssignableFrom(Boolean.class) || clazz.isAssignableFrom(boolean.class) || clazz.isAssignableFrom(Long.class) || clazz.isAssignableFrom(long.class) || |
| clazz.isAssignableFrom(Double.class) || clazz.isAssignableFrom(double.class) || clazz.isAssignableFrom(String.class)) { |
| // no data type necessary except when being used in an array/collection |
| if (!allowEmpty) { |
| // for all other cases just use the simple name |
| return clazz.getSimpleName(); |
| } |
| |
| } else if (clazz.isAssignableFrom(Integer.class) || clazz.isAssignableFrom(int.class) || clazz.isAssignableFrom(Float.class) || clazz.isAssignableFrom(float.class) |
| || clazz.isAssignableFrom(Byte.class) || clazz.isAssignableFrom(byte.class) || clazz.isAssignableFrom(Short.class) || clazz.isAssignableFrom(short.class) |
| || clazz.isAssignableFrom(Character.class) || clazz.isAssignableFrom(char.class)) { |
| return clazz.getSimpleName(); |
| } |
| if (!allowEmpty) { |
| throw new IllegalStateException("Class does not have a valid type " + clazz); |
| } |
| return null; |
| |
| } |
| } |