/* * This file is part of GumTree. * * GumTree is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GumTree is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with GumTree. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2011-2015 Jean-Rémy Falleri <jr.falleri@gmail.com> * Copyright 2011-2015 Floréal Morandat <florealm@gmail.com> */ package com.github.gumtreediff.gen; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; public abstract class Registry<K, C, A> { private boolean useExperimental = Boolean.parseBoolean( System.getProperty(String.format("gumtree.%s.experimental", getClass().getSimpleName()), "false")); public C get(K key, Object... args) { Factory<? extends C> factory = getFactory(key); if (factory != null) return factory.instantiate(args); return null; } public Factory<? extends C> getFactory(K key) { Entry entry = find(key); if (entry != null) return entry.factory; return null; } protected Entry find(K key) { Entry entry = findEntry(key); if (entry == null) return null; if (useExperimental() || !entry.experimental) return entry; return null; } private boolean useExperimental() { return useExperimental; } public abstract void install(Class<? extends C> clazz, A annotation); protected abstract Entry newEntry(Class<? extends C> clazz, A annotation); protected abstract Entry findEntry(K key); protected abstract class Entry { final String id; final Class<? extends C> clazz; final Factory<? extends C> factory; final boolean experimental; protected Entry(String id, Class<? extends C> clazz, Factory<? extends C> factory, boolean experimental) { this.id = id; this.clazz = clazz; this.factory = factory; this.experimental = experimental; } public C instantiate(Object[] args) { try { return factory.newInstance(args); } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { return null; } } protected abstract boolean handle(K key); } protected Factory<? extends C> defaultFactory(Class<? extends C> clazz, Class... signature) { try { Constructor<? extends C> ctor = clazz.getConstructor(signature); return (args) -> ctor.newInstance(args); } catch (NoSuchMethodException e) { System.out.println(Arrays.toString(clazz.getConstructors())); throw new RuntimeException(String.format("This is a static bug. Constructor %s(%s) not found", clazz.getName(), Arrays.toString(signature)), e); } } public interface Factory<C> { C newInstance(Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException; default C instantiate(Object[] args) { try { return newInstance(args); } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { return null; } } } public abstract static class NamedRegistry<K, C, A> extends Registry<K, C, A> { Map<K, NamedEntry> entries = new LinkedHashMap<>(); protected abstract K getName(A annotation, Class<? extends C> clazz); @Override public void install(Class<? extends C> clazz, A annotation) { K name = getName(annotation, clazz); NamedEntry entry = newEntry(clazz, annotation); entries.put(name, entry); } protected abstract NamedEntry newEntry(Class<? extends C> clazz, A annotation); @Override protected NamedEntry findEntry(K key) { NamedEntry e = entries.get(key); if (e != null && e.handle(key)) return e; return null; } protected class NamedEntry extends Entry { public NamedEntry(String id, Class<? extends C> clazz, Factory<? extends C> factory, boolean experimental) { super(id, clazz, factory, experimental); } @Override protected boolean handle(K key) { return true; } } } }