/* * 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.gogo.jline; import org.apache.felix.service.command.Converter; import org.apache.felix.service.command.Function; import java.io.IOException; import java.io.InputStream; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; public class BaseConverters implements Converter { public Object convert(Class<?> desiredType, final Object in) throws Exception { if (desiredType == Class.class) { try { return Class.forName(in.toString()); } catch (ClassNotFoundException e) { return null; } } if (desiredType.isAssignableFrom(String.class) && in instanceof InputStream) { return read(((InputStream) in)); } if (in instanceof Function && isFunctional(desiredType)) { return Proxy.newProxyInstance(desiredType.getClassLoader(), new Class[]{desiredType}, new InvocationHandler() { Function command = ((Function) in); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (isObjectMethod(method)) { return method.invoke(command, args); } else if (method.isDefault()) { final Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); field.setAccessible(true); final MethodHandles.Lookup lookup = (MethodHandles.Lookup) field.get(null); return lookup .unreflectSpecial(method, method.getDeclaringClass()) .bindTo(proxy) .invokeWithArguments(args); } else { return command.execute(null, args != null ? Arrays.asList(args) : Collections.emptyList()); } } }); } return null; } public CharSequence format(Object target, int level, Converter converter) throws IOException { if (level == INSPECT && target instanceof InputStream) { return read(((InputStream) target)); } return null; } private CharSequence read(InputStream in) throws IOException { int c; StringBuffer sb = new StringBuffer(); while ((c = in.read()) > 0) { if (c >= 32 && c <= 0x7F || c == '\n' || c == '\r') { sb.append((char) c); } else { String s = Integer.toHexString(c).toUpperCase(); sb.append("\\"); if (s.length() < 1) { sb.append(0); } sb.append(s); } } return sb; } public static boolean isFunctional(Class<?> clazz) { if (!clazz.isInterface()) { return false; } int nb = 0; for (Method method : clazz.getMethods()) { if (method.isDefault() || isObjectMethod(method) || isStatic(method)) { continue; } nb++; } return nb == 1; } public static boolean isStatic(Method method) { return (method.getModifiers() & Modifier.STATIC) == Modifier.STATIC; } public static boolean isObjectMethod(Method method) { switch (method.getName()) { case "toString": if (method.getParameterCount() == 0 && method.getReturnType() == String.class) { return true; } break; case "equals": if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == Object.class && method.getReturnType() == boolean.class) { return true; } break; case "hashCode": if (method.getParameterCount() == 0 && method.getReturnType() == int.class) { return true; } break; } return false; } }