| /* |
| * 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.freemarker.generator.base.tools; |
| |
| import java.lang.reflect.Constructor; |
| import java.util.Map; |
| |
| import static java.util.Arrays.stream; |
| import static org.apache.freemarker.generator.base.util.StringUtils.isEmpty; |
| |
| public class ToolsFactory { |
| |
| /** |
| * Checks if the given class can be loaded from the class loader. |
| * |
| * @param clazzName Class to instantiate |
| * @return true if loaded |
| */ |
| public static boolean exists(String clazzName) { |
| if (isEmpty(clazzName)) { |
| return false; |
| } |
| |
| try { |
| return forName(clazzName) != null; |
| } catch (NoClassDefFoundError | ClassNotFoundException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Create a tool instance either using single argument constructor taking a map or |
| * the default constructor. |
| * |
| * @param clazzName Class to instantiate |
| * @param settings Settings used to configure the tool |
| * @return Tool instance |
| */ |
| public static Object create(String clazzName, Map<String, Object> settings) { |
| try { |
| final Class<?> clazz = Class.forName(clazzName); |
| final Constructor<?>[] constructors = clazz.getConstructors(); |
| final Constructor<?> constructorWithSettings = findSingleParameterConstructor(constructors, Map.class); |
| final Constructor<?> defaultConstructor = findDefaultConstructor(constructors); |
| return constructorWithSettings != null ? constructorWithSettings.newInstance(settings) : defaultConstructor.newInstance(); |
| } catch (Exception e) { |
| throw new RuntimeException("Failed to create tool: " + clazzName, e); |
| } |
| } |
| |
| private static Constructor<?> findSingleParameterConstructor(Constructor<?>[] constructors, Class<?> parameterClazz) { |
| return stream(constructors) |
| .filter(c -> c.getParameterCount() == 1 && c.getParameterTypes()[0].equals(parameterClazz)) |
| .findFirst() |
| .orElse(null); |
| } |
| |
| private static Constructor<?> findDefaultConstructor(Constructor<?>[] constructors) { |
| return stream(constructors) |
| .filter(c -> c.getParameterCount() == 0) |
| .findFirst() |
| .orElse(null); |
| } |
| |
| /** |
| * Similar to {@link Class#forName(java.lang.String)}, but attempts to load |
| * through the thread context class loader. Only if thread context class |
| * loader is inaccessible, or it can't find the class will it attempt to |
| * fall back to the class loader that loads the FreeMarker classes. |
| */ |
| private static Class<?> forName(String className) throws ClassNotFoundException { |
| try { |
| final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); |
| if (contextClassLoader != null) { // not null: we don't want to fall back to the bootstrap class loader |
| return Class.forName(className, true, contextClassLoader); |
| } |
| } catch (ClassNotFoundException | SecurityException e) { |
| ;// Intentionally ignored |
| } |
| |
| // Fall back to the defining class loader of the FreeMarker classes |
| return Class.forName(className); |
| } |
| } |