Set update interval on all created reactions.
[rhynodge.git] / src / main / java / net / pterodactylus / rhynodge / loader / ReactionLoader.java
1 /*
2  * Rhynodge - Loader.java - Copyright © 2013 David Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.rhynodge.loader;
19
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;
25
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;
34
35 /**
36  * Creates {@link Reaction}s from {@link Chain}s.
37  *
38  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
39  */
40 public class ReactionLoader {
41
42         /**
43          * Creates a {@link Reaction} from the given {@link Chain}.
44          *
45          * @param 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
50          */
51         @SuppressWarnings("static-method")
52         public Reaction loadReaction(Chain chain) throws LoaderException {
53
54                 /* check if chain is enabled. */
55                 if (!chain.enabled()) {
56                         throw new IllegalArgumentException("Chain is not enabled.");
57                 }
58
59                 Reaction reaction;
60
61                 /* create action. */
62                 Action action = createObject(chain.action().name(), "net.pterodactylus.rhynodge.actions", extractParameters(chain.action().parameters()));
63
64                 /* do we have a reaction defined? */
65                 if (chain.watcher() != null) {
66
67                         /* create watcher. */
68                         Watcher watcher = createObject(chain.watcher().name(), "net.pterodactylus.rhynodge.watchers", extractParameters(chain.watcher().parameters()));
69
70                         /* create reaction. */
71                         reaction = new Reaction(chain.name(), watcher.query(), watcher.filters(), watcher.trigger(), action);
72
73                 } else {
74
75                         /* create query. */
76                         Query query = createObject(chain.query().name(), "net.pterodactylus.rhynodge.queries", extractParameters(chain.query().parameters()));
77
78                         /* create filters. */
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())));
82                         }
83
84                         /* create trigger. */
85                         Trigger trigger = createObject(chain.trigger().name(), "net.pterodactylus.rhynodge.triggers", extractParameters(chain.trigger().parameters()));
86
87                         /* create reaction. */
88                         reaction = new Reaction(chain.name(), query, filters, trigger, action);
89                 }
90
91                 reaction.setUpdateInterval(TimeUnit.SECONDS.toMillis(chain.updateInterval()));
92                 return reaction;
93         }
94
95         //
96         // STATIC METHODS
97         //
98
99         /**
100          * Extracts all parameter values from the given parameters.
101          *
102          * @param parameters
103          *            The parameters to extract the values from
104          * @return The extracted values
105          */
106         private static List<String> extractParameters(List<Parameter> parameters) {
107                 List<String> parameterValues = new ArrayList<String>();
108
109                 for (Parameter parameter : parameters) {
110                         parameterValues.add(parameter.value());
111                 }
112
113                 return parameterValues;
114         }
115
116         /**
117          * Creates a new object.
118          * <p>
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.
122          * <p>
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.
127          *
128          * @param className
129          *            The name of the class
130          * @param packageName
131          *            The optional name of the package to prepend
132          * @param parameters
133          *            The parameters for the constructor call
134          * @return The created object
135          * @throws LoaderException
136          *             if the object can not be created
137          */
138         @SuppressWarnings("unchecked")
139         private static <T> T createObject(String className, String packageName, List<String> parameters) throws LoaderException {
140
141                 /* try to load class without package name. */
142                 Class<?> objectClass = null;
143                 try {
144                         objectClass = Class.forName(className);
145                 } catch (ClassNotFoundException cnfe1) {
146                         /* ignore, we’ll try again. */
147                 }
148
149                 if (objectClass == null) {
150                         try {
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);
155                         }
156                 }
157
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()) {
163                                 continue;
164                         }
165                         boolean compatibleTypes = true;
166                         for (Class<?> parameterType : parameterTypes) {
167                                 if (parameterType != String.class) {
168                                         compatibleTypes = false;
169                                         break;
170                                 }
171                         }
172                         if (!compatibleTypes) {
173                                 continue;
174                         }
175                         wantedConstructor = constructor;
176                 }
177
178                 if (wantedConstructor == null) {
179                         throw new LoaderException("Could not find eligible constructor.");
180                 }
181
182                 try {
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);
192                 }
193
194         }
195
196 }