2 * Rhynodge - Loader.java - Copyright © 2013 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.rhynodge.loader;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.InvocationTargetException;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.concurrent.TimeUnit;
26 import net.pterodactylus.rhynodge.Action;
27 import net.pterodactylus.rhynodge.Filter;
28 import net.pterodactylus.rhynodge.Query;
29 import net.pterodactylus.rhynodge.Reaction;
30 import net.pterodactylus.rhynodge.Trigger;
31 import net.pterodactylus.rhynodge.Watcher;
32 import net.pterodactylus.rhynodge.loader.Chain.Parameter;
33 import net.pterodactylus.rhynodge.loader.Chain.Part;
36 * Creates {@link Reaction}s from {@link Chain}s.
38 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
40 public class ReactionLoader {
43 * Creates a {@link Reaction} from the given {@link Chain}.
46 * The chain to create a reaction from
47 * @return The created reaction
48 * @throws LoaderException
49 * if a class can not be loaded
51 @SuppressWarnings("static-method")
52 public Reaction loadReaction(Chain chain) throws LoaderException {
54 /* check if chain is enabled. */
55 if (!chain.enabled()) {
56 throw new IllegalArgumentException("Chain is not enabled.");
62 Action action = createObject(chain.action().name(), "net.pterodactylus.rhynodge.actions", extractParameters(chain.action().parameters()));
64 /* do we have a reaction defined? */
65 if (chain.watcher() != null) {
68 Watcher watcher = createObject(chain.watcher().name(), "net.pterodactylus.rhynodge.watchers", extractParameters(chain.watcher().parameters()));
70 /* create reaction. */
71 reaction = new Reaction(chain.name(), watcher.query(), watcher.filters(), watcher.trigger(), action);
76 Query query = createObject(chain.query().name(), "net.pterodactylus.rhynodge.queries", extractParameters(chain.query().parameters()));
79 List<Filter> filters = new ArrayList<Filter>();
80 for (Part filterPart : chain.filters()) {
81 filters.add(ReactionLoader.<Filter> createObject(filterPart.name(), "net.pterodactylus.rhynodge.filters", extractParameters(filterPart.parameters())));
85 Trigger trigger = createObject(chain.trigger().name(), "net.pterodactylus.rhynodge.triggers", extractParameters(chain.trigger().parameters()));
87 /* create reaction. */
88 reaction = new Reaction(chain.name(), query, filters, trigger, action);
91 reaction.setUpdateInterval(TimeUnit.SECONDS.toMillis(chain.updateInterval()));
100 * Extracts all parameter values from the given parameters.
103 * The parameters to extract the values from
104 * @return The extracted values
106 private static List<String> extractParameters(List<Parameter> parameters) {
107 List<String> parameterValues = new ArrayList<String>();
109 for (Parameter parameter : parameters) {
110 parameterValues.add(parameter.value());
113 return parameterValues;
117 * Creates a new object.
119 * First, {@code className} is used to try to load a {@link Class} with that
120 * name. If that fails, {@code packageName} is prepended to the class name.
121 * If no class can be found, a {@link LoaderException} will be thrown.
123 * If a class could be located using the described method, a constructor
124 * will be searched that has the same number of {@link String} parameters as
125 * the given parameters. The parameters from the given parameters are then
126 * used in a constructor call to create the new object.
129 * The name of the class
131 * The optional name of the package to prepend
133 * The parameters for the constructor call
134 * @return The created object
135 * @throws LoaderException
136 * if the object can not be created
138 @SuppressWarnings("unchecked")
139 private static <T> T createObject(String className, String packageName, List<String> parameters) throws LoaderException {
141 /* try to load class without package name. */
142 Class<?> objectClass = null;
144 objectClass = Class.forName(className);
145 } catch (ClassNotFoundException cnfe1) {
146 /* ignore, we’ll try again. */
149 if (objectClass == null) {
151 objectClass = Class.forName(packageName + "." + className);
152 } catch (ClassNotFoundException cnfe1) {
153 /* okay, now we need to throw. */
154 throw new LoaderException(String.format("Could find neither class “%s” nor class “%s.”", className, packageName + "." + className), cnfe1);
158 /* locate an eligible constructor. */
159 Constructor<?> wantedConstructor = null;
160 for (Constructor<?> constructor : objectClass.getConstructors()) {
161 Class<?>[] parameterTypes = constructor.getParameterTypes();
162 if (parameterTypes.length != parameters.size()) {
165 boolean compatibleTypes = true;
166 for (Class<?> parameterType : parameterTypes) {
167 if (parameterType != String.class) {
168 compatibleTypes = false;
172 if (!compatibleTypes) {
175 wantedConstructor = constructor;
178 if (wantedConstructor == null) {
179 throw new LoaderException("Could not find eligible constructor.");
183 return (T) wantedConstructor.newInstance(parameters.toArray());
184 } catch (IllegalArgumentException iae1) {
185 throw new LoaderException("Could not invoke constructor.", iae1);
186 } catch (InstantiationException ie1) {
187 throw new LoaderException("Could not invoke constructor.", ie1);
188 } catch (IllegalAccessException iae1) {
189 throw new LoaderException("Could not invoke constructor.", iae1);
190 } catch (InvocationTargetException ite1) {
191 throw new LoaderException("Could not invoke constructor.", ite1);